diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/rendering/RenderObject.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/rendering/RenderObject.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderObject.cpp | 1966 |
1 files changed, 768 insertions, 1198 deletions
diff --git a/Source/WebCore/rendering/RenderObject.cpp b/Source/WebCore/rendering/RenderObject.cpp index fa99f248c..337bca4cc 100644 --- a/Source/WebCore/rendering/RenderObject.cpp +++ b/Source/WebCore/rendering/RenderObject.cpp @@ -28,23 +28,23 @@ #include "RenderObject.h" #include "AXObjectCache.h" -#include "AnimationController.h" -#include "EventHandler.h" +#include "CSSAnimationController.h" #include "FloatQuad.h" #include "FlowThreadController.h" -#include "Frame.h" #include "FrameSelection.h" #include "FrameView.h" +#include "GeometryUtilities.h" #include "GraphicsContext.h" -#include "HTMLAnchorElement.h" #include "HTMLElement.h" -#include "HTMLImageElement.h" #include "HTMLNames.h" +#include "HTMLTableCellElement.h" #include "HTMLTableElement.h" #include "HitTestResult.h" #include "LogicalSelectionOffsetCaches.h" +#include "MainFrame.h" #include "Page.h" #include "PseudoElement.h" +#include "RenderChildIterator.h" #include "RenderCounter.h" #include "RenderFlowThread.h" #include "RenderGeometryMap.h" @@ -52,22 +52,28 @@ #include "RenderIterator.h" #include "RenderLayer.h" #include "RenderLayerBacking.h" -#include "RenderNamedFlowThread.h" +#include "RenderMultiColumnFlowThread.h" +#include "RenderNamedFlowFragment.h" +#include "RenderNamedFlowThread.h" +#include "RenderRuby.h" +#include "RenderSVGBlock.h" +#include "RenderSVGInline.h" +#include "RenderSVGModelObject.h" +#include "RenderSVGResourceContainer.h" +#include "RenderSVGRoot.h" #include "RenderScrollbarPart.h" +#include "RenderTableRow.h" #include "RenderTheme.h" #include "RenderView.h" -#include "Settings.h" +#include "RenderWidget.h" +#include "SVGRenderSupport.h" #include "StyleResolver.h" #include "TransformState.h" #include "htmlediting.h" #include <algorithm> +#include <stdio.h> #include <wtf/RefCountedLeakCounter.h> -#if ENABLE(SVG) -#include "RenderSVGResourceContainer.h" -#include "SVGRenderSupport.h" -#endif - #if PLATFORM(IOS) #include "SelectionRect.h" #endif @@ -77,6 +83,7 @@ namespace WebCore { using namespace HTMLNames; #ifndef NDEBUG + RenderObject::SetLayoutNeededForbiddenScope::SetLayoutNeededForbiddenScope(RenderObject* renderObject, bool isForbidden) : m_renderObject(renderObject) , m_preexistingForbidden(m_renderObject->isSetNeedsLayoutForbidden()) @@ -106,17 +113,17 @@ DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, renderObjectCounter, ("Rend RenderObject::RenderObject(Node& node) : CachedImageClient() , m_node(node) - , m_parent(0) - , m_previous(0) - , m_next(0) + , m_parent(nullptr) + , m_previous(nullptr) + , m_next(nullptr) #ifndef NDEBUG , m_hasAXObject(false) , m_setNeedsLayoutForbidden(false) #endif , m_bitfields(node) { - if (!node.isDocumentNode()) - view().didCreateRenderer(); + if (RenderView* renderView = node.document().renderView()) + renderView->didCreateRenderer(); #ifndef NDEBUG renderObjectCounter.increment(); #endif @@ -124,23 +131,23 @@ RenderObject::RenderObject(Node& node) RenderObject::~RenderObject() { + view().didDestroyRenderer(); #ifndef NDEBUG ASSERT(!m_hasAXObject); renderObjectCounter.decrement(); #endif - view().didDestroyRenderer(); + ASSERT(!hasRareData()); } RenderTheme& RenderObject::theme() const { - ASSERT(document().page()); - return document().page()->theme(); + return page().theme(); } -bool RenderObject::isDescendantOf(const RenderObject* obj) const +bool RenderObject::isDescendantOf(const RenderObject* ancestor) const { - for (const RenderObject* r = this; r; r = r->m_parent) { - if (r == obj) + for (const RenderObject* renderer = this; renderer; renderer = renderer->m_parent) { + if (renderer == ancestor) return true; } return false; @@ -160,25 +167,74 @@ void RenderObject::setFlowThreadStateIncludingDescendants(FlowThreadState state) { setFlowThreadState(state); - for (RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) { + if (!is<RenderElement>(*this)) + return; + + for (auto& child : childrenOfType<RenderObject>(downcast<RenderElement>(*this))) { // If the child is a fragmentation context it already updated the descendants flag accordingly. - if (child->isRenderFlowThread()) + if (child.isRenderFlowThread()) continue; - ASSERT(state != child->flowThreadState()); - child->setFlowThreadStateIncludingDescendants(state); + ASSERT(state != child.flowThreadState()); + child.setFlowThreadStateIncludingDescendants(state); + } +} + +RenderObject::FlowThreadState RenderObject::computedFlowThreadState(const RenderObject& renderer) +{ + if (!renderer.parent()) + return renderer.flowThreadState(); + + auto inheritedFlowState = RenderObject::NotInsideFlowThread; + if (is<RenderText>(renderer)) + inheritedFlowState = renderer.parent()->flowThreadState(); + else if (is<RenderSVGBlock>(renderer) || is<RenderSVGInline>(renderer) || is<RenderSVGModelObject>(renderer)) { + // containingBlock() skips svg boundary (SVG root is a RenderReplaced). + if (auto* svgRoot = SVGRenderSupport::findTreeRootObject(downcast<RenderElement>(renderer))) + inheritedFlowState = svgRoot->flowThreadState(); + } else if (auto* container = renderer.container()) + inheritedFlowState = container->flowThreadState(); + else { + // Splitting lines or doing continuation, so just keep the current state. + inheritedFlowState = renderer.flowThreadState(); + } + return inheritedFlowState; +} + +void RenderObject::initializeFlowThreadStateOnInsertion() +{ + ASSERT(parent()); + + // A RenderFlowThread is always considered to be inside itself, so it never has to change its state in response to parent changes. + if (isRenderFlowThread()) + return; + + auto computedState = computedFlowThreadState(*this); + if (flowThreadState() == computedState) + return; + + setFlowThreadStateIncludingDescendants(computedState); +} + +void RenderObject::resetFlowThreadStateOnRemoval() +{ + if (flowThreadState() == NotInsideFlowThread) + return; + + if (!renderTreeBeingDestroyed() && is<RenderElement>(*this)) { + downcast<RenderElement>(*this).removeFromRenderFlowThread(); + return; } + + // A RenderFlowThread is always considered to be inside itself, so it never has to change its state in response to parent changes. + if (isRenderFlowThread()) + return; + + setFlowThreadStateIncludingDescendants(NotInsideFlowThread); } void RenderObject::setParent(RenderElement* parent) { m_parent = parent; - - // Only update if our flow thread state is different from our new parent and if we're not a RenderFlowThread. - // A RenderFlowThread is always considered to be inside itself, so it never has to change its state - // in response to parent changes. - FlowThreadState newState = parent ? parent->flowThreadState() : NotInsideFlowThread; - if (newState != flowThreadState() && !isRenderFlowThread()) - setFlowThreadStateIncludingDescendants(newState); } void RenderObject::removeFromParent() @@ -220,14 +276,14 @@ RenderObject* RenderObject::nextInPreOrder(const RenderObject* stayWithin) const RenderObject* RenderObject::nextInPreOrderAfterChildren(const RenderObject* stayWithin) const { if (this == stayWithin) - return 0; + return nullptr; const RenderObject* current = this; RenderObject* next; while (!(next = current->nextSibling())) { current = current->parent(); if (!current || current == stayWithin) - return 0; + return nullptr; } return next; } @@ -246,7 +302,7 @@ RenderObject* RenderObject::previousInPreOrder() const RenderObject* RenderObject::previousInPreOrder(const RenderObject* stayWithin) const { if (this == stayWithin) - return 0; + return nullptr; return previousInPreOrder(); } @@ -263,7 +319,7 @@ RenderObject* RenderObject::firstLeafChild() const { RenderObject* r = firstChildSlow(); while (r) { - RenderObject* n = 0; + RenderObject* n = nullptr; n = r->firstChildSlow(); if (!n) break; @@ -276,7 +332,7 @@ RenderObject* RenderObject::lastLeafChild() const { RenderObject* r = lastChildSlow(); while (r) { - RenderObject* n = 0; + RenderObject* n = nullptr; n = r->lastChildSlow(); if (!n) break; @@ -285,30 +341,7 @@ RenderObject* RenderObject::lastLeafChild() const return r; } -#if ENABLE(IOS_TEXT_AUTOSIZING) -// Inspired by Node::traverseNextNode. -RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin) const -{ - RenderObject* child = firstChildSlow(); - if (child) { - ASSERT(!stayWithin || child->isDescendantOf(stayWithin)); - return child; - } - if (this == stayWithin) - return 0; - if (nextSibling()) { - ASSERT(!stayWithin || nextSibling()->isDescendantOf(stayWithin)); - return nextSibling(); - } - const RenderObject* n = this; - while (n && !n->nextSibling() && (!stayWithin || n->parent() != stayWithin)) - n = n->parent(); - if (n) { - ASSERT(!stayWithin || !n->nextSibling() || n->nextSibling()->isDescendantOf(stayWithin)); - return n->nextSibling(); - } - return 0; -} +#if ENABLE(TEXT_AUTOSIZING) // Non-recursive version of the DFS search. RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, HeightTypeTraverseNextInclusionFunction inclusionFunction, int& currentDepth, int& newFixedDepth) const @@ -317,7 +350,7 @@ RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, HeightT // Check for suitable children. for (RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) { - overflowType = inclusionFunction(child); + overflowType = inclusionFunction(*child); if (overflowType != FixedHeight) { currentDepth++; if (overflowType == OverflowHeight) @@ -328,7 +361,7 @@ RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, HeightT } if (this == stayWithin) - return 0; + return nullptr; // Now we traverse other nodes if they exist, otherwise // we go to the parent node and try doing the same. @@ -339,9 +372,9 @@ RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, HeightT currentDepth--; } if (!n) - return 0; + return nullptr; for (RenderObject* sibling = n->nextSibling(); sibling; sibling = sibling->nextSibling()) { - overflowType = inclusionFunction(sibling); + overflowType = inclusionFunction(*sibling); if (overflowType != FixedHeight) { if (overflowType == OverflowHeight) newFixedDepth = currentDepth; @@ -353,122 +386,12 @@ RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, HeightT n = n->parent(); currentDepth--; } else - return 0; - } - return 0; -} - -RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, TraverseNextInclusionFunction inclusionFunction) const -{ - for (RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) { - if (inclusionFunction(child)) { - ASSERT(!stayWithin || child->isDescendantOf(stayWithin)); - return child; - } - } - - if (this == stayWithin) - return 0; - - for (RenderObject* sibling = nextSibling(); sibling; sibling = sibling->nextSibling()) { - if (inclusionFunction(sibling)) { - ASSERT(!stayWithin || sibling->isDescendantOf(stayWithin)); - return sibling; - } - } - - const RenderObject* n = this; - while (n) { - while (n && !n->nextSibling() && (!stayWithin || n->parent() != stayWithin)) - n = n->parent(); - if (n) { - for (RenderObject* sibling = n->nextSibling(); sibling; sibling = sibling->nextSibling()) { - if (inclusionFunction(sibling)) { - ASSERT(!stayWithin || !n->nextSibling() || n->nextSibling()->isDescendantOf(stayWithin)); - return sibling; - } - } - if ((!stayWithin || n->parent() != stayWithin)) - n = n->parent(); - else - return 0; - } - } - return 0; -} - -static RenderObject::BlockContentHeightType includeNonFixedHeight(const RenderObject* render) -{ - const RenderStyle& style = render->style(); - if (style.height().type() == Fixed) { - if (render->isRenderBlock()) { - const RenderBlock* block = toRenderBlock(render); - // For fixed height styles, if the overflow size of the element spills out of the specified - // height, assume we can apply text auto-sizing. - if (style.overflowY() == OVISIBLE && style.height().value() < block->layoutOverflowRect().maxY()) - return RenderObject::OverflowHeight; - } - return RenderObject::FixedHeight; + return nullptr; } - return RenderObject::FlexibleHeight; -} - - -void RenderObject::adjustComputedFontSizesOnBlocks(float size, float visibleWidth) -{ - Document* document = view().frameView().frame().document(); - if (!document) - return; - - Vector<int> depthStack; - int currentDepth = 0; - int newFixedDepth = 0; - - // We don't apply autosizing to nodes with fixed height normally. - // But we apply it to nodes which are located deep enough - // (nesting depth is greater than some const) inside of a parent block - // which has fixed height but its content overflows intentionally. - for (RenderObject* descendent = traverseNext(this, includeNonFixedHeight, currentDepth, newFixedDepth); descendent; descendent = descendent->traverseNext(this, includeNonFixedHeight, currentDepth, newFixedDepth)) { - while (depthStack.size() > 0 && currentDepth <= depthStack[depthStack.size() - 1]) - depthStack.remove(depthStack.size() - 1); - if (newFixedDepth) - depthStack.append(newFixedDepth); - - int stackSize = depthStack.size(); - if (descendent->isRenderBlockFlow() && !descendent->isListItem() && (!stackSize || currentDepth - depthStack[stackSize - 1] > TextAutoSizingFixedHeightDepth)) - toRenderBlockFlow(descendent)->adjustComputedFontSizes(size, visibleWidth); - newFixedDepth = 0; - } - - // Remove style from auto-sizing table that are no longer valid. - document->validateAutoSizingNodes(); + return nullptr; } -void RenderObject::resetTextAutosizing() -{ - Document* document = view().frameView().frame().document(); - if (!document) - return; - - document->resetAutoSizingNodes(); - - Vector<int> depthStack; - int currentDepth = 0; - int newFixedDepth = 0; - - for (RenderObject* descendent = traverseNext(this, includeNonFixedHeight, currentDepth, newFixedDepth); descendent; descendent = descendent->traverseNext(this, includeNonFixedHeight, currentDepth, newFixedDepth)) { - while (depthStack.size() > 0 && currentDepth <= depthStack[depthStack.size() - 1]) - depthStack.remove(depthStack.size() - 1); - if (newFixedDepth) - depthStack.append(newFixedDepth); - - int stackSize = depthStack.size(); - if (descendent->isRenderBlockFlow() && !descendent->isListItem() && (!stackSize || currentDepth - depthStack[stackSize - 1] > TextAutoSizingFixedHeightDepth)) - toRenderBlockFlow(descendent)->resetComputedFontSize(); - newFixedDepth = 0; - } -} -#endif // ENABLE(IOS_TEXT_AUTOSIZING) +#endif // ENABLE(TEXT_AUTOSIZING) RenderLayer* RenderObject::enclosingLayer() const { @@ -479,26 +402,27 @@ RenderLayer* RenderObject::enclosingLayer() const return nullptr; } -bool RenderObject::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignment& alignX, const ScrollAlignment& alignY) +bool RenderObject::scrollRectToVisible(SelectionRevealMode revealMode, const LayoutRect& absoluteRect, bool insideFixed, const ScrollAlignment& alignX, const ScrollAlignment& alignY) { + if (revealMode == SelectionRevealMode::DoNotReveal) + return false; + RenderLayer* enclosingLayer = this->enclosingLayer(); if (!enclosingLayer) return false; - enclosingLayer->scrollRectToVisible(rect, alignX, alignY); + enclosingLayer->scrollRectToVisible(revealMode, absoluteRect, insideFixed, alignX, alignY); return true; } -RenderBox* RenderObject::enclosingBox() const +RenderBox& RenderObject::enclosingBox() const { - // FIXME: This should return a reference; it can always find the root RenderView. - return lineageOfType<RenderBox>(const_cast<RenderObject&>(*this)).first(); + return *lineageOfType<RenderBox>(const_cast<RenderObject&>(*this)).first(); } -RenderBoxModelObject* RenderObject::enclosingBoxModelObject() const +RenderBoxModelObject& RenderObject::enclosingBoxModelObject() const { - // FIXME: This should return a reference; it can always find the root RenderView. - return lineageOfType<RenderBoxModelObject>(const_cast<RenderObject&>(*this)).first(); + return *lineageOfType<RenderBoxModelObject>(const_cast<RenderObject&>(*this)).first(); } bool RenderObject::fixedPositionedWithNamedFlowContainingBlock() const @@ -513,7 +437,7 @@ static bool hasFixedPosInNamedFlowContainingBlock(const RenderObject* renderer) ASSERT(renderer->flowThreadState() != RenderObject::NotInsideFlowThread); RenderObject* curr = const_cast<RenderObject*>(renderer); - while (curr) { + while (curr && !is<RenderView>(*curr)) { if (curr->fixedPositionedWithNamedFlowContainingBlock()) return true; curr = curr->containingBlock(); @@ -522,37 +446,9 @@ static bool hasFixedPosInNamedFlowContainingBlock(const RenderObject* renderer) return false; } -RenderFlowThread* RenderObject::locateFlowThreadContainingBlock() const -{ - ASSERT(flowThreadState() != NotInsideFlowThread); - - // See if we have the thread cached because we're in the middle of layout. - RenderFlowThread* flowThread = view().flowThreadController().currentRenderFlowThread(); - if (flowThread) - return flowThread; - - // Not in the middle of layout so have to find the thread the slow way. - RenderObject* curr = const_cast<RenderObject*>(this); - while (curr) { - if (curr->isRenderFlowThread()) - return toRenderFlowThread(curr); - curr = curr->containingBlock(); - } - return 0; -} - -RenderNamedFlowThread* RenderObject::renderNamedFlowThreadWrapper() const -{ - RenderObject* object = const_cast<RenderObject*>(this); - while (object && object->isAnonymousBlock() && !object->isRenderNamedFlowThread()) - object = object->parent(); - - return object && object->isRenderNamedFlowThread() ? toRenderNamedFlowThread(object) : 0; -} - RenderBlock* RenderObject::firstLineBlock() const { - return 0; + return nullptr; } static inline bool objectIsRelayoutBoundary(const RenderElement* object) @@ -564,15 +460,13 @@ static inline bool objectIsRelayoutBoundary(const RenderElement* object) if (object->isTextControl()) return true; -#if ENABLE(SVG) if (object->isSVGRoot()) return true; -#endif if (!object->hasOverflowClip()) return false; - if (object->style().width().isIntrinsicOrAuto() || object->style().height().isIntrinsicOrAuto() || object->style().height().isPercent()) + if (object->style().width().isIntrinsicOrAuto() || object->style().height().isIntrinsicOrAuto() || object->style().height().isPercentOrCalculated()) return false; // Table parts can't be relayout roots since the table is responsible for layouting all the parts. @@ -590,8 +484,8 @@ void RenderObject::clearNeedsLayout() setNeedsSimplifiedNormalFlowLayoutBit(false); setNormalChildNeedsLayoutBit(false); setNeedsPositionedMovementLayoutBit(false); - if (isRenderElement()) - toRenderElement(this)->setAncestorLineBoxDirty(false); + if (is<RenderElement>(*this)) + downcast<RenderElement>(*this).setAncestorLineBoxDirty(false); #ifndef NDEBUG checkBlockPositionedObjectsNeedLayout(); #endif @@ -599,18 +493,18 @@ void RenderObject::clearNeedsLayout() static void scheduleRelayoutForSubtree(RenderElement& renderer) { - if (!renderer.isRenderView()) { - if (!renderer.isRooted()) - return; - renderer.view().frameView().scheduleRelayoutOfSubtree(renderer); + if (is<RenderView>(renderer)) { + downcast<RenderView>(renderer).frameView().scheduleRelayout(); return; } - toRenderView(renderer).frameView().scheduleRelayout(); + + if (renderer.isRooted()) + renderer.view().frameView().scheduleRelayoutOfSubtree(renderer); } -void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, RenderElement* newRoot) +void RenderObject::markContainingBlocksForLayout(ScheduleRelayout scheduleRelayout, RenderElement* newRoot) { - ASSERT(!scheduleRelayout || !newRoot); + ASSERT(scheduleRelayout == ScheduleRelayout::No || !newRoot); ASSERT(!isSetNeedsLayoutForbidden()); auto ancestor = container(); @@ -654,14 +548,14 @@ void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, RenderEl if (ancestor == newRoot) return; - if (scheduleRelayout && objectIsRelayoutBoundary(ancestor)) + if (scheduleRelayout == ScheduleRelayout::Yes && objectIsRelayoutBoundary(ancestor)) break; hasOutOfFlowPosition = ancestor->style().hasOutOfFlowPosition(); ancestor = container; } - if (scheduleRelayout && ancestor) + if (scheduleRelayout == ScheduleRelayout::Yes && ancestor) scheduleRelayoutForSubtree(*ancestor); } @@ -670,8 +564,8 @@ void RenderObject::checkBlockPositionedObjectsNeedLayout() { ASSERT(!needsLayout()); - if (isRenderBlock()) - toRenderBlock(this)->checkPositionedObjectsNeedLayout(); + if (is<RenderBlock>(*this)) + downcast<RenderBlock>(*this).checkPositionedObjectsNeedLayout(); } #endif @@ -707,371 +601,73 @@ void RenderObject::invalidateContainerPreferredLogicalWidths() void RenderObject::setLayerNeedsFullRepaint() { ASSERT(hasLayer()); - toRenderLayerModelObject(this)->layer()->setRepaintStatus(NeedsFullRepaint); + downcast<RenderLayerModelObject>(*this).layer()->setRepaintStatus(NeedsFullRepaint); } void RenderObject::setLayerNeedsFullRepaintForPositionedMovementLayout() { ASSERT(hasLayer()); - toRenderLayerModelObject(this)->layer()->setRepaintStatus(NeedsFullRepaintForPositionedMovementLayout); + downcast<RenderLayerModelObject>(*this).layer()->setRepaintStatus(NeedsFullRepaintForPositionedMovementLayout); } RenderBlock* RenderObject::containingBlock() const { - auto o = parent(); - if (!o && isRenderScrollbarPart()) - o = toRenderScrollbarPart(this)->rendererOwningScrollbar(); + auto containingBlockForRenderer = [](const RenderElement& renderer) + { + auto& style = renderer.style(); + if (style.position() == AbsolutePosition) + return renderer.containingBlockForAbsolutePosition(); + if (style.position() == FixedPosition) + return renderer.containingBlockForFixedPosition(); + return renderer.containingBlockForObjectInFlow(); + }; - const RenderStyle& style = this->style(); - if (!isText() && style.position() == FixedPosition) - o = containingBlockForFixedPosition(o); - else if (!isText() && style.position() == AbsolutePosition) - o = containingBlockForAbsolutePosition(o); - else - o = containingBlockForObjectInFlow(o); - - if (!o || !o->isRenderBlock()) - return 0; // This can still happen in case of an orphaned tree + if (is<RenderText>(*this)) + return containingBlockForObjectInFlow(); - return toRenderBlock(o); + if (!parent() && is<RenderScrollbarPart>(*this)) { + if (auto* scrollbarPart = downcast<RenderScrollbarPart>(*this).rendererOwningScrollbar()) + return containingBlockForRenderer(*scrollbarPart); + return nullptr; + } + return containingBlockForRenderer(downcast<RenderElement>(*this)); } -void RenderObject::drawLineForBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, - BoxSide side, Color color, EBorderStyle borderStyle, int adjacentWidth1, int adjacentWidth2, bool antialias) +RenderBlock* RenderObject::containingBlockForObjectInFlow() const { - int thickness; - int length; - if (side == BSTop || side == BSBottom) { - thickness = y2 - y1; - length = x2 - x1; - } else { - thickness = x2 - x1; - length = y2 - y1; - } - - // FIXME: We really would like this check to be an ASSERT as we don't want to draw empty borders. However - // nothing guarantees that the following recursive calls to drawLineForBoxSide will have non-null dimensions. - if (!thickness || !length) - return; - - if (borderStyle == DOUBLE && thickness < 3) - borderStyle = SOLID; - - const RenderStyle& style = this->style(); - switch (borderStyle) { - case BNONE: - case BHIDDEN: - return; - case DOTTED: - case DASHED: { - if (thickness > 0) { - bool wasAntialiased = graphicsContext->shouldAntialias(); - StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); - graphicsContext->setShouldAntialias(antialias); - graphicsContext->setStrokeColor(color, style.colorSpace()); - graphicsContext->setStrokeThickness(thickness); - graphicsContext->setStrokeStyle(borderStyle == DASHED ? DashedStroke : DottedStroke); - - switch (side) { - case BSBottom: - case BSTop: - graphicsContext->drawLine(IntPoint(x1, (y1 + y2) / 2), IntPoint(x2, (y1 + y2) / 2)); - break; - case BSRight: - case BSLeft: - graphicsContext->drawLine(IntPoint((x1 + x2) / 2, y1), IntPoint((x1 + x2) / 2, y2)); - break; - } - graphicsContext->setShouldAntialias(wasAntialiased); - graphicsContext->setStrokeStyle(oldStrokeStyle); - } - break; - } - case DOUBLE: { - int thirdOfThickness = (thickness + 1) / 3; - ASSERT(thirdOfThickness); - - if (adjacentWidth1 == 0 && adjacentWidth2 == 0) { - StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); - graphicsContext->setStrokeStyle(NoStroke); - graphicsContext->setFillColor(color, style.colorSpace()); - - bool wasAntialiased = graphicsContext->shouldAntialias(); - graphicsContext->setShouldAntialias(antialias); - - switch (side) { - case BSTop: - case BSBottom: - graphicsContext->drawRect(IntRect(x1, y1, length, thirdOfThickness)); - graphicsContext->drawRect(IntRect(x1, y2 - thirdOfThickness, length, thirdOfThickness)); - break; - case BSLeft: - case BSRight: - // FIXME: Why do we offset the border by 1 in this case but not the other one? - if (length > 1) { - graphicsContext->drawRect(IntRect(x1, y1 + 1, thirdOfThickness, length - 1)); - graphicsContext->drawRect(IntRect(x2 - thirdOfThickness, y1 + 1, thirdOfThickness, length - 1)); - } - break; - } - - graphicsContext->setShouldAntialias(wasAntialiased); - graphicsContext->setStrokeStyle(oldStrokeStyle); - } else { - int adjacent1BigThird = ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 3; - int adjacent2BigThird = ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 3; - - switch (side) { - case BSTop: - drawLineForBoxSide(graphicsContext, x1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0), - y1, x2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness, - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - drawLineForBoxSide(graphicsContext, x1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0), - y2 - thirdOfThickness, x2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), y2, - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - break; - case BSLeft: - drawLineForBoxSide(graphicsContext, x1, y1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0), - x1 + thirdOfThickness, y2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, y1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0), - x2, y2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - break; - case BSBottom: - drawLineForBoxSide(graphicsContext, x1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0), - y1, x2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness, - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - drawLineForBoxSide(graphicsContext, x1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0), - y2 - thirdOfThickness, x2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), y2, - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - break; - case BSRight: - drawLineForBoxSide(graphicsContext, x1, y1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0), - x1 + thirdOfThickness, y2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, y1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0), - x2, y2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - break; - default: - break; - } - } - break; - } - case RIDGE: - case GROOVE: { - EBorderStyle s1; - EBorderStyle s2; - if (borderStyle == GROOVE) { - s1 = INSET; - s2 = OUTSET; - } else { - s1 = OUTSET; - s2 = INSET; - } - - int adjacent1BigHalf = ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 2; - int adjacent2BigHalf = ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 2; - - switch (side) { - case BSTop: - drawLineForBoxSide(graphicsContext, x1 + std::max(-adjacentWidth1, 0) / 2, y1, x2 - std::max(-adjacentWidth2, 0) / 2, (y1 + y2 + 1) / 2, - side, color, s1, adjacent1BigHalf, adjacent2BigHalf, antialias); - drawLineForBoxSide(graphicsContext, x1 + std::max(adjacentWidth1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - std::max(adjacentWidth2 + 1, 0) / 2, y2, - side, color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); - break; - case BSLeft: - drawLineForBoxSide(graphicsContext, x1, y1 + std::max(-adjacentWidth1, 0) / 2, (x1 + x2 + 1) / 2, y2 - std::max(-adjacentWidth2, 0) / 2, - side, color, s1, adjacent1BigHalf, adjacent2BigHalf, antialias); - drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + std::max(adjacentWidth1 + 1, 0) / 2, x2, y2 - std::max(adjacentWidth2 + 1, 0) / 2, - side, color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); - break; - case BSBottom: - drawLineForBoxSide(graphicsContext, x1 + std::max(adjacentWidth1, 0) / 2, y1, x2 - std::max(adjacentWidth2, 0) / 2, (y1 + y2 + 1) / 2, - side, color, s2, adjacent1BigHalf, adjacent2BigHalf, antialias); - drawLineForBoxSide(graphicsContext, x1 + std::max(-adjacentWidth1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - std::max(-adjacentWidth2 + 1, 0) / 2, y2, - side, color, s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); - break; - case BSRight: - drawLineForBoxSide(graphicsContext, x1, y1 + std::max(adjacentWidth1, 0) / 2, (x1 + x2 + 1) / 2, y2 - std::max(adjacentWidth2, 0) / 2, - side, color, s2, adjacent1BigHalf, adjacent2BigHalf, antialias); - drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + std::max(-adjacentWidth1 + 1, 0) / 2, x2, y2 - std::max(-adjacentWidth2 + 1, 0) / 2, - side, color, s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); - break; - } - break; - } - case INSET: - // FIXME: Maybe we should lighten the colors on one side like Firefox. - // https://bugs.webkit.org/show_bug.cgi?id=58608 - if (side == BSTop || side == BSLeft) - color = color.dark(); - FALLTHROUGH; - case OUTSET: - if (borderStyle == OUTSET && (side == BSBottom || side == BSRight)) - color = color.dark(); - FALLTHROUGH; - case SOLID: { - StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); - graphicsContext->setStrokeStyle(NoStroke); - graphicsContext->setFillColor(color, style.colorSpace()); - ASSERT(x2 >= x1); - ASSERT(y2 >= y1); - if (!adjacentWidth1 && !adjacentWidth2) { - // Turn off antialiasing to match the behavior of drawConvexPolygon(); - // this matters for rects in transformed contexts. - bool wasAntialiased = graphicsContext->shouldAntialias(); - graphicsContext->setShouldAntialias(antialias); - graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, y2 - y1)); - graphicsContext->setShouldAntialias(wasAntialiased); - graphicsContext->setStrokeStyle(oldStrokeStyle); - return; - } - FloatPoint quad[4]; - switch (side) { - case BSTop: - quad[0] = FloatPoint(x1 + std::max(-adjacentWidth1, 0), y1); - quad[1] = FloatPoint(x1 + std::max(adjacentWidth1, 0), y2); - quad[2] = FloatPoint(x2 - std::max(adjacentWidth2, 0), y2); - quad[3] = FloatPoint(x2 - std::max(-adjacentWidth2, 0), y1); - break; - case BSBottom: - quad[0] = FloatPoint(x1 + std::max(adjacentWidth1, 0), y1); - quad[1] = FloatPoint(x1 + std::max(-adjacentWidth1, 0), y2); - quad[2] = FloatPoint(x2 - std::max(-adjacentWidth2, 0), y2); - quad[3] = FloatPoint(x2 - std::max(adjacentWidth2, 0), y1); - break; - case BSLeft: - quad[0] = FloatPoint(x1, y1 + std::max(-adjacentWidth1, 0)); - quad[1] = FloatPoint(x1, y2 - std::max(-adjacentWidth2, 0)); - quad[2] = FloatPoint(x2, y2 - std::max(adjacentWidth2, 0)); - quad[3] = FloatPoint(x2, y1 + std::max(adjacentWidth1, 0)); - break; - case BSRight: - quad[0] = FloatPoint(x1, y1 + std::max(adjacentWidth1, 0)); - quad[1] = FloatPoint(x1, y2 - std::max(adjacentWidth2, 0)); - quad[2] = FloatPoint(x2, y2 - std::max(-adjacentWidth2, 0)); - quad[3] = FloatPoint(x2, y1 + std::max(-adjacentWidth1, 0)); - break; - } - - graphicsContext->drawConvexPolygon(4, quad, antialias); - graphicsContext->setStrokeStyle(oldStrokeStyle); - break; - } - } + auto* renderer = parent(); + while (renderer && ((renderer->isInline() && !renderer->isReplaced()) || !renderer->isRenderBlock())) + renderer = renderer->parent(); + return downcast<RenderBlock>(renderer); } -void RenderObject::paintFocusRing(PaintInfo& paintInfo, const LayoutPoint& paintOffset, RenderStyle* style) +void RenderObject::addPDFURLRect(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { - Vector<IntRect> focusRingRects; + Vector<LayoutRect> focusRingRects; addFocusRingRects(focusRingRects, paintOffset, paintInfo.paintContainer); - if (style->outlineStyleIsAuto()) - paintInfo.context->drawFocusRing(focusRingRects, style->outlineWidth(), style->outlineOffset(), style->visitedDependentColor(CSSPropertyOutlineColor)); - else - addPDFURLRect(paintInfo.context, unionRect(focusRingRects)); -} + LayoutRect urlRect = unionRect(focusRingRects); -void RenderObject::addPDFURLRect(GraphicsContext* context, const LayoutRect& rect) -{ - if (rect.isEmpty()) + if (urlRect.isEmpty()) return; - Node* n = node(); - if (!n || !n->isLink() || !n->isElementNode()) + Node* node = this->node(); + if (!is<Element>(node) || !node->isLink()) return; - const AtomicString& href = toElement(n)->getAttribute(hrefAttr); + Element& element = downcast<Element>(*node); + const AtomicString& href = element.getAttribute(hrefAttr); if (href.isNull()) return; - context->setURLForRect(n->document().completeURL(href), pixelSnappedIntRect(rect)); -} - -void RenderObject::paintOutline(PaintInfo& paintInfo, const LayoutRect& paintRect) -{ - if (!hasOutline()) - return; - - RenderStyle& styleToUse = style(); - LayoutUnit outlineWidth = styleToUse.outlineWidth(); - - int outlineOffset = styleToUse.outlineOffset(); - - if (styleToUse.outlineStyleIsAuto() || hasOutlineAnnotation()) { - if (!theme().supportsFocusRing(&styleToUse)) { - // Only paint the focus ring by hand if the theme isn't able to draw the focus ring. - paintFocusRing(paintInfo, paintRect.location(), &styleToUse); - } - } - - if (styleToUse.outlineStyleIsAuto() || styleToUse.outlineStyle() == BNONE) - return; - - IntRect inner = pixelSnappedIntRect(paintRect); - inner.inflate(outlineOffset); - - IntRect outer = pixelSnappedIntRect(inner); - outer.inflate(outlineWidth); - - // FIXME: This prevents outlines from painting inside the object. See bug 12042 - if (outer.isEmpty()) - return; - EBorderStyle outlineStyle = styleToUse.outlineStyle(); - Color outlineColor = styleToUse.visitedDependentColor(CSSPropertyOutlineColor); - - GraphicsContext* graphicsContext = paintInfo.context; - bool useTransparencyLayer = outlineColor.hasAlpha(); - if (useTransparencyLayer) { - if (outlineStyle == SOLID) { - Path path; - path.addRect(outer); - path.addRect(inner); - graphicsContext->setFillRule(RULE_EVENODD); - graphicsContext->setFillColor(outlineColor, styleToUse.colorSpace()); - graphicsContext->fillPath(path); + if (paintInfo.context().supportsInternalLinks()) { + String outAnchorName; + Element* linkTarget = element.findAnchorElementForLink(outAnchorName); + if (linkTarget) { + paintInfo.context().setDestinationForRect(outAnchorName, urlRect); return; } - graphicsContext->beginTransparencyLayer(static_cast<float>(outlineColor.alpha()) / 255); - outlineColor = Color(outlineColor.red(), outlineColor.green(), outlineColor.blue()); } - int leftOuter = outer.x(); - int leftInner = inner.x(); - int rightOuter = outer.maxX(); - int rightInner = inner.maxX(); - int topOuter = outer.y(); - int topInner = inner.y(); - int bottomOuter = outer.maxY(); - int bottomInner = inner.maxY(); - - drawLineForBoxSide(graphicsContext, leftOuter, topOuter, leftInner, bottomOuter, BSLeft, outlineColor, outlineStyle, outlineWidth, outlineWidth); - drawLineForBoxSide(graphicsContext, leftOuter, topOuter, rightOuter, topInner, BSTop, outlineColor, outlineStyle, outlineWidth, outlineWidth); - drawLineForBoxSide(graphicsContext, rightInner, topOuter, rightOuter, bottomOuter, BSRight, outlineColor, outlineStyle, outlineWidth, outlineWidth); - drawLineForBoxSide(graphicsContext, leftOuter, bottomInner, rightOuter, bottomOuter, BSBottom, outlineColor, outlineStyle, outlineWidth, outlineWidth); - - if (useTransparencyLayer) - graphicsContext->endTransparencyLayer(); -} + paintInfo.context().setURLForRect(node->document().completeURL(href), urlRect); -// FIXME: Make this return an unsigned integer? -int RenderObject::columnNumberForOffset(int offset) -{ - int columnNumber = 0; - RenderBlock* containingBlock = this->containingBlock(); - RenderView& view = containingBlock->view(); - const Pagination& pagination = view.frameView().frame().page()->pagination(); - if (pagination.mode == Pagination::Unpaginated) - return columnNumber; - - ColumnInfo* columnInfo = view.columnInfo(); - if (columnInfo && !columnInfo->progressionIsInline()) { - if (!columnInfo->progressionIsReversed()) - columnNumber = (pagination.pageLength + pagination.gap - offset) / (pagination.pageLength + pagination.gap); - else - columnNumber = offset / (pagination.pageLength + pagination.gap); - } - return columnNumber; } #if PLATFORM(IOS) @@ -1101,15 +697,15 @@ void RenderObject::collectSelectionRects(Vector<SelectionRect>& rects, unsigned unsigned numberOfQuads = quads.size(); for (unsigned i = 0; i < numberOfQuads; ++i) - rects.append(SelectionRect(quads[i].enclosingBoundingBox(), isHorizontalWritingMode(), columnNumberForOffset(quads[i].enclosingBoundingBox().x()))); + rects.append(SelectionRect(quads[i].enclosingBoundingBox(), isHorizontalWritingMode(), view().pageNumberForBlockProgressionOffset(quads[i].enclosingBoundingBox().x()))); } #endif -IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms) const +IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms, bool* wasFixed) const { if (useTransforms) { Vector<FloatQuad> quads; - absoluteQuads(quads); + absoluteQuads(quads, wasFixed); size_t n = quads.size(); if (!n) @@ -1121,7 +717,7 @@ IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms) const return result; } - FloatPoint absPos = localToAbsolute(); + FloatPoint absPos = localToAbsolute(FloatPoint(), 0 /* ignore transforms */, wasFixed); Vector<IntRect> rects; absoluteRects(rects, flooredLayoutPoint(absPos)); @@ -1132,35 +728,34 @@ IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms) const LayoutRect result = rects[0]; for (size_t i = 1; i < n; ++i) result.unite(rects[i]); - return pixelSnappedIntRect(result); + return snappedIntRect(result); } void RenderObject::absoluteFocusRingQuads(Vector<FloatQuad>& quads) { - Vector<IntRect> rects; + Vector<LayoutRect> rects; // FIXME: addFocusRingRects() needs to be passed this transform-unaware // localToAbsolute() offset here because RenderInline::addFocusRingRects() // implicitly assumes that. This doesn't work correctly with transformed // descendants. FloatPoint absolutePoint = localToAbsolute(); addFocusRingRects(rects, flooredLayoutPoint(absolutePoint)); - size_t count = rects.size(); - for (size_t i = 0; i < count; ++i) { - IntRect rect = rects[i]; - rect.move(-absolutePoint.x(), -absolutePoint.y()); - quads.append(localToAbsoluteQuad(FloatQuad(rect))); + float deviceScaleFactor = document().deviceScaleFactor(); + for (auto rect : rects) { + rect.moveBy(LayoutPoint(-absolutePoint)); + quads.append(localToAbsoluteQuad(FloatQuad(snapRectToDevicePixels(rect, deviceScaleFactor)))); } } FloatRect RenderObject::absoluteBoundingBoxRectForRange(const Range* range) { - if (!range || !range->startContainer()) + if (!range) return FloatRect(); range->ownerDocument().updateLayout(); Vector<FloatQuad> quads; - range->textQuads(quads); + range->absoluteTextQuads(quads); if (quads.isEmpty()) return FloatRect(); @@ -1176,8 +771,12 @@ void RenderObject::addAbsoluteRectForLayer(LayoutRect& result) { if (hasLayer()) result.unite(absoluteBoundingBoxRectIgnoringTransforms()); - for (RenderObject* current = firstChildSlow(); current; current = current->nextSibling()) - current->addAbsoluteRectForLayer(result); + + if (!is<RenderElement>(*this)) + return; + + for (auto& child : childrenOfType<RenderObject>(downcast<RenderElement>(*this))) + child.addAbsoluteRectForLayer(result); } // FIXME: change this to use the subtreePaint terminology @@ -1185,16 +784,17 @@ LayoutRect RenderObject::paintingRootRect(LayoutRect& topLevelRect) { LayoutRect result = absoluteBoundingBoxRectIgnoringTransforms(); topLevelRect = result; - for (RenderObject* current = firstChildSlow(); current; current = current->nextSibling()) - current->addAbsoluteRectForLayer(result); + if (is<RenderElement>(*this)) { + for (auto& child : childrenOfType<RenderObject>(downcast<RenderElement>(*this))) + child.addAbsoluteRectForLayer(result); + } return result; } RenderLayerModelObject* RenderObject::containerForRepaint() const { - RenderLayerModelObject* repaintContainer = 0; + RenderLayerModelObject* repaintContainer = nullptr; -#if USE(ACCELERATED_COMPOSITING) if (view().usesCompositing()) { if (RenderLayer* parentLayer = enclosingLayer()) { RenderLayer* compLayer = parentLayer->enclosingCompositingLayerForRepaint(); @@ -1202,9 +802,6 @@ RenderLayerModelObject* RenderObject::containerForRepaint() const repaintContainer = &compLayer->renderer(); } } -#endif - -#if ENABLE(CSS_FILTERS) if (view().hasSoftwareFilters()) { if (RenderLayer* parentLayer = enclosingLayer()) { RenderLayer* enclosingFilterLayer = parentLayer->enclosingFilterLayer(); @@ -1212,7 +809,6 @@ RenderLayerModelObject* RenderObject::containerForRepaint() const return &enclosingFilterLayer->renderer(); } } -#endif // If we have a flow thread, then we need to do individual repaints within the RenderRegions instead. // Return the flow thread as a repaint container in order to create a chokepoint that allows us to change @@ -1223,104 +819,149 @@ RenderLayerModelObject* RenderObject::containerForRepaint() const // then the repaint container is not the flow thread. if (hasFixedPosInNamedFlowContainingBlock(this)) return repaintContainer; - // The ancestor document will do the reparenting when the repaint propagates further up. - // We're just a seamless child document, and we don't need to do the hacking. - if (parentRenderFlowThread && &parentRenderFlowThread->document() != &document()) - return repaintContainer; // If we have already found a repaint container then we will repaint into that container only if it is part of the same // flow thread. Otherwise we will need to catch the repaint call and send it to the flow thread. - RenderFlowThread* repaintContainerFlowThread = repaintContainer ? repaintContainer->flowThreadContainingBlock() : 0; + RenderFlowThread* repaintContainerFlowThread = repaintContainer ? repaintContainer->flowThreadContainingBlock() : nullptr; if (!repaintContainerFlowThread || repaintContainerFlowThread != parentRenderFlowThread) repaintContainer = parentRenderFlowThread; } return repaintContainer; } -void RenderObject::repaintUsingContainer(const RenderLayerModelObject* repaintContainer, const IntRect& r, bool immediate, bool shouldClipToLayer) const +void RenderObject::propagateRepaintToParentWithOutlineAutoIfNeeded(const RenderLayerModelObject& repaintContainer, const LayoutRect& repaintRect) const { - if (!repaintContainer) { - view().repaintViewRectangle(r, immediate); + if (!hasOutlineAutoAncestor()) + return; + + // FIXME: We should really propagate only when the the child renderer sticks out. + bool repaintRectNeedsConverting = false; + // Issue repaint on the renderer with outline: auto. + for (const auto* renderer = this; renderer; renderer = renderer->parent()) { + bool rendererHasOutlineAutoAncestor = renderer->hasOutlineAutoAncestor(); + ASSERT(rendererHasOutlineAutoAncestor + || renderer->outlineStyleForRepaint().outlineStyleIsAuto() + || (is<RenderElement>(*renderer) && downcast<RenderElement>(*renderer).hasContinuation())); + if (renderer == &repaintContainer && rendererHasOutlineAutoAncestor) + repaintRectNeedsConverting = true; + if (rendererHasOutlineAutoAncestor) + continue; + // Issue repaint on the correct repaint container. + LayoutRect adjustedRepaintRect = repaintRect; + adjustedRepaintRect.inflate(renderer->outlineStyleForRepaint().outlineSize()); + if (!repaintRectNeedsConverting) + repaintContainer.repaintRectangle(adjustedRepaintRect); + else if (is<RenderLayerModelObject>(renderer)) { + const auto& rendererWithOutline = downcast<RenderLayerModelObject>(*renderer); + adjustedRepaintRect = LayoutRect(repaintContainer.localToContainerQuad(FloatRect(adjustedRepaintRect), &rendererWithOutline).boundingBox()); + rendererWithOutline.repaintRectangle(adjustedRepaintRect); + } return; } + ASSERT_NOT_REACHED(); +} + +void RenderObject::repaintUsingContainer(const RenderLayerModelObject* repaintContainer, const LayoutRect& r, bool shouldClipToLayer) const +{ + if (r.isEmpty()) + return; + + if (!repaintContainer) + repaintContainer = &view(); - if (repaintContainer->isRenderFlowThread()) { - toRenderFlowThread(repaintContainer)->repaintRectangleInRegions(r, immediate); + if (is<RenderFlowThread>(*repaintContainer)) { + downcast<RenderFlowThread>(*repaintContainer).repaintRectangleInRegions(r); return; } -#if ENABLE(CSS_FILTERS) + propagateRepaintToParentWithOutlineAutoIfNeeded(*repaintContainer, r); + if (repaintContainer->hasFilter() && repaintContainer->layer() && repaintContainer->layer()->requiresFullLayerImageForFilters()) { - repaintContainer->layer()->setFilterBackendNeedsRepaintingInRect(r, immediate); + repaintContainer->layer()->setFilterBackendNeedsRepaintingInRect(r); return; } -#endif -#if USE(ACCELERATED_COMPOSITING) - RenderView& v = view(); if (repaintContainer->isRenderView()) { - ASSERT(repaintContainer == &v); - bool viewHasCompositedLayer = v.hasLayer() && v.layer()->isComposited(); - if (!viewHasCompositedLayer || v.layer()->backing()->paintsIntoWindow()) { - v.repaintViewRectangle(viewHasCompositedLayer && v.layer()->transform() ? v.layer()->transform()->mapRect(r) : r, immediate); + RenderView& view = this->view(); + ASSERT(repaintContainer == &view); + bool viewHasCompositedLayer = view.isComposited(); + if (!viewHasCompositedLayer || view.layer()->backing()->paintsIntoWindow()) { + LayoutRect rect = r; + if (viewHasCompositedLayer && view.layer()->transform()) + rect = LayoutRect(view.layer()->transform()->mapRect(snapRectToDevicePixels(rect, document().deviceScaleFactor()))); + view.repaintViewRectangle(rect); return; } } - - if (v.usesCompositing()) { - ASSERT(repaintContainer->hasLayer() && repaintContainer->layer()->isComposited()); + + if (view().usesCompositing()) { + ASSERT(repaintContainer->isComposited()); repaintContainer->layer()->setBackingNeedsRepaintInRect(r, shouldClipToLayer ? GraphicsLayer::ClipToLayer : GraphicsLayer::DoNotClipToLayer); } -#else - if (repaintContainer->isRenderView()) - toRenderView(*repaintContainer).repaintViewRectangle(r, immediate); -#endif } -void RenderObject::repaint(bool immediate) const +void RenderObject::repaint() const { // Don't repaint if we're unrooted (note that view() still returns the view when unrooted) - RenderView* view; - if (!isRooted(&view)) + if (!isRooted()) return; - if (view->printing()) - return; // Don't repaint if we're printing. + const RenderView& view = this->view(); + if (view.printing()) + return; RenderLayerModelObject* repaintContainer = containerForRepaint(); - repaintUsingContainer(repaintContainer ? repaintContainer : view, pixelSnappedIntRect(clippedOverflowRectForRepaint(repaintContainer)), immediate); + repaintUsingContainer(repaintContainer, clippedOverflowRectForRepaint(repaintContainer)); } -void RenderObject::repaintRectangle(const LayoutRect& r, bool immediate, bool shouldClipToLayer) const +void RenderObject::repaintRectangle(const LayoutRect& r, bool shouldClipToLayer) const { // Don't repaint if we're unrooted (note that view() still returns the view when unrooted) - RenderView* view; - if (!isRooted(&view)) + if (!isRooted()) return; - if (view->printing()) - return; // Don't repaint if we're printing. + const RenderView& view = this->view(); + if (view.printing()) + return; LayoutRect dirtyRect(r); - // FIXME: layoutDelta needs to be applied in parts before/after transforms and // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 - dirtyRect.move(view->layoutDelta()); + dirtyRect.move(view.layoutDelta()); RenderLayerModelObject* repaintContainer = containerForRepaint(); - computeRectForRepaint(repaintContainer, dirtyRect); - repaintUsingContainer(repaintContainer ? repaintContainer : view, pixelSnappedIntRect(dirtyRect), immediate, shouldClipToLayer); + repaintUsingContainer(repaintContainer, computeRectForRepaint(dirtyRect, repaintContainer), shouldClipToLayer); } -IntRect RenderObject::pixelSnappedAbsoluteClippedOverflowRect() const +void RenderObject::repaintSlowRepaintObject() const { - return pixelSnappedIntRect(absoluteClippedOverflowRect()); + // Don't repaint if we're unrooted (note that view() still returns the view when unrooted) + if (!isRooted()) + return; + + const RenderView& view = this->view(); + if (view.printing()) + return; + + const RenderLayerModelObject* repaintContainer = containerForRepaint(); + + bool shouldClipToLayer = true; + IntRect repaintRect; + // If this is the root background, we need to check if there is an extended background rect. If + // there is, then we should not allow painting to clip to the layer size. + if (isDocumentElementRenderer() || isBody()) { + shouldClipToLayer = !view.frameView().hasExtendedBackgroundRectForPainting(); + repaintRect = snappedIntRect(view.backgroundRect()); + } else + repaintRect = snappedIntRect(clippedOverflowRectForRepaint(repaintContainer)); + + repaintUsingContainer(repaintContainer, repaintRect, shouldClipToLayer); } -bool RenderObject::checkForRepaintDuringLayout() const +IntRect RenderObject::pixelSnappedAbsoluteClippedOverflowRect() const { - return !document().view()->needsFullRepaint() && !hasLayer() && everHadLayout(); + return snappedIntRect(absoluteClippedOverflowRect()); } - + LayoutRect RenderObject::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const { LayoutRect r(clippedOverflowRectForRepaint(repaintContainer)); @@ -1334,201 +975,274 @@ LayoutRect RenderObject::clippedOverflowRectForRepaint(const RenderLayerModelObj return LayoutRect(); } -void RenderObject::computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect& rect, bool fixed) const +LayoutRect RenderObject::computeRectForRepaint(const LayoutRect& rect, const RenderLayerModelObject* repaintContainer, RepaintContext context) const { if (repaintContainer == this) - return; + return rect; - if (auto o = parent()) { - if (o->isRenderBlockFlow()) { - RenderBlock* cb = toRenderBlock(o); - if (cb->hasColumns()) - cb->adjustRectForColumns(rect); - } + auto* parent = this->parent(); + if (!parent) + return rect; - if (o->hasOverflowClip()) { - RenderBox* boxParent = toRenderBox(o); - boxParent->applyCachedClipAndScrollOffsetForRepaint(rect); - if (rect.isEmpty()) - return; - } - - o->computeRectForRepaint(repaintContainer, rect, fixed); + LayoutRect adjustedRect = rect; + if (parent->hasOverflowClip()) { + downcast<RenderBox>(*parent).applyCachedClipAndScrollPositionForRepaint(adjustedRect); + if (adjustedRect.isEmpty()) + return adjustedRect; } + return parent->computeRectForRepaint(adjustedRect, repaintContainer, context); } -void RenderObject::computeFloatRectForRepaint(const RenderLayerModelObject*, FloatRect&, bool) const +FloatRect RenderObject::computeFloatRectForRepaint(const FloatRect&, const RenderLayerModelObject*, bool) const { ASSERT_NOT_REACHED(); + return FloatRect(); } -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) -void RenderObject::showTreeForThis() const +static void showRenderTreeLegend() { - if (node()) - node()->showTreeForThis(); + fprintf(stderr, "\n(B)lock/(I)nline/I(N)line-block, (A)bsolute/Fi(X)ed/(R)elative/Stic(K)y, (F)loating, (O)verflow clip, Anon(Y)mous, (G)enerated, has(L)ayer, (C)omposited, (+)Dirty style, (+)Dirty layout\n"); } -void RenderObject::showRenderTreeForThis() const +void RenderObject::showNodeTreeForThis() const { - showRenderTree(this, 0); + if (!node()) + return; + node()->showTreeForThis(); } -void RenderObject::showLineTreeForThis() const +void RenderObject::showRenderTreeForThis() const { - if (containingBlock()) - containingBlock()->showLineTreeAndMark(0, 0, 0, 0, this); + const WebCore::RenderObject* root = this; + while (root->parent()) + root = root->parent(); + showRenderTreeLegend(); + root->showRenderSubTreeAndMark(this, 1); } -void RenderObject::showRenderObject() const +void RenderObject::showLineTreeForThis() const { - showRenderObject(0); + if (!is<RenderBlockFlow>(*this)) + return; + showRenderTreeLegend(); + showRenderObject(false, 1); + downcast<RenderBlockFlow>(*this).showLineTreeAndMark(nullptr, 2); } -void RenderObject::showRenderObject(int printedCharacters) const +static const RenderFlowThread* flowThreadContainingBlockFromRenderer(const RenderObject* renderer) { - // As this function is intended to be used when debugging, the - // this pointer may be 0. - if (!this) { - fputs("(null)\n", stderr); - return; - } + if (!renderer) + return nullptr; - printedCharacters += fprintf(stderr, "%s %p", renderName(), this); + if (renderer->flowThreadState() == RenderObject::NotInsideFlowThread) + return nullptr; - if (node()) { - if (printedCharacters) - for (; printedCharacters < showTreeCharacterOffset; printedCharacters++) - fputc(' ', stderr); - fputc('\t', stderr); - node()->showNode(); - } else - fputc('\n', stderr); + if (is<RenderFlowThread>(*renderer)) + return downcast<RenderFlowThread>(renderer); + + if (is<RenderBlock>(*renderer)) + return downcast<RenderBlock>(*renderer).cachedFlowThreadContainingBlock(); + + return nullptr; } -void RenderObject::showRenderTreeAndMark(const RenderObject* markedObject1, const char* markedLabel1, const RenderObject* markedObject2, const char* markedLabel2, int depth) const +void RenderObject::showRegionsInformation() const { - int printedCharacters = 0; - if (markedObject1 == this && markedLabel1) - printedCharacters += fprintf(stderr, "%s", markedLabel1); - if (markedObject2 == this && markedLabel2) - printedCharacters += fprintf(stderr, "%s", markedLabel2); - for (; printedCharacters < depth * 2; printedCharacters++) - fputc(' ', stderr); + const RenderFlowThread* ftcb = flowThreadContainingBlockFromRenderer(this); + + if (!ftcb) { + // Only the boxes have region range information. + // Try to get the flow thread containing block information + // from the containing block of this box. + if (is<RenderBox>(*this)) + ftcb = flowThreadContainingBlockFromRenderer(containingBlock()); + } - showRenderObject(printedCharacters); - if (!this) + if (!ftcb) return; - for (const RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) - child->showRenderTreeAndMark(markedObject1, markedLabel1, markedObject2, markedLabel2, depth + 1); + RenderRegion* startRegion = nullptr; + RenderRegion* endRegion = nullptr; + ftcb->getRegionRangeForBox(downcast<RenderBox>(this), startRegion, endRegion); + fprintf(stderr, " [Rs:%p Re:%p]", startRegion, endRegion); } -#endif // NDEBUG - -Color RenderObject::selectionBackgroundColor() const +void RenderObject::showRenderObject(bool mark, int depth) const { - Color color; - if (style().userSelect() != SELECT_NONE) { - if (frame().selection().shouldShowBlockCursor() && frame().selection().isCaret()) - color = style().visitedDependentColor(CSSPropertyColor).blendWithWhite(); - else { - RefPtr<RenderStyle> pseudoStyle = getUncachedPseudoStyle(PseudoStyleRequest(SELECTION)); - if (pseudoStyle && pseudoStyle->visitedDependentColor(CSSPropertyBackgroundColor).isValid()) - color = pseudoStyle->visitedDependentColor(CSSPropertyBackgroundColor).blendWithWhite(); + if (isInlineBlockOrInlineTable()) + fputc('N', stderr); + else if (isInline()) + fputc('I', stderr); + else + fputc('B', stderr); + + if (isPositioned()) { + if (isRelPositioned()) + fputc('R', stderr); + else if (isStickyPositioned()) + fputc('K', stderr); + else if (isOutOfFlowPositioned()) { + if (style().position() == AbsolutePosition) + fputc('A', stderr); else - color = frame().selection().isFocusedAndActive() ? theme().activeSelectionBackgroundColor() : theme().inactiveSelectionBackgroundColor(); + fputc('X', stderr); } - } + } else + fputc('-', stderr); - return color; -} + if (isFloating()) + fputc('F', stderr); + else + fputc('-', stderr); -Color RenderObject::selectionColor(int colorProperty) const -{ - Color color; - // If the element is unselectable, or we are only painting the selection, - // don't override the foreground color with the selection foreground color. - if (style().userSelect() == SELECT_NONE - || (view().frameView().paintBehavior() & PaintBehaviorSelectionOnly)) - return color; + if (hasOverflowClip()) + fputc('O', stderr); + else + fputc('-', stderr); - if (RefPtr<RenderStyle> pseudoStyle = getUncachedPseudoStyle(PseudoStyleRequest(SELECTION))) { - color = pseudoStyle->visitedDependentColor(colorProperty); - if (!color.isValid()) - color = pseudoStyle->visitedDependentColor(CSSPropertyColor); - } else - color = frame().selection().isFocusedAndActive() ? theme().activeSelectionForegroundColor() : theme().inactiveSelectionForegroundColor(); + if (isAnonymous()) + fputc('Y', stderr); + else + fputc('-', stderr); - return color; -} + if (isPseudoElement() || isAnonymous()) + fputc('G', stderr); + else + fputc('-', stderr); -Color RenderObject::selectionForegroundColor() const -{ - return selectionColor(CSSPropertyWebkitTextFillColor); -} + if (hasLayer()) + fputc('L', stderr); + else + fputc('-', stderr); -Color RenderObject::selectionEmphasisMarkColor() const -{ - return selectionColor(CSSPropertyWebkitTextEmphasisColor); -} + if (isComposited()) + fputc('C', stderr); + else + fputc('-', stderr); -void RenderObject::selectionStartEnd(int& spos, int& epos) const -{ - view().selectionStartEnd(spos, epos); -} + fputc(' ', stderr); -void RenderObject::handleDynamicFloatPositionChange() -{ - // We have gone from not affecting the inline status of the parent flow to suddenly - // having an impact. See if there is a mismatch between the parent flow's - // childrenInline() state and our state. - setInline(style().isDisplayInlineType()); - if (isInline() != parent()->childrenInline()) { - if (!isInline()) - toRenderBoxModelObject(parent())->childBecameNonInline(this); - else { - // An anonymous block must be made to wrap this inline. - RenderBlock* block = toRenderBlock(parent())->createAnonymousBlock(); - parent()->insertChildInternal(block, this, RenderElement::NotifyChildren); - parent()->removeChildInternal(*this, RenderElement::NotifyChildren); - block->insertChildInternal(this, nullptr, RenderElement::NotifyChildren); + if (node() && node()->needsStyleRecalc()) + fputc('+', stderr); + else + fputc('-', stderr); + + if (needsLayout()) + fputc('+', stderr); + else + fputc('-', stderr); + + int printedCharacters = 0; + if (mark) { + fprintf(stderr, "*"); + ++printedCharacters; + } + + while (++printedCharacters <= depth * 2) + fputc(' ', stderr); + + if (node()) + fprintf(stderr, "%s ", node()->nodeName().utf8().data()); + + String name = renderName(); + // FIXME: Renderer's name should not include property value listing. + int pos = name.find('('); + if (pos > 0) + fprintf(stderr, "%s", name.left(pos - 1).utf8().data()); + else + fprintf(stderr, "%s", name.utf8().data()); + + if (is<RenderBox>(*this)) { + auto& renderBox = downcast<RenderBox>(*this); + FloatRect boxRect = renderBox.frameRect(); + if (renderBox.isInFlowPositioned()) + boxRect.move(renderBox.offsetForInFlowPosition()); + fprintf(stderr, " (%.2f, %.2f) (%.2f, %.2f)", boxRect.x(), boxRect.y(), boxRect.width(), boxRect.height()); + } else if (is<RenderInline>(*this) && isInFlowPositioned()) { + FloatSize inlineOffset = downcast<RenderInline>(*this).offsetForInFlowPosition(); + fprintf(stderr, " (%.2f, %.2f)", inlineOffset.width(), inlineOffset.height()); + } + + fprintf(stderr, " renderer->(%p)", this); + if (node()) { + fprintf(stderr, " node->(%p)", node()); + if (node()->isTextNode()) { + String value = node()->nodeValue(); + fprintf(stderr, " length->(%u)", value.length()); + + value.replaceWithLiteral('\\', "\\\\"); + value.replaceWithLiteral('\n', "\\n"); + + const int maxPrintedLength = 80; + if (value.length() > maxPrintedLength) { + String substring = value.substring(0, maxPrintedLength); + fprintf(stderr, " \"%s\"...", substring.utf8().data()); + } else + fprintf(stderr, " \"%s\"", value.utf8().data()); } } + if (is<RenderBoxModelObject>(*this)) { + auto& renderer = downcast<RenderBoxModelObject>(*this); + if (renderer.hasContinuation()) + fprintf(stderr, " continuation->(%p)", renderer.continuation()); + } + showRegionsInformation(); + if (needsLayout()) { + fprintf(stderr, " layout->"); + if (selfNeedsLayout()) + fprintf(stderr, "[self]"); + if (normalChildNeedsLayout()) + fprintf(stderr, "[normal child]"); + if (posChildNeedsLayout()) + fprintf(stderr, "[positioned child]"); + if (needsSimplifiedNormalFlowLayout()) + fprintf(stderr, "[simplified]"); + if (needsPositionedMovementLayout()) + fprintf(stderr, "[positioned movement]"); + } + fprintf(stderr, "\n"); } -void RenderObject::removeAnonymousWrappersForInlinesIfNecessary() +void RenderObject::showRenderSubTreeAndMark(const RenderObject* markedObject, int depth) const { - RenderBlock* parentBlock = toRenderBlock(parent()); - if (!parentBlock->canCollapseAnonymousBlockChild()) - return; + showRenderObject(markedObject == this, depth); + if (is<RenderBlockFlow>(*this)) + downcast<RenderBlockFlow>(*this).showLineTreeAndMark(nullptr, depth + 1); - // We have changed to floated or out-of-flow positioning so maybe all our parent's - // children can be inline now. Bail if there are any block children left on the line, - // otherwise we can proceed to stripping solitary anonymous wrappers from the inlines. - // FIXME: We should also handle split inlines here - we exclude them at the moment by returning - // if we find a continuation. - RenderObject* curr = parent()->firstChild(); - while (curr && ((curr->isAnonymousBlock() && !toRenderBlock(curr)->isAnonymousBlockContinuation()) || curr->style().isFloating() || curr->style().hasOutOfFlowPosition())) - curr = curr->nextSibling(); + for (const RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) + child->showRenderSubTreeAndMark(markedObject, depth + 1); +} - if (curr) - return; +#endif // NDEBUG - curr = parent()->firstChild(); - while (curr) { - RenderObject* next = curr->nextSibling(); - if (curr->isAnonymousBlock()) - parentBlock->collapseAnonymousBoxChild(parentBlock, toRenderBlock(curr)); - curr = next; +SelectionSubtreeRoot& RenderObject::selectionRoot() const +{ + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (!flowThread) + return view(); + + if (is<RenderNamedFlowThread>(*flowThread)) + return downcast<RenderNamedFlowThread>(*flowThread); + if (is<RenderMultiColumnFlowThread>(*flowThread)) { + if (!flowThread->containingBlock()) + return view(); + return flowThread->containingBlock()->selectionRoot(); } + ASSERT_NOT_REACHED(); + return view(); +} + +void RenderObject::selectionStartEnd(unsigned& spos, unsigned& epos) const +{ + selectionRoot().selectionData().selectionStartEndPositions(spos, epos); } -FloatPoint RenderObject::localToAbsolute(const FloatPoint& localPoint, MapCoordinatesFlags mode) const +FloatPoint RenderObject::localToAbsolute(const FloatPoint& localPoint, MapCoordinatesFlags mode, bool* wasFixed) const { TransformState transformState(TransformState::ApplyTransformDirection, localPoint); - mapLocalToContainer(0, transformState, mode | ApplyContainerFlip); + mapLocalToContainer(nullptr, transformState, mode | ApplyContainerFlip, wasFixed); transformState.flatten(); return transformState.lastPlanarPoint(); @@ -1556,63 +1270,55 @@ void RenderObject::mapLocalToContainer(const RenderLayerModelObject* repaintCont if (repaintContainer == this) return; - auto o = parent(); - if (!o) + auto* parent = this->parent(); + if (!parent) return; // FIXME: this should call offsetFromContainer to share code, but I'm not sure it's ever called. - LayoutPoint centerPoint = roundedLayoutPoint(transformState.mappedPoint()); - if (mode & ApplyContainerFlip && o->isBox()) { - if (o->style().isFlippedBlocksWritingMode()) - transformState.move(toRenderBox(o)->flipForWritingModeIncludingColumns(roundedLayoutPoint(transformState.mappedPoint())) - centerPoint); + LayoutPoint centerPoint(transformState.mappedPoint()); + if (mode & ApplyContainerFlip && is<RenderBox>(*parent)) { + if (parent->style().isFlippedBlocksWritingMode()) + transformState.move(downcast<RenderBox>(parent)->flipForWritingMode(LayoutPoint(transformState.mappedPoint())) - centerPoint); mode &= ~ApplyContainerFlip; } - LayoutSize columnOffset; - o->adjustForColumns(columnOffset, roundedLayoutPoint(transformState.mappedPoint())); - if (!columnOffset.isZero()) - transformState.move(columnOffset); + if (is<RenderBox>(*parent)) + transformState.move(-toLayoutSize(downcast<RenderBox>(*parent).scrollPosition())); - if (o->hasOverflowClip()) - transformState.move(-toRenderBox(o)->scrolledContentOffset()); - - o->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed); + parent->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed); } const RenderObject* RenderObject::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const { ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != this); - auto container = parent(); + auto* container = parent(); if (!container) - return 0; + return nullptr; // FIXME: this should call offsetFromContainer to share code, but I'm not sure it's ever called. LayoutSize offset; - if (container->hasOverflowClip()) - offset = -toRenderBox(container)->scrolledContentOffset(); + if (is<RenderBox>(*container)) + offset = -toLayoutSize(downcast<RenderBox>(*container).scrollPosition()); - geometryMap.push(this, offset, hasColumns()); + geometryMap.push(this, offset, false); return container; } void RenderObject::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const { - auto o = parent(); - if (o) { - o->mapAbsoluteToLocalPoint(mode, transformState); - if (o->hasOverflowClip()) - transformState.move(toRenderBox(o)->scrolledContentOffset()); + if (auto* parent = this->parent()) { + parent->mapAbsoluteToLocalPoint(mode, transformState); + if (is<RenderBox>(*parent)) + transformState.move(toLayoutSize(downcast<RenderBox>(*parent).scrollPosition())); } } bool RenderObject::shouldUseTransformFromContainer(const RenderObject* containerObject) const { -#if ENABLE(3D_RENDERING) - // hasTransform() indicates whether the object has transform, transform-style or perspective. We just care about transform, - // so check the layer's transform directly. - return (hasLayer() && toRenderLayerModelObject(this)->layer()->transform()) || (containerObject && containerObject->style().hasPerspective()); +#if ENABLE(3D_TRANSFORMS) + return hasTransform() || (containerObject && containerObject->style().hasPerspective()); #else UNUSED_PARAM(containerObject); return hasTransform(); @@ -1624,14 +1330,14 @@ void RenderObject::getTransformFromContainer(const RenderObject* containerObject transform.makeIdentity(); transform.translate(offsetInContainer.width(), offsetInContainer.height()); RenderLayer* layer; - if (hasLayer() && (layer = toRenderLayerModelObject(this)->layer()) && layer->transform()) + if (hasLayer() && (layer = downcast<RenderLayerModelObject>(*this).layer()) && layer->transform()) transform.multiply(layer->currentTransform()); -#if ENABLE(3D_RENDERING) +#if ENABLE(3D_TRANSFORMS) if (containerObject && containerObject->hasLayer() && containerObject->style().hasPerspective()) { // Perpsective on the container affects us, so we have to factor it in here. ASSERT(containerObject->hasLayer()); - FloatPoint perspectiveOrigin = toRenderLayerModelObject(containerObject)->layer()->perspectiveOrigin(); + FloatPoint perspectiveOrigin = downcast<RenderLayerModelObject>(*containerObject).layer()->perspectiveOrigin(); TransformationMatrix perspectiveMatrix; perspectiveMatrix.applyPerspective(containerObject->style().perspective()); @@ -1650,7 +1356,7 @@ FloatQuad RenderObject::localToContainerQuad(const FloatQuad& localQuad, const R // Track the point at the center of the quad's bounding box. As mapLocalToContainer() calls offsetFromContainer(), // it will use that point as the reference point to decide which column's transform to apply in multiple-column blocks. TransformState transformState(TransformState::ApplyTransformDirection, localQuad.boundingBox().center(), localQuad); - mapLocalToContainer(repaintContainer, transformState, mode | ApplyContainerFlip | UseTransforms, wasFixed); + mapLocalToContainer(repaintContainer, transformState, mode | ApplyContainerFlip, wasFixed); transformState.flatten(); return transformState.lastPlanarQuad(); @@ -1659,50 +1365,47 @@ FloatQuad RenderObject::localToContainerQuad(const FloatQuad& localQuad, const R FloatPoint RenderObject::localToContainerPoint(const FloatPoint& localPoint, const RenderLayerModelObject* repaintContainer, MapCoordinatesFlags mode, bool* wasFixed) const { TransformState transformState(TransformState::ApplyTransformDirection, localPoint); - mapLocalToContainer(repaintContainer, transformState, mode | ApplyContainerFlip | UseTransforms, wasFixed); + mapLocalToContainer(repaintContainer, transformState, mode | ApplyContainerFlip, wasFixed); transformState.flatten(); return transformState.lastPlanarPoint(); } -LayoutSize RenderObject::offsetFromContainer(RenderObject* o, const LayoutPoint& point, bool* offsetDependsOnPoint) const +LayoutSize RenderObject::offsetFromContainer(RenderElement& container, const LayoutPoint&, bool* offsetDependsOnPoint) const { - ASSERT(o == container()); + ASSERT(&container == this->container()); LayoutSize offset; - - o->adjustForColumns(offset, point); - - if (o->hasOverflowClip()) - offset -= toRenderBox(o)->scrolledContentOffset(); + if (is<RenderBox>(container)) + offset -= toLayoutSize(downcast<RenderBox>(container).scrollPosition()); if (offsetDependsOnPoint) - *offsetDependsOnPoint = hasColumns() || o->isRenderFlowThread(); + *offsetDependsOnPoint = is<RenderFlowThread>(container); return offset; } -LayoutSize RenderObject::offsetFromAncestorContainer(RenderObject* container) const +LayoutSize RenderObject::offsetFromAncestorContainer(RenderElement& container) const { LayoutSize offset; LayoutPoint referencePoint; const RenderObject* currContainer = this; do { - auto nextContainer = currContainer->container(); + RenderElement* nextContainer = currContainer->container(); ASSERT(nextContainer); // This means we reached the top without finding container. if (!nextContainer) break; ASSERT(!currContainer->hasTransform()); - LayoutSize currentOffset = currContainer->offsetFromContainer(nextContainer, referencePoint); + LayoutSize currentOffset = currContainer->offsetFromContainer(*nextContainer, referencePoint); offset += currentOffset; referencePoint.move(currentOffset); currContainer = nextContainer; - } while (currContainer != container); + } while (currContainer != &container); return offset; } -LayoutRect RenderObject::localCaretRect(InlineBox*, int, LayoutUnit* extraWidthToEndOfLine) +LayoutRect RenderObject::localCaretRect(InlineBox*, unsigned, LayoutUnit* extraWidthToEndOfLine) { if (extraWidthToEndOfLine) *extraWidthToEndOfLine = 0; @@ -1710,125 +1413,49 @@ LayoutRect RenderObject::localCaretRect(InlineBox*, int, LayoutUnit* extraWidthT return LayoutRect(); } -bool RenderObject::isRooted(RenderView** view) const +bool RenderObject::isRooted() const { - const RenderObject* o = this; - while (o->parent()) - o = o->parent(); - - if (!o->isRenderView()) - return false; - - if (view) - *view = &const_cast<RenderView&>(toRenderView(*o)); - - return true; -} - -RespectImageOrientationEnum RenderObject::shouldRespectImageOrientation() const -{ -#if USE(CG) || USE(CAIRO) - // This can only be enabled for ports which honor the orientation flag in their drawing code. - if (document().isImageDocument()) - return RespectImageOrientation; -#endif - // Respect the image's orientation if it's being used as a full-page image or it's - // an <img> and the setting to respect it everywhere is set. - return (frame().settings().shouldRespectImageOrientation() && node() && isHTMLImageElement(node())) ? RespectImageOrientation : DoNotRespectImageOrientation; + return isDescendantOf(&view()); } -bool RenderObject::hasOutlineAnnotation() const +static inline RenderElement* containerForElement(const RenderObject& renderer, const RenderLayerModelObject* repaintContainer, bool* repaintContainerSkipped) { - return node() && node()->isLink() && document().printing(); + // This method is extremely similar to containingBlock(), but with a few notable + // exceptions. + // (1) For normal flow elements, it just returns the parent. + // (2) For absolute positioned elements, it will return a relative positioned inline, while + // containingBlock() skips to the non-anonymous containing block. + // This does mean that computePositionedLogicalWidth and computePositionedLogicalHeight have to use container(). + EPosition pos = renderer.style().position(); + auto* parent = renderer.parent(); + if (is<RenderText>(renderer) || (pos != FixedPosition && pos != AbsolutePosition)) + return parent; + for (; parent && (pos == AbsolutePosition ? !parent->canContainAbsolutelyPositionedObjects() : !parent->canContainFixedPositionObjects()); parent = parent->parent()) { + if (repaintContainerSkipped && repaintContainer == parent) + *repaintContainerSkipped = true; + } + return parent; } -bool RenderObject::hasEntirelyFixedBackground() const +RenderElement* RenderObject::container() const { - return style().hasEntirelyFixedBackground(); + return containerForElement(*this, nullptr, nullptr); } -RenderElement* RenderObject::container(const RenderLayerModelObject* repaintContainer, bool* repaintContainerSkipped) const +RenderElement* RenderObject::container(const RenderLayerModelObject* repaintContainer, bool& repaintContainerSkipped) const { - if (repaintContainerSkipped) - *repaintContainerSkipped = false; - - // This method is extremely similar to containingBlock(), but with a few notable - // exceptions. - // (1) It can be used on orphaned subtrees, i.e., it can be called safely even when - // the object is not part of the primary document subtree yet. - // (2) For normal flow elements, it just returns the parent. - // (3) For absolute positioned elements, it will return a relative positioned inline. - // containingBlock() simply skips relpositioned inlines and lets an enclosing block handle - // the layout of the positioned object. This does mean that computePositionedLogicalWidth and - // computePositionedLogicalHeight have to use container(). - auto o = parent(); - - if (isText()) - return o; - - EPosition pos = style().position(); - if (pos == FixedPosition) { - // container() can be called on an object that is not in the - // tree yet. We don't call view() since it will assert if it - // can't get back to the canvas. Instead we just walk as high up - // as we can. If we're in the tree, we'll get the root. If we - // aren't we'll get the root of our little subtree (most likely - // we'll just return 0). - // FIXME: The definition of view() has changed to not crawl up the render tree. It might - // be safe now to use it. - while (o && o->parent() && !(o->hasTransform() && o->isRenderBlock())) { -#if ENABLE(SVG) - // foreignObject is the containing block for its contents. - if (o->isSVGForeignObject()) - break; -#endif - // The render flow thread is the top most containing block - // for the fixed positioned elements. - if (o->isOutOfFlowRenderFlowThread()) - break; - - if (repaintContainerSkipped && o == repaintContainer) - *repaintContainerSkipped = true; - - o = o->parent(); - } - } else if (pos == AbsolutePosition) { - // Same goes here. We technically just want our containing block, but - // we may not have one if we're part of an uninstalled subtree. We'll - // climb as high as we can though. - while (o && o->style().position() == StaticPosition && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) { -#if ENABLE(SVG) - if (o->isSVGForeignObject()) // foreignObject is the containing block for contents inside it - break; -#endif - if (repaintContainerSkipped && o == repaintContainer) - *repaintContainerSkipped = true; - - o = o->parent(); - } - } - - return o; + repaintContainerSkipped = false; + return containerForElement(*this, repaintContainer, &repaintContainerSkipped); } bool RenderObject::isSelectionBorder() const { SelectionState st = selectionState(); - return st == SelectionStart || st == SelectionEnd || st == SelectionBoth; -} - -inline void RenderObject::clearLayoutRootIfNeeded() const -{ - if (documentBeingDestroyed()) - return; - - if (view().frameView().layoutRoot() == this) { - ASSERT_NOT_REACHED(); - // This indicates a failure to layout the child, which is why - // the layout root is still set to |this|. Make sure to clear it - // since we are getting destroyed. - view().frameView().clearLayoutRoot(); - } + return st == SelectionStart + || st == SelectionEnd + || st == SelectionBoth + || view().selectionUnsplitStart() == this + || view().selectionUnsplitEnd() == this; } void RenderObject::willBeDestroyed() @@ -1840,41 +1467,22 @@ void RenderObject::willBeDestroyed() removeFromParent(); - ASSERT(documentBeingDestroyed() || !isRenderElement() || !view().frameView().hasSlowRepaintObject(toRenderElement(this))); + ASSERT(renderTreeBeingDestroyed() || !is<RenderElement>(*this) || !view().frameView().hasSlowRepaintObject(downcast<RenderElement>(*this))); // The remove() call above may invoke axObjectCache()->childrenChanged() on the parent, which may require the AX render // object for this renderer. So we remove the AX render object now, after the renderer is removed. if (AXObjectCache* cache = document().existingAXObjectCache()) cache->remove(this); -#ifndef NDEBUG - if (!documentBeingDestroyed() && view().hasRenderNamedFlowThreads()) { - // After remove, the object and the associated information should not be in any flow thread. - const RenderNamedFlowThreadList* flowThreadList = view().flowThreadController().renderNamedFlowThreadList(); - for (RenderNamedFlowThreadList::const_iterator iter = flowThreadList->begin(); iter != flowThreadList->end(); ++iter) { - const RenderNamedFlowThread* renderFlowThread = *iter; - ASSERT(!renderFlowThread->hasChild(this)); - ASSERT(!renderFlowThread->hasChildInfo(this)); - } - } -#endif - - // If this renderer had a parent, remove should have destroyed any counters - // attached to this renderer and marked the affected other counters for - // reevaluation. This apparently redundant check is here for the case when - // this renderer had no parent at the time remove() was called. - - if (hasCounterNodeMap()) - RenderCounter::destroyCounterNodes(this); - // FIXME: Would like to do this in RenderBoxModelObject, but the timing is so complicated that this can't easily - // be moved into RenderBoxModelObject::destroy. + // be moved into RenderLayerModelObject::willBeDestroyed(). + // FIXME: Is this still true? if (hasLayer()) { setHasLayer(false); - toRenderLayerModelObject(this)->destroyLayer(); + downcast<RenderLayerModelObject>(*this).destroyLayer(); } - clearLayoutRootIfNeeded(); + removeRareData(); } void RenderObject::insertedIntoTree() @@ -1882,88 +1490,73 @@ void RenderObject::insertedIntoTree() // FIXME: We should ASSERT(isRooted()) here but generated content makes some out-of-order insertion. if (!isFloating() && parent()->childrenInline()) - parent()->dirtyLinesFromChangedChild(this); + parent()->dirtyLinesFromChangedChild(*this); - if (RenderNamedFlowThread* containerFlowThread = parent()->renderNamedFlowThreadWrapper()) - containerFlowThread->addFlowChild(this); + if (RenderFlowThread* flowThread = flowThreadContainingBlock()) + flowThread->flowThreadDescendantInserted(*this); } void RenderObject::willBeRemovedFromTree() { // FIXME: We should ASSERT(isRooted()) but we have some out-of-order removals which would need to be fixed first. - - removeFromRenderFlowThread(); - - if (RenderNamedFlowThread* containerFlowThread = parent()->renderNamedFlowThreadWrapper()) - containerFlowThread->removeFlowChild(this); - -#if ENABLE(SVG) // Update cached boundaries in SVG renderers, if a child is removed. parent()->setNeedsBoundariesUpdate(); -#endif -} - -void RenderObject::removeFromRenderFlowThread() -{ - if (flowThreadState() == NotInsideFlowThread) - return; - - // Sometimes we remove the element from the flow, but it's not destroyed at that time. - // It's only until later when we actually destroy it and remove all the children from it. - // Currently, that happens for firstLetter elements and list markers. - // Pass in the flow thread so that we don't have to look it up for all the children. - removeFromRenderFlowThreadRecursive(flowThreadContainingBlock()); -} - -void RenderObject::removeFromRenderFlowThreadRecursive(RenderFlowThread* renderFlowThread) -{ - for (RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) - child->removeFromRenderFlowThreadRecursive(renderFlowThread); - - RenderFlowThread* localFlowThread = renderFlowThread; - if (flowThreadState() == InsideInFlowThread) - localFlowThread = flowThreadContainingBlock(); // We have to ask. We can't just assume we are in the same flow thread. - if (localFlowThread) - localFlowThread->removeFlowChildInfo(this); - setFlowThreadState(NotInsideFlowThread); } void RenderObject::destroyAndCleanupAnonymousWrappers() { // If the tree is destroyed, there is no need for a clean-up phase. - if (documentBeingDestroyed()) { + if (renderTreeBeingDestroyed()) { destroy(); return; } - RenderObject* destroyRoot = this; - for (auto destroyRootParent = destroyRoot->parent(); destroyRootParent && destroyRootParent->isAnonymous(); destroyRoot = destroyRootParent, destroyRootParent = destroyRootParent->parent()) { - // Currently we only remove anonymous cells' and table sections' wrappers but we should remove all unneeded - // wrappers. See http://webkit.org/b/52123 as an example where this is needed. - if (!destroyRootParent->isTableCell() && !destroyRootParent->isTableSection()) + auto* destroyRoot = this; + auto* destroyRootParent = destroyRoot->parent(); + while (destroyRootParent && destroyRootParent->isAnonymous()) { + if (!destroyRootParent->isTableCell() && !destroyRootParent->isTableRow() + && !destroyRootParent->isTableCaption() && !destroyRootParent->isTableSection() && !destroyRootParent->isTable()) break; - - if (destroyRootParent->firstChild() != this || destroyRootParent->lastChild() != this) + // single child? + if (!(destroyRootParent->firstChild() == destroyRoot && destroyRootParent->lastChild() == destroyRoot)) break; + destroyRoot = destroyRootParent; + destroyRootParent = destroyRootParent->parent(); } - destroyRoot->destroy(); + if (is<RenderTableRow>(*destroyRoot)) { + downcast<RenderTableRow>(*destroyRoot).destroyAndCollapseAnonymousSiblingRows(); + return; + } + destroyRoot->destroy(); // WARNING: |this| is deleted here. } void RenderObject::destroy() { + m_bitfields.setBeingDestroyed(true); + #if PLATFORM(IOS) if (hasLayer()) - toRenderBoxModelObject(this)->layer()->willBeDestroyed(); + downcast<RenderBoxModelObject>(*this).layer()->willBeDestroyed(); #endif willBeDestroyed(); + if (is<RenderWidget>(*this)) { + downcast<RenderWidget>(*this).deref(); + return; + } delete this; } -VisiblePosition RenderObject::positionForPoint(const LayoutPoint&) +Position RenderObject::positionForPoint(const LayoutPoint& point) +{ + // FIXME: This should just create a Position object instead (webkit.org/b/168566). + return positionForPoint(point, nullptr).deepEquivalent(); +} + +VisiblePosition RenderObject::positionForPoint(const LayoutPoint&, const RenderRegion*) { return createVisiblePosition(caretMinOffset(), DOWNSTREAM); } @@ -1972,15 +1565,26 @@ void RenderObject::updateDragState(bool dragOn) { bool valueChanged = (dragOn != isDragging()); setIsDragging(dragOn); - if (valueChanged && node() && (style().affectedByDrag() || (node()->isElementNode() && toElement(node())->childrenAffectedByDrag()))) - node()->setNeedsStyleRecalc(); - for (RenderObject* curr = firstChildSlow(); curr; curr = curr->nextSibling()) - curr->updateDragState(dragOn); + + if (!is<RenderElement>(*this)) + return; + auto& renderElement = downcast<RenderElement>(*this); + + if (valueChanged && renderElement.element() && (style().affectedByDrag() || renderElement.element()->childrenAffectedByDrag())) + renderElement.element()->invalidateStyleForSubtree(); + + for (auto& child : childrenOfType<RenderObject>(renderElement)) + child.updateDragState(dragOn); } bool RenderObject::isComposited() const { - return hasLayer() && toRenderLayerModelObject(this)->layer()->isComposited(); + return hasLayer() && downcast<RenderLayerModelObject>(*this).layer()->isComposited(); +} + +bool RenderObject::isAnonymousInlineBlock() const +{ + return isAnonymous() && style().display() == INLINE_BLOCK && style().styleType() == NOPSEUDO && isRenderBlockFlow() && !isRubyRun() && !isRubyBase() && !isRuby(parent()); } bool RenderObject::hitTest(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestFilter hitTestFilter) @@ -2016,7 +1620,7 @@ void RenderObject::updateHitTestResult(HitTestResult& result, const LayoutPoint& // If we hit the anonymous renderers inside generated content we should // actually hit the generated content so walk up to the PseudoElement. if (!node && parent() && parent()->isBeforeOrAfterContent()) { - for (auto renderer = parent(); renderer && !node; renderer = renderer->parent()) + for (auto* renderer = parent(); renderer && !node; renderer = renderer->parent()) node = renderer->element(); } @@ -2038,129 +1642,20 @@ int RenderObject::innerLineHeight() const return style().computedLineHeight(); } -RenderStyle* RenderObject::getCachedPseudoStyle(PseudoId pseudo, RenderStyle* parentStyle) const -{ - if (pseudo < FIRST_INTERNAL_PSEUDOID && !style().hasPseudoStyle(pseudo)) - return 0; - - RenderStyle* cachedStyle = style().getCachedPseudoStyle(pseudo); - if (cachedStyle) - return cachedStyle; - - RefPtr<RenderStyle> result = getUncachedPseudoStyle(PseudoStyleRequest(pseudo), parentStyle); - if (result) - return style().addCachedPseudoStyle(result.release()); - return 0; -} - -PassRefPtr<RenderStyle> RenderObject::getUncachedPseudoStyle(const PseudoStyleRequest& pseudoStyleRequest, RenderStyle* parentStyle, RenderStyle* ownStyle) const -{ - if (pseudoStyleRequest.pseudoId < FIRST_INTERNAL_PSEUDOID && !ownStyle && !style().hasPseudoStyle(pseudoStyleRequest.pseudoId)) - return 0; - - if (!parentStyle) { - ASSERT(!ownStyle); - parentStyle = &style(); - } - - // FIXME: This "find nearest element parent" should be a helper function. - Node* n = node(); - while (n && !n->isElementNode()) - n = n->parentNode(); - if (!n) - return 0; - Element* element = toElement(n); - - if (pseudoStyleRequest.pseudoId == FIRST_LINE_INHERITED) { - RefPtr<RenderStyle> result = document().ensureStyleResolver().styleForElement(element, parentStyle, DisallowStyleSharing); - result->setStyleType(FIRST_LINE_INHERITED); - return result.release(); - } - - return document().ensureStyleResolver().pseudoStyleForElement(element, pseudoStyleRequest, parentStyle); -} - -static Color decorationColor(RenderStyle* style) -{ - Color result; - // Check for text decoration color first. - result = style->visitedDependentColor(CSSPropertyWebkitTextDecorationColor); - if (result.isValid()) - return result; - if (style->textStrokeWidth() > 0) { - // Prefer stroke color if possible but not if it's fully transparent. - result = style->visitedDependentColor(CSSPropertyWebkitTextStrokeColor); - if (result.alpha()) - return result; - } - - result = style->visitedDependentColor(CSSPropertyWebkitTextFillColor); - return result; -} - -void RenderObject::getTextDecorationColors(int decorations, Color& underline, Color& overline, - Color& linethrough, bool quirksMode, bool firstlineStyle) -{ - RenderObject* curr = this; - RenderStyle* styleToUse = 0; - TextDecoration currDecs = TextDecorationNone; - Color resultColor; - do { - styleToUse = firstlineStyle ? &curr->firstLineStyle() : &curr->style(); - currDecs = styleToUse->textDecoration(); - resultColor = decorationColor(styleToUse); - // Parameter 'decorations' is cast as an int to enable the bitwise operations below. - if (currDecs) { - if (currDecs & TextDecorationUnderline) { - decorations &= ~TextDecorationUnderline; - underline = resultColor; - } - if (currDecs & TextDecorationOverline) { - decorations &= ~TextDecorationOverline; - overline = resultColor; - } - if (currDecs & TextDecorationLineThrough) { - decorations &= ~TextDecorationLineThrough; - linethrough = resultColor; - } - } - if (curr->isRubyText()) - return; - curr = curr->parent(); - if (curr && curr->isAnonymousBlock() && toRenderBlock(curr)->continuation()) - curr = toRenderBlock(curr)->continuation(); - } while (curr && decorations && (!quirksMode || !curr->node() || (!isHTMLAnchorElement(curr->node()) && !curr->node()->hasTagName(fontTag)))); - - // If we bailed out, use the element we bailed out at (typically a <font> or <a> element). - if (decorations && curr) { - styleToUse = firstlineStyle ? &curr->firstLineStyle() : &curr->style(); - resultColor = decorationColor(styleToUse); - if (decorations & TextDecorationUnderline) - underline = resultColor; - if (decorations & TextDecorationOverline) - overline = resultColor; - if (decorations & TextDecorationLineThrough) - linethrough = resultColor; - } -} - #if ENABLE(DASHBOARD_SUPPORT) void RenderObject::addAnnotatedRegions(Vector<AnnotatedRegionValue>& regions) { // Convert the style regions to absolute coordinates. - if (style().visibility() != VISIBLE || !isBox()) + if (style().visibility() != VISIBLE || !is<RenderBox>(*this)) return; - RenderBox* box = toRenderBox(this); + auto& box = downcast<RenderBox>(*this); FloatPoint absPos = localToAbsolute(); const Vector<StyleDashboardRegion>& styleRegions = style().dashboardRegions(); - unsigned i, count = styleRegions.size(); - for (i = 0; i < count; i++) { - StyleDashboardRegion styleRegion = styleRegions[i]; - - LayoutUnit w = box->width(); - LayoutUnit h = box->height(); + for (const auto& styleRegion : styleRegions) { + LayoutUnit w = box.width(); + LayoutUnit h = box.height(); AnnotatedRegionValue region; region.label = styleRegion.label; @@ -2170,8 +1665,7 @@ void RenderObject::addAnnotatedRegions(Vector<AnnotatedRegionValue>& regions) h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value()); region.type = styleRegion.type; - region.clip = region.bounds; - computeAbsoluteRepaintRect(region.clip); + region.clip = computeAbsoluteRepaintRect(region.bounds); if (region.clip.height() < 0) { region.clip.setHeight(0); region.clip.setWidth(0); @@ -2188,42 +1682,15 @@ void RenderObject::collectAnnotatedRegions(Vector<AnnotatedRegionValue>& regions { // RenderTexts don't have their own style, they just use their parent's style, // so we don't want to include them. - if (isText()) + if (is<RenderText>(*this)) return; addAnnotatedRegions(regions); - for (RenderObject* curr = toRenderElement(this)->firstChild(); curr; curr = curr->nextSibling()) - curr->collectAnnotatedRegions(regions); + for (RenderObject* current = downcast<RenderElement>(*this).firstChild(); current; current = current->nextSibling()) + current->collectAnnotatedRegions(regions); } #endif -bool RenderObject::willRenderImage(CachedImage*) -{ - // Without visibility we won't render (and therefore don't care about animation). - if (style().visibility() != VISIBLE) - return false; - -#if PLATFORM(IOS) - if (document().frame()->timersPaused()) - return false; -#else - // We will not render a new image when Active DOM is suspended - if (document().activeDOMObjectsAreSuspended()) - return false; -#endif - - // If we're not in a window (i.e., we're dormant from being put in the b/f cache or in a background tab) - // then we don't want to render either. - return !document().inPageCache() && !document().view()->isOffscreen(); -} - -int RenderObject::maximalOutlineSize(PaintPhase p) const -{ - if (p != PaintPhaseOutline && p != PaintPhaseSelfOutline && p != PaintPhaseChildOutlines) - return 0; - return view().maximalOutlineSize(); -} - int RenderObject::caretMinOffset() const { return 0; @@ -2232,7 +1699,7 @@ int RenderObject::caretMinOffset() const int RenderObject::caretMaxOffset() const { if (isReplaced()) - return node() ? std::max(1U, node()->childNodeCount()) : 1; + return node() ? std::max(1U, node()->countChildNodes()) : 1; if (isHR()) return 1; return 0; @@ -2255,20 +1722,14 @@ int RenderObject::nextOffset(int current) const void RenderObject::adjustRectForOutlineAndShadow(LayoutRect& rect) const { - int outlineSize = outlineStyleForRepaint().outlineSize(); + LayoutUnit outlineSize = outlineStyleForRepaint().outlineSize(); if (const ShadowData* boxShadow = style().boxShadow()) { boxShadow->adjustRectForShadow(rect, outlineSize); return; } - rect.inflate(outlineSize); } -AnimationController& RenderObject::animation() const -{ - return frame().animation(); -} - void RenderObject::imageChanged(CachedImage* image, const IntRect* rect) { imageChanged(static_cast<WrappedImagePtr>(image), rect); @@ -2280,8 +1741,8 @@ RenderBoxModelObject* RenderObject::offsetParent() const // A is the root element. // A is the HTML body element. // The computed value of the position property for element A is fixed. - if (isRoot() || isBody() || (isOutOfFlowPositioned() && style().position() == FixedPosition)) - return 0; + if (isDocumentElementRenderer() || isBody() || (isOutOfFlowPositioned() && style().position() == FixedPosition)) + return nullptr; // If A is an area HTML element which has a map HTML element somewhere in the ancestor // chain return the nearest ancestor map HTML element and stop this algorithm. @@ -2297,24 +1758,26 @@ RenderBoxModelObject* RenderObject::offsetParent() const bool skipTables = isPositioned(); float currZoom = style().effectiveZoom(); - auto curr = parent(); - while (curr && (!curr->element() || (!curr->isPositioned() && !curr->isBody())) && !curr->isRenderNamedFlowThread()) { - Element* element = curr->element(); - if (!skipTables && element && (isHTMLTableElement(element) || element->hasTagName(tdTag) || element->hasTagName(thTag))) + auto current = parent(); + while (current && (!current->element() || (!current->isPositioned() && !current->isBody())) && !is<RenderNamedFlowThread>(*current)) { + Element* element = current->element(); + if (!skipTables && element && (is<HTMLTableElement>(*element) || is<HTMLTableCellElement>(*element))) break; - float newZoom = curr->style().effectiveZoom(); + float newZoom = current->style().effectiveZoom(); if (currZoom != newZoom) break; currZoom = newZoom; - curr = curr->parent(); + current = current->parent(); } // CSS regions specification says that region flows should return the body element as their offsetParent. - if (curr && curr->isRenderNamedFlowThread()) - curr = document().body() ? document().body()->renderer() : 0; + if (is<RenderNamedFlowThread>(current)) { + auto* body = document().bodyOrFrameset(); + current = body ? body->renderer() : nullptr; + } - return curr && curr->isBoxModelObject() ? toRenderBoxModelObject(curr) : 0; + return is<RenderBoxModelObject>(current) ? downcast<RenderBoxModelObject>(current) : nullptr; } VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affinity) const @@ -2405,20 +1868,7 @@ bool RenderObject::canHaveGeneratedChildren() const Node* RenderObject::generatingPseudoHostElement() const { - return toPseudoElement(node())->hostElement(); -} - -bool RenderObject::canBeReplacedWithInlineRunIn() const -{ - return true; -} - -#if ENABLE(SVG) - -RenderSVGResourceContainer* RenderObject::toRenderSVGResourceContainer() -{ - ASSERT_NOT_REACHED(); - return 0; + return downcast<PseudoElement>(*node()).hostElement(); } void RenderObject::setNeedsBoundariesUpdate() @@ -2465,37 +1915,157 @@ bool RenderObject::nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const return false; } -#endif // ENABLE(SVG) +RenderNamedFlowFragment* RenderObject::currentRenderNamedFlowFragment() const +{ + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (!is<RenderNamedFlowThread>(flowThread)) + return nullptr; -} // namespace WebCore + // FIXME: Once regions are fully integrated with the compositing system we should uncomment this assert. + // This assert needs to be disabled because it's possible to ask for the ancestor clipping rectangle of + // a layer without knowing the containing region in advance. + // ASSERT(flowThread->currentRegion() && flowThread->currentRegion()->isRenderNamedFlowFragment()); -#ifndef NDEBUG + return downcast<RenderNamedFlowFragment>(flowThread->currentRegion()); +} -void showTree(const WebCore::RenderObject* object) +RenderFlowThread* RenderObject::locateFlowThreadContainingBlock() const { - if (object) - object->showTreeForThis(); + RenderBlock* containingBlock = this->containingBlock(); + return containingBlock ? containingBlock->flowThreadContainingBlock() : nullptr; } -void showLineTree(const WebCore::RenderObject* object) +void RenderObject::calculateBorderStyleColor(const EBorderStyle& style, const BoxSide& side, Color& color) +{ + ASSERT(style == INSET || style == OUTSET); + // This values were derived empirically. + const RGBA32 baseDarkColor = 0xFF202020; + const RGBA32 baseLightColor = 0xFFEBEBEB; + enum Operation { Darken, Lighten }; + + Operation operation = (side == BSTop || side == BSLeft) == (style == INSET) ? Darken : Lighten; + + // Here we will darken the border decoration color when needed. This will yield a similar behavior as in FF. + if (operation == Darken) { + if (differenceSquared(color, Color::black) > differenceSquared(baseDarkColor, Color::black)) + color = color.dark(); + } else { + if (differenceSquared(color, Color::white) > differenceSquared(baseLightColor, Color::white)) + color = color.light(); + } +} + +void RenderObject::setIsDragging(bool isDragging) +{ + if (isDragging || hasRareData()) + ensureRareData().setIsDragging(isDragging); +} + +void RenderObject::setHasReflection(bool hasReflection) +{ + if (hasReflection || hasRareData()) + ensureRareData().setHasReflection(hasReflection); +} + +void RenderObject::setIsRenderFlowThread(bool isFlowThread) +{ + if (isFlowThread || hasRareData()) + ensureRareData().setIsRenderFlowThread(isFlowThread); +} + +void RenderObject::setHasOutlineAutoAncestor(bool hasOutlineAutoAncestor) +{ + if (hasOutlineAutoAncestor || hasRareData()) + ensureRareData().setHasOutlineAutoAncestor(hasOutlineAutoAncestor); +} + +void RenderObject::setIsRegisteredForVisibleInViewportCallback(bool registered) +{ + if (registered || hasRareData()) + ensureRareData().setIsRegisteredForVisibleInViewportCallback(registered); +} + +void RenderObject::setVisibleInViewportState(VisibleInViewportState visible) +{ + if (visible != VisibilityUnknown || hasRareData()) + ensureRareData().setVisibleInViewportState(visible); +} + +RenderObject::RareDataMap& RenderObject::rareDataMap() { - if (object) - object->showLineTreeForThis(); + static NeverDestroyed<RareDataMap> map; + return map; } -void showRenderTree(const WebCore::RenderObject* object1) +const RenderObject::RenderObjectRareData& RenderObject::rareData() const { - showRenderTree(object1, 0); + ASSERT(hasRareData()); + return *rareDataMap().get(this); } -void showRenderTree(const WebCore::RenderObject* object1, const WebCore::RenderObject* object2) +RenderObject::RenderObjectRareData& RenderObject::ensureRareData() { - if (object1) { - const WebCore::RenderObject* root = object1; - while (root->parent()) - root = root->parent(); - root->showRenderTreeAndMark(object1, "*", object2, "-", 0); + setHasRareData(true); + return *rareDataMap().ensure(this, [] { return std::make_unique<RenderObjectRareData>(); }).iterator->value; +} + +void RenderObject::removeRareData() +{ + rareDataMap().remove(this); + setHasRareData(false); +} + +#if ENABLE(TREE_DEBUGGING) + +void printRenderTreeForLiveDocuments() +{ + for (const auto* document : Document::allDocuments()) { + if (!document->renderView()) + continue; + if (document->frame() && document->frame()->isMainFrame()) + fprintf(stderr, "----------------------main frame--------------------------\n"); + fprintf(stderr, "%s", document->url().string().utf8().data()); + showRenderTree(document->renderView()); } } +void printLayerTreeForLiveDocuments() +{ + for (const auto* document : Document::allDocuments()) { + if (!document->renderView()) + continue; + if (document->frame() && document->frame()->isMainFrame()) + fprintf(stderr, "----------------------main frame--------------------------\n"); + fprintf(stderr, "%s", document->url().string().utf8().data()); + showLayerTree(document->renderView()); + } +} + +#endif // ENABLE(TREE_DEBUGGING) + +} // namespace WebCore + +#if ENABLE(TREE_DEBUGGING) + +void showNodeTree(const WebCore::RenderObject* object) +{ + if (!object) + return; + object->showNodeTreeForThis(); +} + +void showLineTree(const WebCore::RenderObject* object) +{ + if (!object) + return; + object->showLineTreeForThis(); +} + +void showRenderTree(const WebCore::RenderObject* object) +{ + if (!object) + return; + object->showRenderTreeForThis(); +} + #endif |