diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
commit | 32761a6cee1d0dee366b885b7b9c777e67885688 (patch) | |
tree | d6bec92bebfb216f4126356e55518842c2f476a1 /Source/WebCore/rendering/RenderObject.cpp | |
parent | a4e969f4965059196ca948db781e52f7cfebf19e (diff) | |
download | WebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz |
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/WebCore/rendering/RenderObject.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderObject.cpp | 1651 |
1 files changed, 917 insertions, 734 deletions
diff --git a/Source/WebCore/rendering/RenderObject.cpp b/Source/WebCore/rendering/RenderObject.cpp index edb4f3398..fa99f248c 100644 --- a/Source/WebCore/rendering/RenderObject.cpp +++ b/Source/WebCore/rendering/RenderObject.cpp @@ -32,20 +32,17 @@ #include "EventHandler.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 "Logging.h" #include "LogicalSelectionOffsetCaches.h" -#include "MainFrame.h" #include "Page.h" #include "PseudoElement.h" #include "RenderCounter.h" @@ -55,26 +52,22 @@ #include "RenderIterator.h" #include "RenderLayer.h" #include "RenderLayerBacking.h" -#include "RenderMultiColumnFlowThread.h" -#include "RenderNamedFlowFragment.h" -#include "RenderNamedFlowThread.h" -#include "RenderRuby.h" -#include "RenderSVGResourceContainer.h" +#include "RenderNamedFlowThread.h" #include "RenderScrollbarPart.h" -#include "RenderTableRow.h" -#include "RenderTableSection.h" #include "RenderTheme.h" #include "RenderView.h" -#include "RenderWidget.h" -#include "SVGRenderSupport.h" #include "Settings.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 @@ -84,8 +77,6 @@ namespace WebCore { using namespace HTMLNames; #ifndef NDEBUG -void printRenderTreeForLiveDocuments(); - RenderObject::SetLayoutNeededForbiddenScope::SetLayoutNeededForbiddenScope(RenderObject* renderObject, bool isForbidden) : m_renderObject(renderObject) , m_preexistingForbidden(m_renderObject->isSetNeedsLayoutForbidden()) @@ -115,34 +106,29 @@ DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, renderObjectCounter, ("Rend RenderObject::RenderObject(Node& node) : CachedImageClient() , m_node(node) - , m_parent(nullptr) - , m_previous(nullptr) - , m_next(nullptr) + , m_parent(0) + , m_previous(0) + , m_next(0) #ifndef NDEBUG , m_hasAXObject(false) , m_setNeedsLayoutForbidden(false) #endif , m_bitfields(node) { - if (RenderView* renderView = node.document().renderView()) - renderView->didCreateRenderer(); + if (!node.isDocumentNode()) + view().didCreateRenderer(); #ifndef NDEBUG renderObjectCounter.increment(); - static std::once_flag onceFlag; - std::call_once(onceFlag, [] { - registerNotifyCallback("com.apple.WebKit.showRenderTree", printRenderTreeForLiveDocuments); - }); #endif } RenderObject::~RenderObject() { - view().didDestroyRenderer(); #ifndef NDEBUG ASSERT(!m_hasAXObject); renderObjectCounter.decrement(); #endif - ASSERT(!hasRareData()); + view().didDestroyRenderer(); } RenderTheme& RenderObject::theme() const @@ -151,10 +137,10 @@ RenderTheme& RenderObject::theme() const return document().page()->theme(); } -bool RenderObject::isDescendantOf(const RenderObject* ancestor) const +bool RenderObject::isDescendantOf(const RenderObject* obj) const { - for (const RenderObject* renderer = this; renderer; renderer = renderer->m_parent) { - if (renderer == ancestor) + for (const RenderObject* r = this; r; r = r->m_parent) { + if (r == obj) return true; } return false; @@ -234,14 +220,14 @@ RenderObject* RenderObject::nextInPreOrder(const RenderObject* stayWithin) const RenderObject* RenderObject::nextInPreOrderAfterChildren(const RenderObject* stayWithin) const { if (this == stayWithin) - return nullptr; + return 0; const RenderObject* current = this; RenderObject* next; while (!(next = current->nextSibling())) { current = current->parent(); if (!current || current == stayWithin) - return nullptr; + return 0; } return next; } @@ -260,7 +246,7 @@ RenderObject* RenderObject::previousInPreOrder() const RenderObject* RenderObject::previousInPreOrder(const RenderObject* stayWithin) const { if (this == stayWithin) - return nullptr; + return 0; return previousInPreOrder(); } @@ -277,7 +263,7 @@ RenderObject* RenderObject::firstLeafChild() const { RenderObject* r = firstChildSlow(); while (r) { - RenderObject* n = nullptr; + RenderObject* n = 0; n = r->firstChildSlow(); if (!n) break; @@ -290,7 +276,7 @@ RenderObject* RenderObject::lastLeafChild() const { RenderObject* r = lastChildSlow(); while (r) { - RenderObject* n = nullptr; + RenderObject* n = 0; n = r->lastChildSlow(); if (!n) break; @@ -309,7 +295,7 @@ RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin) const return child; } if (this == stayWithin) - return nullptr; + return 0; if (nextSibling()) { ASSERT(!stayWithin || nextSibling()->isDescendantOf(stayWithin)); return nextSibling(); @@ -321,7 +307,7 @@ RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin) const ASSERT(!stayWithin || !n->nextSibling() || n->nextSibling()->isDescendantOf(stayWithin)); return n->nextSibling(); } - return nullptr; + return 0; } // Non-recursive version of the DFS search. @@ -342,7 +328,7 @@ RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, HeightT } if (this == stayWithin) - return nullptr; + return 0; // Now we traverse other nodes if they exist, otherwise // we go to the parent node and try doing the same. @@ -353,7 +339,7 @@ RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, HeightT currentDepth--; } if (!n) - return nullptr; + return 0; for (RenderObject* sibling = n->nextSibling(); sibling; sibling = sibling->nextSibling()) { overflowType = inclusionFunction(sibling); if (overflowType != FixedHeight) { @@ -367,9 +353,9 @@ RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, HeightT n = n->parent(); currentDepth--; } else - return nullptr; + return 0; } - return nullptr; + return 0; } RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, TraverseNextInclusionFunction inclusionFunction) const @@ -382,7 +368,7 @@ RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, Travers } if (this == stayWithin) - return nullptr; + return 0; for (RenderObject* sibling = nextSibling(); sibling; sibling = sibling->nextSibling()) { if (inclusionFunction(sibling)) { @@ -405,21 +391,21 @@ RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, Travers if ((!stayWithin || n->parent() != stayWithin)) n = n->parent(); else - return nullptr; + return 0; } } - return nullptr; + return 0; } -static RenderObject::BlockContentHeightType includeNonFixedHeight(const RenderObject* renderer) +static RenderObject::BlockContentHeightType includeNonFixedHeight(const RenderObject* render) { - const RenderStyle& style = renderer->style(); + const RenderStyle& style = render->style(); if (style.height().type() == Fixed) { - if (is<RenderBlock>(*renderer)) { + 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() < downcast<RenderBlock>(renderer)->layoutOverflowRect().maxY()) + if (style.overflowY() == OVISIBLE && style.height().value() < block->layoutOverflowRect().maxY()) return RenderObject::OverflowHeight; } return RenderObject::FixedHeight; @@ -449,8 +435,8 @@ void RenderObject::adjustComputedFontSizesOnBlocks(float size, float visibleWidt depthStack.append(newFixedDepth); int stackSize = depthStack.size(); - if (is<RenderBlockFlow>(*descendent) && !descendent->isListItem() && (!stackSize || currentDepth - depthStack[stackSize - 1] > TextAutoSizingFixedHeightDepth)) - downcast<RenderBlockFlow>(*descendent).adjustComputedFontSizes(size, visibleWidth); + if (descendent->isRenderBlockFlow() && !descendent->isListItem() && (!stackSize || currentDepth - depthStack[stackSize - 1] > TextAutoSizingFixedHeightDepth)) + toRenderBlockFlow(descendent)->adjustComputedFontSizes(size, visibleWidth); newFixedDepth = 0; } @@ -477,8 +463,8 @@ void RenderObject::resetTextAutosizing() depthStack.append(newFixedDepth); int stackSize = depthStack.size(); - if (is<RenderBlockFlow>(*descendent) && !descendent->isListItem() && (!stackSize || currentDepth - depthStack[stackSize - 1] > TextAutoSizingFixedHeightDepth)) - downcast<RenderBlockFlow>(*descendent).resetComputedFontSize(); + if (descendent->isRenderBlockFlow() && !descendent->isListItem() && (!stackSize || currentDepth - depthStack[stackSize - 1] > TextAutoSizingFixedHeightDepth)) + toRenderBlockFlow(descendent)->resetComputedFontSize(); newFixedDepth = 0; } } @@ -503,14 +489,16 @@ bool RenderObject::scrollRectToVisible(const LayoutRect& rect, const ScrollAlign return true; } -RenderBox& RenderObject::enclosingBox() const +RenderBox* RenderObject::enclosingBox() const { - return *lineageOfType<RenderBox>(const_cast<RenderObject&>(*this)).first(); + // FIXME: This should return a reference; it can always find the root RenderView. + return lineageOfType<RenderBox>(const_cast<RenderObject&>(*this)).first(); } -RenderBoxModelObject& RenderObject::enclosingBoxModelObject() const +RenderBoxModelObject* RenderObject::enclosingBoxModelObject() const { - return *lineageOfType<RenderBoxModelObject>(const_cast<RenderObject&>(*this)).first(); + // FIXME: This should return a reference; it can always find the root RenderView. + return lineageOfType<RenderBoxModelObject>(const_cast<RenderObject&>(*this)).first(); } bool RenderObject::fixedPositionedWithNamedFlowContainingBlock() const @@ -525,7 +513,7 @@ static bool hasFixedPosInNamedFlowContainingBlock(const RenderObject* renderer) ASSERT(renderer->flowThreadState() != RenderObject::NotInsideFlowThread); RenderObject* curr = const_cast<RenderObject*>(renderer); - while (curr && !is<RenderView>(*curr)) { + while (curr) { if (curr->fixedPositionedWithNamedFlowContainingBlock()) return true; curr = curr->containingBlock(); @@ -534,9 +522,37 @@ 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 nullptr; + return 0; } static inline bool objectIsRelayoutBoundary(const RenderElement* object) @@ -548,13 +564,15 @@ 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().isPercentOrCalculated()) + if (object->style().width().isIntrinsicOrAuto() || object->style().height().isIntrinsicOrAuto() || object->style().height().isPercent()) return false; // Table parts can't be relayout roots since the table is responsible for layouting all the parts. @@ -572,8 +590,8 @@ void RenderObject::clearNeedsLayout() setNeedsSimplifiedNormalFlowLayoutBit(false); setNormalChildNeedsLayoutBit(false); setNeedsPositionedMovementLayoutBit(false); - if (is<RenderElement>(*this)) - downcast<RenderElement>(*this).setAncestorLineBoxDirty(false); + if (isRenderElement()) + toRenderElement(this)->setAncestorLineBoxDirty(false); #ifndef NDEBUG checkBlockPositionedObjectsNeedLayout(); #endif @@ -581,18 +599,18 @@ void RenderObject::clearNeedsLayout() static void scheduleRelayoutForSubtree(RenderElement& renderer) { - if (is<RenderView>(renderer)) { - downcast<RenderView>(renderer).frameView().scheduleRelayout(); + if (!renderer.isRenderView()) { + if (!renderer.isRooted()) + return; + renderer.view().frameView().scheduleRelayoutOfSubtree(renderer); return; } - - if (renderer.isRooted()) - renderer.view().frameView().scheduleRelayoutOfSubtree(renderer); + toRenderView(renderer).frameView().scheduleRelayout(); } -void RenderObject::markContainingBlocksForLayout(ScheduleRelayout scheduleRelayout, RenderElement* newRoot) +void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, RenderElement* newRoot) { - ASSERT(scheduleRelayout == ScheduleRelayout::No || !newRoot); + ASSERT(!scheduleRelayout || !newRoot); ASSERT(!isSetNeedsLayoutForbidden()); auto ancestor = container(); @@ -636,14 +654,14 @@ void RenderObject::markContainingBlocksForLayout(ScheduleRelayout scheduleRelayo if (ancestor == newRoot) return; - if (scheduleRelayout == ScheduleRelayout::Yes && objectIsRelayoutBoundary(ancestor)) + if (scheduleRelayout && objectIsRelayoutBoundary(ancestor)) break; hasOutOfFlowPosition = ancestor->style().hasOutOfFlowPosition(); ancestor = container; } - if (scheduleRelayout == ScheduleRelayout::Yes && ancestor) + if (scheduleRelayout && ancestor) scheduleRelayoutForSubtree(*ancestor); } @@ -652,8 +670,8 @@ void RenderObject::checkBlockPositionedObjectsNeedLayout() { ASSERT(!needsLayout()); - if (is<RenderBlock>(*this)) - downcast<RenderBlock>(*this).checkPositionedObjectsNeedLayout(); + if (isRenderBlock()) + toRenderBlock(this)->checkPositionedObjectsNeedLayout(); } #endif @@ -689,50 +707,371 @@ void RenderObject::invalidateContainerPreferredLogicalWidths() void RenderObject::setLayerNeedsFullRepaint() { ASSERT(hasLayer()); - downcast<RenderLayerModelObject>(*this).layer()->setRepaintStatus(NeedsFullRepaint); + toRenderLayerModelObject(this)->layer()->setRepaintStatus(NeedsFullRepaint); } void RenderObject::setLayerNeedsFullRepaintForPositionedMovementLayout() { ASSERT(hasLayer()); - downcast<RenderLayerModelObject>(*this).layer()->setRepaintStatus(NeedsFullRepaintForPositionedMovementLayout); + toRenderLayerModelObject(this)->layer()->setRepaintStatus(NeedsFullRepaintForPositionedMovementLayout); } RenderBlock* RenderObject::containingBlock() const { - auto parent = this->parent(); - if (!parent && is<RenderScrollbarPart>(*this)) - parent = downcast<RenderScrollbarPart>(*this).rendererOwningScrollbar(); + auto o = parent(); + if (!o && isRenderScrollbarPart()) + o = toRenderScrollbarPart(this)->rendererOwningScrollbar(); const RenderStyle& style = this->style(); - if (!is<RenderText>(*this) && style.position() == FixedPosition) - parent = containingBlockForFixedPosition(parent); - else if (!is<RenderText>(*this) && style.position() == AbsolutePosition) - parent = containingBlockForAbsolutePosition(parent); + if (!isText() && style.position() == FixedPosition) + o = containingBlockForFixedPosition(o); + else if (!isText() && style.position() == AbsolutePosition) + o = containingBlockForAbsolutePosition(o); else - parent = containingBlockForObjectInFlow(parent); + o = containingBlockForObjectInFlow(o); + + if (!o || !o->isRenderBlock()) + return 0; // This can still happen in case of an orphaned tree - // This can still happen in case of an detached tree - if (!parent) - return nullptr; - return downcast<RenderBlock>(parent); + return toRenderBlock(o); } -void RenderObject::addPDFURLRect(PaintInfo& paintInfo, const LayoutPoint& paintOffset) +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) { - Vector<LayoutRect> focusRingRects; + 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; + } + } +} + +void RenderObject::paintFocusRing(PaintInfo& paintInfo, const LayoutPoint& paintOffset, RenderStyle* style) +{ + Vector<IntRect> focusRingRects; addFocusRingRects(focusRingRects, paintOffset, paintInfo.paintContainer); - LayoutRect urlRect = unionRect(focusRingRects); + if (style->outlineStyleIsAuto()) + paintInfo.context->drawFocusRing(focusRingRects, style->outlineWidth(), style->outlineOffset(), style->visitedDependentColor(CSSPropertyOutlineColor)); + else + addPDFURLRect(paintInfo.context, unionRect(focusRingRects)); +} - if (urlRect.isEmpty()) +void RenderObject::addPDFURLRect(GraphicsContext* context, const LayoutRect& rect) +{ + if (rect.isEmpty()) return; - Node* node = this->node(); - if (!is<Element>(node) || !node->isLink()) + Node* n = node(); + if (!n || !n->isLink() || !n->isElementNode()) return; - const AtomicString& href = downcast<Element>(*node).getAttribute(hrefAttr); + const AtomicString& href = toElement(n)->getAttribute(hrefAttr); if (href.isNull()) return; - paintInfo.context().setURLForRect(node->document().completeURL(href), snappedIntRect(urlRect)); + 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); + 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(); +} + +// 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) @@ -762,15 +1101,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(), view().pageNumberForBlockProgressionOffset(quads[i].enclosingBoundingBox().x()))); + rects.append(SelectionRect(quads[i].enclosingBoundingBox(), isHorizontalWritingMode(), columnNumberForOffset(quads[i].enclosingBoundingBox().x()))); } #endif -IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms, bool* wasFixed) const +IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms) const { if (useTransforms) { Vector<FloatQuad> quads; - absoluteQuads(quads, wasFixed); + absoluteQuads(quads); size_t n = quads.size(); if (!n) @@ -782,7 +1121,7 @@ IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms, bool* wasFixed return result; } - FloatPoint absPos = localToAbsolute(FloatPoint(), 0 /* ignore transforms */, wasFixed); + FloatPoint absPos = localToAbsolute(); Vector<IntRect> rects; absoluteRects(rects, flooredLayoutPoint(absPos)); @@ -793,34 +1132,35 @@ IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms, bool* wasFixed LayoutRect result = rects[0]; for (size_t i = 1; i < n; ++i) result.unite(rects[i]); - return snappedIntRect(result); + return pixelSnappedIntRect(result); } void RenderObject::absoluteFocusRingQuads(Vector<FloatQuad>& quads) { - Vector<LayoutRect> rects; + Vector<IntRect> 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)); - float deviceScaleFactor = document().deviceScaleFactor(); - for (auto rect : rects) { - rect.moveBy(LayoutPoint(-absolutePoint)); - quads.append(localToAbsoluteQuad(FloatQuad(snapRectToDevicePixels(rect, deviceScaleFactor)))); + 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))); } } FloatRect RenderObject::absoluteBoundingBoxRectForRange(const Range* range) { - if (!range) + if (!range || !range->startContainer()) return FloatRect(); range->ownerDocument().updateLayout(); Vector<FloatQuad> quads; - range->absoluteTextQuads(quads); + range->textQuads(quads); if (quads.isEmpty()) return FloatRect(); @@ -852,8 +1192,9 @@ LayoutRect RenderObject::paintingRootRect(LayoutRect& topLevelRect) RenderLayerModelObject* RenderObject::containerForRepaint() const { - RenderLayerModelObject* repaintContainer = nullptr; + RenderLayerModelObject* repaintContainer = 0; +#if USE(ACCELERATED_COMPOSITING) if (view().usesCompositing()) { if (RenderLayer* parentLayer = enclosingLayer()) { RenderLayer* compLayer = parentLayer->enclosingCompositingLayerForRepaint(); @@ -861,6 +1202,9 @@ RenderLayerModelObject* RenderObject::containerForRepaint() const repaintContainer = &compLayer->renderer(); } } +#endif + +#if ENABLE(CSS_FILTERS) if (view().hasSoftwareFilters()) { if (RenderLayer* parentLayer = enclosingLayer()) { RenderLayer* enclosingFilterLayer = parentLayer->enclosingFilterLayer(); @@ -868,6 +1212,7 @@ 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 @@ -878,149 +1223,97 @@ 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() : nullptr; + RenderFlowThread* repaintContainerFlowThread = repaintContainer ? repaintContainer->flowThreadContainingBlock() : 0; if (!repaintContainerFlowThread || repaintContainerFlowThread != parentRenderFlowThread) repaintContainer = parentRenderFlowThread; } return repaintContainer; } -void RenderObject::propagateRepaintToParentWithOutlineAutoIfNeeded(const RenderLayerModelObject& repaintContainer, const LayoutRect& repaintRect) const +void RenderObject::repaintUsingContainer(const RenderLayerModelObject* repaintContainer, const IntRect& r, bool immediate, bool shouldClipToLayer) const { - 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); - } + if (!repaintContainer) { + view().repaintViewRectangle(r, immediate); 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 (is<RenderFlowThread>(*repaintContainer)) { - downcast<RenderFlowThread>(*repaintContainer).repaintRectangleInRegions(r); + if (repaintContainer->isRenderFlowThread()) { + toRenderFlowThread(repaintContainer)->repaintRectangleInRegions(r, immediate); return; } - propagateRepaintToParentWithOutlineAutoIfNeeded(*repaintContainer, r); - +#if ENABLE(CSS_FILTERS) if (repaintContainer->hasFilter() && repaintContainer->layer() && repaintContainer->layer()->requiresFullLayerImageForFilters()) { - repaintContainer->layer()->setFilterBackendNeedsRepaintingInRect(r); + repaintContainer->layer()->setFilterBackendNeedsRepaintingInRect(r, immediate); return; } +#endif +#if USE(ACCELERATED_COMPOSITING) + RenderView& v = view(); if (repaintContainer->isRenderView()) { - 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); + 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); return; } } - - if (view().usesCompositing()) { - ASSERT(repaintContainer->isComposited()); + + if (v.usesCompositing()) { + ASSERT(repaintContainer->hasLayer() && repaintContainer->layer()->isComposited()); repaintContainer->layer()->setBackingNeedsRepaintInRect(r, shouldClipToLayer ? GraphicsLayer::ClipToLayer : GraphicsLayer::DoNotClipToLayer); } +#else + if (repaintContainer->isRenderView()) + toRenderView(*repaintContainer).repaintViewRectangle(r, immediate); +#endif } -void RenderObject::repaint() const +void RenderObject::repaint(bool immediate) const { // Don't repaint if we're unrooted (note that view() still returns the view when unrooted) - if (!isRooted()) + RenderView* view; + if (!isRooted(&view)) return; - const RenderView& view = this->view(); - if (view.printing()) - return; + if (view->printing()) + return; // Don't repaint if we're printing. RenderLayerModelObject* repaintContainer = containerForRepaint(); - repaintUsingContainer(repaintContainer, clippedOverflowRectForRepaint(repaintContainer)); + repaintUsingContainer(repaintContainer ? repaintContainer : view, pixelSnappedIntRect(clippedOverflowRectForRepaint(repaintContainer)), immediate); } -void RenderObject::repaintRectangle(const LayoutRect& r, bool shouldClipToLayer) const +void RenderObject::repaintRectangle(const LayoutRect& r, bool immediate, bool shouldClipToLayer) const { // Don't repaint if we're unrooted (note that view() still returns the view when unrooted) - if (!isRooted()) + RenderView* view; + if (!isRooted(&view)) return; - const RenderView& view = this->view(); - if (view.printing()) - return; + if (view->printing()) + return; // Don't repaint if we're printing. 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(); - repaintUsingContainer(repaintContainer, computeRectForRepaint(dirtyRect, repaintContainer), shouldClipToLayer); -} - -void RenderObject::repaintSlowRepaintObject() const -{ - // 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(); - if (!repaintContainer) - repaintContainer = &view; - - 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); + computeRectForRepaint(repaintContainer, dirtyRect); + repaintUsingContainer(repaintContainer ? repaintContainer : view, pixelSnappedIntRect(dirtyRect), immediate, shouldClipToLayer); } IntRect RenderObject::pixelSnappedAbsoluteClippedOverflowRect() const { - return snappedIntRect(absoluteClippedOverflowRect()); + return pixelSnappedIntRect(absoluteClippedOverflowRect()); } bool RenderObject::checkForRepaintDuringLayout() const @@ -1041,251 +1334,201 @@ LayoutRect RenderObject::clippedOverflowRectForRepaint(const RenderLayerModelObj return LayoutRect(); } -LayoutRect RenderObject::computeRectForRepaint(const LayoutRect& rect, const RenderLayerModelObject* repaintContainer, bool fixed) const +void RenderObject::computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect& rect, bool fixed) const { if (repaintContainer == this) - return rect; + return; - auto* parent = this->parent(); - if (!parent) - return rect; + if (auto o = parent()) { + if (o->isRenderBlockFlow()) { + RenderBlock* cb = toRenderBlock(o); + if (cb->hasColumns()) + cb->adjustRectForColumns(rect); + } - LayoutRect adjustedRect = rect; - if (parent->hasOverflowClip()) { - downcast<RenderBox>(*parent).applyCachedClipAndScrollOffsetForRepaint(adjustedRect); - if (adjustedRect.isEmpty()) - return adjustedRect; + if (o->hasOverflowClip()) { + RenderBox* boxParent = toRenderBox(o); + boxParent->applyCachedClipAndScrollOffsetForRepaint(rect); + if (rect.isEmpty()) + return; + } + + o->computeRectForRepaint(repaintContainer, rect, fixed); } - return parent->computeRectForRepaint(adjustedRect, repaintContainer, fixed); } -FloatRect RenderObject::computeFloatRectForRepaint(const FloatRect&, const RenderLayerModelObject*, bool) const +void RenderObject::computeFloatRectForRepaint(const RenderLayerModelObject*, FloatRect&, bool) const { ASSERT_NOT_REACHED(); - return FloatRect(); } -#if ENABLE(TREE_DEBUGGING) - -static void showRenderTreeLegend() -{ - fprintf(stderr, "\n(B)lock/(I)nline/I(N)line-block, (R)elative/A(B)solute/Fi(X)ed/Stick(Y) positioned, (O)verflow clipping, (A)nonymous, (G)enerated, (F)loating, has(L)ayer, (C)omposited, (D)irty layout, Dirty (S)tyle\n"); -} +#ifndef NDEBUG -void RenderObject::showNodeTreeForThis() const +void RenderObject::showTreeForThis() const { - if (!node()) - return; - node()->showTreeForThis(); + if (node()) + node()->showTreeForThis(); } void RenderObject::showRenderTreeForThis() const { - const WebCore::RenderObject* root = this; - while (root->parent()) - root = root->parent(); - showRenderTreeLegend(); - root->showRenderSubTreeAndMark(this, 1); + showRenderTree(this, 0); } void RenderObject::showLineTreeForThis() const { - if (!is<RenderBlockFlow>(*this)) - return; - showRenderTreeLegend(); - showRenderObject(false, 1); - downcast<RenderBlockFlow>(*this).showLineTreeAndMark(nullptr, 2); + if (containingBlock()) + containingBlock()->showLineTreeAndMark(0, 0, 0, 0, this); } -static const RenderFlowThread* flowThreadContainingBlockFromRenderer(const RenderObject* renderer) +void RenderObject::showRenderObject() const { - if (!renderer) - return nullptr; - - if (renderer->flowThreadState() == RenderObject::NotInsideFlowThread) - return nullptr; - - if (is<RenderFlowThread>(*renderer)) - return downcast<RenderFlowThread>(renderer); - - if (is<RenderBlock>(*renderer)) - return downcast<RenderBlock>(*renderer).cachedFlowThreadContainingBlock(); - - return nullptr; + showRenderObject(0); } -void RenderObject::showRegionsInformation() const +void RenderObject::showRenderObject(int printedCharacters) const { - 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()); + // As this function is intended to be used when debugging, the + // this pointer may be 0. + if (!this) { + fputs("(null)\n", stderr); + return; } - if (!ftcb) - return; + printedCharacters += fprintf(stderr, "%s %p", renderName(), this); - RenderRegion* startRegion = nullptr; - RenderRegion* endRegion = nullptr; - ftcb->getRegionRangeForBox(downcast<RenderBox>(this), startRegion, endRegion); - fprintf(stderr, " [Rs:%p Re:%p]", startRegion, endRegion); + if (node()) { + if (printedCharacters) + for (; printedCharacters < showTreeCharacterOffset; printedCharacters++) + fputc(' ', stderr); + fputc('\t', stderr); + node()->showNode(); + } else + fputc('\n', stderr); } -void RenderObject::showRenderObject(bool mark, int depth) const +void RenderObject::showRenderTreeAndMark(const RenderObject* markedObject1, const char* markedLabel1, const RenderObject* markedObject2, const char* markedLabel2, int depth) const { - 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('Y', stderr); - else if (isOutOfFlowPositioned()) { - if (style().position() == AbsolutePosition) - fputc('B', stderr); - else - fputc('X', stderr); - } - } else - fputc('-', stderr); - - if (hasOverflowClip()) - fputc('O', stderr); - else - fputc('-', stderr); - - if (isAnonymous()) - fputc('A', stderr); - else - fputc('-', stderr); - - if (isPseudoElement() || isAnonymous()) - fputc('G', stderr); - else - fputc('-', stderr); - - if (isFloating()) - fputc('F', stderr); - else - fputc('-', stderr); - - if (hasLayer()) - fputc('L', stderr); - else - fputc('-', stderr); - - if (isComposited()) - fputc('C', stderr); - else - fputc('-', stderr); - - fputc(' ', stderr); - - if (needsLayout()) - fputc('D', stderr); - else - fputc('-', stderr); - - if (node() && node()->needsStyleRecalc()) - fputc('S', stderr); - else - fputc('-', stderr); - int printedCharacters = 0; - if (mark) { - fprintf(stderr, "*"); - ++printedCharacters; - } - - while (++printedCharacters <= depth * 2) + 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); - if (node()) - fprintf(stderr, "%s ", node()->nodeName().utf8().data()); + showRenderObject(printedCharacters); + if (!this) + return; - 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()); + for (const RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) + child->showRenderTreeAndMark(markedObject1, markedLabel1, markedObject2, markedLabel2, depth + 1); +} - if (is<RenderBox>(*this)) { - const auto& box = downcast<RenderBox>(*this); - fprintf(stderr, " (%.2f, %.2f) (%.2f, %.2f)", box.x().toFloat(), box.y().toFloat(), box.width().toFloat(), box.height().toFloat()); - } +#endif // NDEBUG - 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()); +Color RenderObject::selectionBackgroundColor() 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(); + else + color = frame().selection().isFocusedAndActive() ? theme().activeSelectionBackgroundColor() : theme().inactiveSelectionBackgroundColor(); } } - showRegionsInformation(); - fprintf(stderr, "\n"); + return color; } -void RenderObject::showRenderSubTreeAndMark(const RenderObject* markedObject, int depth) const +Color RenderObject::selectionColor(int colorProperty) const { - showRenderObject(markedObject == this, depth); - if (is<RenderBlockFlow>(*this)) - downcast<RenderBlockFlow>(*this).showLineTreeAndMark(nullptr, depth + 1); + 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; - for (const RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) - child->showRenderSubTreeAndMark(markedObject, depth + 1); + 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(); + + return color; } -#endif // NDEBUG +Color RenderObject::selectionForegroundColor() const +{ + return selectionColor(CSSPropertyWebkitTextFillColor); +} -SelectionSubtreeRoot& RenderObject::selectionRoot() const +Color RenderObject::selectionEmphasisMarkColor() const { - RenderFlowThread* flowThread = flowThreadContainingBlock(); - if (!flowThread) - return view(); + return selectionColor(CSSPropertyWebkitTextEmphasisColor); +} - if (is<RenderNamedFlowThread>(*flowThread)) - return downcast<RenderNamedFlowThread>(*flowThread); - if (is<RenderMultiColumnFlowThread>(*flowThread)) { - if (!flowThread->containingBlock()) - return view(); - return flowThread->containingBlock()->selectionRoot(); +void RenderObject::selectionStartEnd(int& spos, int& epos) const +{ + view().selectionStartEnd(spos, epos); +} + +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); + } } - ASSERT_NOT_REACHED(); - return view(); } -void RenderObject::selectionStartEnd(int& spos, int& epos) const +void RenderObject::removeAnonymousWrappersForInlinesIfNecessary() { - selectionRoot().selectionData().selectionStartEndPositions(spos, epos); + RenderBlock* parentBlock = toRenderBlock(parent()); + if (!parentBlock->canCollapseAnonymousBlockChild()) + return; + + // 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(); + + if (curr) + return; + + curr = parent()->firstChild(); + while (curr) { + RenderObject* next = curr->nextSibling(); + if (curr->isAnonymousBlock()) + parentBlock->collapseAnonymousBoxChild(parentBlock, toRenderBlock(curr)); + curr = next; + } } -FloatPoint RenderObject::localToAbsolute(const FloatPoint& localPoint, MapCoordinatesFlags mode, bool* wasFixed) const +FloatPoint RenderObject::localToAbsolute(const FloatPoint& localPoint, MapCoordinatesFlags mode) const { TransformState transformState(TransformState::ApplyTransformDirection, localPoint); - mapLocalToContainer(nullptr, transformState, mode | ApplyContainerFlip, wasFixed); + mapLocalToContainer(0, transformState, mode | ApplyContainerFlip); transformState.flatten(); return transformState.lastPlanarPoint(); @@ -1313,55 +1556,63 @@ void RenderObject::mapLocalToContainer(const RenderLayerModelObject* repaintCont if (repaintContainer == this) return; - auto* parent = this->parent(); - if (!parent) + auto o = parent(); + if (!o) return; // FIXME: this should call offsetFromContainer to share code, but I'm not sure it's ever called. - LayoutPoint centerPoint(transformState.mappedPoint()); - if (mode & ApplyContainerFlip && is<RenderBox>(*parent)) { - if (parent->style().isFlippedBlocksWritingMode()) - transformState.move(downcast<RenderBox>(parent)->flipForWritingMode(LayoutPoint(transformState.mappedPoint())) - centerPoint); + LayoutPoint centerPoint = roundedLayoutPoint(transformState.mappedPoint()); + if (mode & ApplyContainerFlip && o->isBox()) { + if (o->style().isFlippedBlocksWritingMode()) + transformState.move(toRenderBox(o)->flipForWritingModeIncludingColumns(roundedLayoutPoint(transformState.mappedPoint())) - centerPoint); mode &= ~ApplyContainerFlip; } - if (is<RenderBox>(*parent)) - transformState.move(-downcast<RenderBox>(*parent).scrolledContentOffset()); + LayoutSize columnOffset; + o->adjustForColumns(columnOffset, roundedLayoutPoint(transformState.mappedPoint())); + if (!columnOffset.isZero()) + transformState.move(columnOffset); - parent->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed); + if (o->hasOverflowClip()) + transformState.move(-toRenderBox(o)->scrolledContentOffset()); + + o->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 nullptr; + return 0; // FIXME: this should call offsetFromContainer to share code, but I'm not sure it's ever called. LayoutSize offset; - if (is<RenderBox>(*container)) - offset = -downcast<RenderBox>(*container).scrolledContentOffset(); + if (container->hasOverflowClip()) + offset = -toRenderBox(container)->scrolledContentOffset(); - geometryMap.push(this, offset, false); + geometryMap.push(this, offset, hasColumns()); return container; } void RenderObject::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const { - if (auto* parent = this->parent()) { - parent->mapAbsoluteToLocalPoint(mode, transformState); - if (is<RenderBox>(*parent)) - transformState.move(downcast<RenderBox>(*parent).scrolledContentOffset()); + auto o = parent(); + if (o) { + o->mapAbsoluteToLocalPoint(mode, transformState); + if (o->hasOverflowClip()) + transformState.move(toRenderBox(o)->scrolledContentOffset()); } } bool RenderObject::shouldUseTransformFromContainer(const RenderObject* containerObject) const { -#if ENABLE(3D_TRANSFORMS) - return hasTransform() || (containerObject && containerObject->style().hasPerspective()); +#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()); #else UNUSED_PARAM(containerObject); return hasTransform(); @@ -1373,14 +1624,14 @@ void RenderObject::getTransformFromContainer(const RenderObject* containerObject transform.makeIdentity(); transform.translate(offsetInContainer.width(), offsetInContainer.height()); RenderLayer* layer; - if (hasLayer() && (layer = downcast<RenderLayerModelObject>(*this).layer()) && layer->transform()) + if (hasLayer() && (layer = toRenderLayerModelObject(this)->layer()) && layer->transform()) transform.multiply(layer->currentTransform()); -#if ENABLE(3D_TRANSFORMS) +#if ENABLE(3D_RENDERING) 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 = downcast<RenderLayerModelObject>(*containerObject).layer()->perspectiveOrigin(); + FloatPoint perspectiveOrigin = toRenderLayerModelObject(containerObject)->layer()->perspectiveOrigin(); TransformationMatrix perspectiveMatrix; perspectiveMatrix.applyPerspective(containerObject->style().perspective()); @@ -1399,7 +1650,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, wasFixed); + mapLocalToContainer(repaintContainer, transformState, mode | ApplyContainerFlip | UseTransforms, wasFixed); transformState.flatten(); return transformState.lastPlanarQuad(); @@ -1408,42 +1659,45 @@ 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, wasFixed); + mapLocalToContainer(repaintContainer, transformState, mode | ApplyContainerFlip | UseTransforms, wasFixed); transformState.flatten(); return transformState.lastPlanarPoint(); } -LayoutSize RenderObject::offsetFromContainer(RenderElement& container, const LayoutPoint&, bool* offsetDependsOnPoint) const +LayoutSize RenderObject::offsetFromContainer(RenderObject* o, const LayoutPoint& point, bool* offsetDependsOnPoint) const { - ASSERT(&container == this->container()); + ASSERT(o == container()); LayoutSize offset; - if (is<RenderBox>(container)) - offset -= downcast<RenderBox>(container).scrolledContentOffset(); + + o->adjustForColumns(offset, point); + + if (o->hasOverflowClip()) + offset -= toRenderBox(o)->scrolledContentOffset(); if (offsetDependsOnPoint) - *offsetDependsOnPoint = is<RenderFlowThread>(container); + *offsetDependsOnPoint = hasColumns() || o->isRenderFlowThread(); return offset; } -LayoutSize RenderObject::offsetFromAncestorContainer(RenderElement& container) const +LayoutSize RenderObject::offsetFromAncestorContainer(RenderObject* container) const { LayoutSize offset; LayoutPoint referencePoint; const RenderObject* currContainer = this; do { - RenderElement* nextContainer = currContainer->container(); + auto 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; } @@ -1456,9 +1710,19 @@ LayoutRect RenderObject::localCaretRect(InlineBox*, int, LayoutUnit* extraWidthT return LayoutRect(); } -bool RenderObject::isRooted() const +bool RenderObject::isRooted(RenderView** view) const { - return isDescendantOf(&view()); + 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 @@ -1470,7 +1734,7 @@ RespectImageOrientationEnum RenderObject::shouldRespectImageOrientation() const #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() && is<HTMLImageElement>(node())) ? RespectImageOrientation : DoNotRespectImageOrientation; + return (frame().settings().shouldRespectImageOrientation() && node() && isHTMLImageElement(node())) ? RespectImageOrientation : DoNotRespectImageOrientation; } bool RenderObject::hasOutlineAnnotation() const @@ -1509,15 +1773,15 @@ RenderElement* RenderObject::container(const RenderLayerModelObject* repaintCont // 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 nullptr). + // 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. - // FIXME: share code with containingBlockForFixedPosition(). 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()) @@ -1532,12 +1796,11 @@ RenderElement* RenderObject::container(const RenderLayerModelObject* repaintCont // 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. - // FIXME: share code with isContainingBlockCandidateForAbsolutelyPositionedObject(). - // FIXME: hasTransformRelatedProperty() includes preserves3D() check, but this may need to change: https://www.w3.org/Bugs/Public/show_bug.cgi?id=27566 - while (o && o->style().position() == StaticPosition && !o->isRenderView() && !(o->hasTransformRelatedProperty() && o->isRenderBlock())) { + 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; @@ -1551,11 +1814,21 @@ RenderElement* RenderObject::container(const RenderLayerModelObject* repaintCont bool RenderObject::isSelectionBorder() const { SelectionState st = selectionState(); - return st == SelectionStart - || st == SelectionEnd - || st == SelectionBoth - || view().selectionUnsplitStart() == this - || view().selectionUnsplitEnd() == this; + 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(); + } } void RenderObject::willBeDestroyed() @@ -1567,21 +1840,41 @@ void RenderObject::willBeDestroyed() removeFromParent(); - ASSERT(documentBeingDestroyed() || !is<RenderElement>(*this) || !view().frameView().hasSlowRepaintObject(downcast<RenderElement>(*this))); + ASSERT(documentBeingDestroyed() || !isRenderElement() || !view().frameView().hasSlowRepaintObject(toRenderElement(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. if (hasLayer()) { setHasLayer(false); - downcast<RenderLayerModelObject>(*this).destroyLayer(); + toRenderLayerModelObject(this)->destroyLayer(); } - removeRareData(); + clearLayoutRootIfNeeded(); } void RenderObject::insertedIntoTree() @@ -1589,10 +1882,10 @@ 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 (RenderFlowThread* flowThread = flowThreadContainingBlock()) - flowThread->flowThreadDescendantInserted(this); + if (RenderNamedFlowThread* containerFlowThread = parent()->renderNamedFlowThreadWrapper()) + containerFlowThread->addFlowChild(this); } void RenderObject::willBeRemovedFromTree() @@ -1601,109 +1894,38 @@ void RenderObject::willBeRemovedFromTree() 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. + // 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. - removeFromRenderFlowThreadIncludingDescendants(true); + removeFromRenderFlowThreadRecursive(flowThreadContainingBlock()); } -void RenderObject::removeFromRenderFlowThreadIncludingDescendants(bool shouldUpdateState) +void RenderObject::removeFromRenderFlowThreadRecursive(RenderFlowThread* renderFlowThread) { - // Once we reach another flow thread we don't need to update the flow thread state - // but we have to continue cleanup the flow thread info. - if (isRenderFlowThread()) - shouldUpdateState = false; - for (RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) - child->removeFromRenderFlowThreadIncludingDescendants(shouldUpdateState); + child->removeFromRenderFlowThreadRecursive(renderFlowThread); - // We have to ask for our containing flow thread as it may be above the removed sub-tree. - RenderFlowThread* flowThreadContainingBlock = this->flowThreadContainingBlock(); - while (flowThreadContainingBlock) { - flowThreadContainingBlock->removeFlowChildInfo(this); - if (flowThreadContainingBlock->flowThreadState() == NotInsideFlowThread) - break; - RenderObject* parent = flowThreadContainingBlock->parent(); - if (!parent) - break; - flowThreadContainingBlock = parent->flowThreadContainingBlock(); - } - if (is<RenderBlock>(*this)) - downcast<RenderBlock>(*this).setCachedFlowThreadContainingBlockNeedsUpdate(); - - if (shouldUpdateState) - setFlowThreadState(NotInsideFlowThread); -} - -void RenderObject::invalidateFlowThreadContainingBlockIncludingDescendants(RenderFlowThread* flowThread) -{ - if (flowThreadState() == NotInsideFlowThread) - return; - - if (is<RenderBlock>(*this)) { - RenderBlock& block = downcast<RenderBlock>(*this); - - if (block.cachedFlowThreadContainingBlockNeedsUpdate()) - return; - - flowThread = block.cachedFlowThreadContainingBlock(); - block.setCachedFlowThreadContainingBlockNeedsUpdate(); - } - - if (flowThread) - flowThread->removeFlowChildInfo(this); - - for (RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) - child->invalidateFlowThreadContainingBlockIncludingDescendants(flowThread); -} - -static void collapseAnonymousTableRowsIfNeeded(const RenderObject& rendererToBeDestroyed) -{ - if (!is<RenderTableRow>(rendererToBeDestroyed)) - return; - - auto& rowToBeDestroyed = downcast<RenderTableRow>(rendererToBeDestroyed); - auto* section = downcast<RenderTableSection>(rowToBeDestroyed.parent()); - if (!section) - return; - - // All siblings generated? - for (auto* current = section->firstRow(); current; current = current->nextRow()) { - if (current == &rendererToBeDestroyed) - continue; - if (!current->isAnonymous()) - return; - } - - RenderTableRow* rowToInsertInto = nullptr; - auto* currentRow = section->firstRow(); - while (currentRow) { - if (currentRow == &rendererToBeDestroyed) { - currentRow = currentRow->nextRow(); - continue; - } - if (!rowToInsertInto) { - rowToInsertInto = currentRow; - currentRow = currentRow->nextRow(); - continue; - } - currentRow->moveAllChildrenTo(rowToInsertInto); - auto* destroyThis = currentRow; - currentRow = currentRow->nextRow(); - destroyThis->destroy(); - } - if (rowToInsertInto) - rowToInsertInto->setNeedsLayout(); + 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() @@ -1714,41 +1936,34 @@ void RenderObject::destroyAndCleanupAnonymousWrappers() return; } - auto* destroyRoot = this; - auto* destroyRootParent = destroyRoot->parent(); - while (destroyRootParent && destroyRootParent->isAnonymous()) { - if (!destroyRootParent->isTableCell() && !destroyRootParent->isTableRow() - && !destroyRootParent->isTableCaption() && !destroyRootParent->isTableSection() && !destroyRootParent->isTable()) + 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()) break; - // single child? - if (!(destroyRootParent->firstChild() == destroyRoot && destroyRootParent->lastChild() == destroyRoot)) + + if (destroyRootParent->firstChild() != this || destroyRootParent->lastChild() != this) break; - destroyRoot = destroyRootParent; - destroyRootParent = destroyRootParent->parent(); } - collapseAnonymousTableRowsIfNeeded(*destroyRoot); + destroyRoot->destroy(); + // WARNING: |this| is deleted here. } void RenderObject::destroy() { - m_bitfields.setBeingDestroyed(true); - #if PLATFORM(IOS) if (hasLayer()) - downcast<RenderBoxModelObject>(*this).layer()->willBeDestroyed(); + toRenderBoxModelObject(this)->layer()->willBeDestroyed(); #endif willBeDestroyed(); - if (is<RenderWidget>(*this)) { - downcast<RenderWidget>(*this).deref(); - return; - } delete this; } -VisiblePosition RenderObject::positionForPoint(const LayoutPoint&, const RenderRegion*) +VisiblePosition RenderObject::positionForPoint(const LayoutPoint&) { return createVisiblePosition(caretMinOffset(), DOWNSTREAM); } @@ -1757,7 +1972,7 @@ void RenderObject::updateDragState(bool dragOn) { bool valueChanged = (dragOn != isDragging()); setIsDragging(dragOn); - if (valueChanged && node() && (style().affectedByDrag() || (is<Element>(*node()) && downcast<Element>(*node()).childrenAffectedByDrag()))) + if (valueChanged && node() && (style().affectedByDrag() || (node()->isElementNode() && toElement(node())->childrenAffectedByDrag()))) node()->setNeedsStyleRecalc(); for (RenderObject* curr = firstChildSlow(); curr; curr = curr->nextSibling()) curr->updateDragState(dragOn); @@ -1765,12 +1980,7 @@ void RenderObject::updateDragState(bool dragOn) bool RenderObject::isComposited() const { - 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()); + return hasLayer() && toRenderLayerModelObject(this)->layer()->isComposited(); } bool RenderObject::hitTest(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestFilter hitTestFilter) @@ -1806,7 +2016,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(); } @@ -1828,6 +2038,48 @@ 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; @@ -1846,58 +2098,49 @@ static Color decorationColor(RenderStyle* style) return result; } -void RenderObject::getTextDecorationColorsAndStyles(int decorations, Color& underlineColor, Color& overlineColor, Color& linethroughColor, - TextDecorationStyle& underlineStyle, TextDecorationStyle& overlineStyle, TextDecorationStyle& linethroughStyle, bool firstlineStyle) const +void RenderObject::getTextDecorationColors(int decorations, Color& underline, Color& overline, + Color& linethrough, bool quirksMode, bool firstlineStyle) { - const RenderObject* current = this; - RenderStyle* styleToUse = nullptr; + RenderObject* curr = this; + RenderStyle* styleToUse = 0; TextDecoration currDecs = TextDecorationNone; Color resultColor; do { - styleToUse = firstlineStyle ? ¤t->firstLineStyle() : ¤t->style(); + 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; - underlineColor = resultColor; - underlineStyle = styleToUse->textDecorationStyle(); + underline = resultColor; } if (currDecs & TextDecorationOverline) { decorations &= ~TextDecorationOverline; - overlineColor = resultColor; - overlineStyle = styleToUse->textDecorationStyle(); + overline = resultColor; } if (currDecs & TextDecorationLineThrough) { decorations &= ~TextDecorationLineThrough; - linethroughColor = resultColor; - linethroughStyle = styleToUse->textDecorationStyle(); + linethrough = resultColor; } } - if (current->isRubyText()) + if (curr->isRubyText()) return; - current = current->parent(); - if (current && current->isAnonymousBlock() && downcast<RenderBlock>(*current).continuation()) - current = downcast<RenderBlock>(*current).continuation(); - } while (current && decorations && (!current->node() || (!is<HTMLAnchorElement>(*current->node()) && !current->node()->hasTagName(fontTag)))); + 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 && current) { - styleToUse = firstlineStyle ? ¤t->firstLineStyle() : ¤t->style(); + if (decorations && curr) { + styleToUse = firstlineStyle ? &curr->firstLineStyle() : &curr->style(); resultColor = decorationColor(styleToUse); - if (decorations & TextDecorationUnderline) { - underlineColor = resultColor; - underlineStyle = styleToUse->textDecorationStyle(); - } - if (decorations & TextDecorationOverline) { - overlineColor = resultColor; - overlineStyle = styleToUse->textDecorationStyle(); - } - if (decorations & TextDecorationLineThrough) { - linethroughColor = resultColor; - linethroughStyle = styleToUse->textDecorationStyle(); - } + if (decorations & TextDecorationUnderline) + underline = resultColor; + if (decorations & TextDecorationOverline) + overline = resultColor; + if (decorations & TextDecorationLineThrough) + linethrough = resultColor; } } @@ -1905,16 +2148,19 @@ void RenderObject::getTextDecorationColorsAndStyles(int decorations, Color& unde void RenderObject::addAnnotatedRegions(Vector<AnnotatedRegionValue>& regions) { // Convert the style regions to absolute coordinates. - if (style().visibility() != VISIBLE || !is<RenderBox>(*this)) + if (style().visibility() != VISIBLE || !isBox()) return; - auto& box = downcast<RenderBox>(*this); + RenderBox* box = toRenderBox(this); FloatPoint absPos = localToAbsolute(); const Vector<StyleDashboardRegion>& styleRegions = style().dashboardRegions(); - for (const auto& styleRegion : styleRegions) { - LayoutUnit w = box.width(); - LayoutUnit h = box.height(); + unsigned i, count = styleRegions.size(); + for (i = 0; i < count; i++) { + StyleDashboardRegion styleRegion = styleRegions[i]; + + LayoutUnit w = box->width(); + LayoutUnit h = box->height(); AnnotatedRegionValue region; region.label = styleRegion.label; @@ -1942,15 +2188,42 @@ 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 (is<RenderText>(*this)) + if (isText()) return; addAnnotatedRegions(regions); - for (RenderObject* current = downcast<RenderElement>(*this).firstChild(); current; current = current->nextSibling()) - current->collectAnnotatedRegions(regions); + for (RenderObject* curr = toRenderElement(this)->firstChild(); curr; curr = curr->nextSibling()) + curr->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; @@ -1959,7 +2232,7 @@ int RenderObject::caretMinOffset() const int RenderObject::caretMaxOffset() const { if (isReplaced()) - return node() ? std::max(1U, node()->countChildNodes()) : 1; + return node() ? std::max(1U, node()->childNodeCount()) : 1; if (isHR()) return 1; return 0; @@ -1982,14 +2255,20 @@ int RenderObject::nextOffset(int current) const void RenderObject::adjustRectForOutlineAndShadow(LayoutRect& rect) const { - LayoutUnit outlineSize = outlineStyleForRepaint().outlineSize(); + int 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); @@ -2001,8 +2280,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 (isDocumentElementRenderer() || isBody() || (isOutOfFlowPositioned() && style().position() == FixedPosition)) - return nullptr; + if (isRoot() || isBody() || (isOutOfFlowPositioned() && style().position() == FixedPosition)) + return 0; // 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. @@ -2018,26 +2297,24 @@ RenderBoxModelObject* RenderObject::offsetParent() const bool skipTables = isPositioned(); float currZoom = style().effectiveZoom(); - 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))) + 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))) break; - float newZoom = current->style().effectiveZoom(); + float newZoom = curr->style().effectiveZoom(); if (currZoom != newZoom) break; currZoom = newZoom; - current = current->parent(); + curr = curr->parent(); } // CSS regions specification says that region flows should return the body element as their offsetParent. - if (is<RenderNamedFlowThread>(current)) { - auto* body = document().bodyOrFrameset(); - current = body ? body->renderer() : nullptr; - } + if (curr && curr->isRenderNamedFlowThread()) + curr = document().body() ? document().body()->renderer() : 0; - return is<RenderBoxModelObject>(current) ? downcast<RenderBoxModelObject>(current) : nullptr; + return curr && curr->isBoxModelObject() ? toRenderBoxModelObject(curr) : 0; } VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affinity) const @@ -2128,7 +2405,20 @@ bool RenderObject::canHaveGeneratedChildren() const Node* RenderObject::generatingPseudoHostElement() const { - return downcast<PseudoElement>(*node()).hostElement(); + return toPseudoElement(node())->hostElement(); +} + +bool RenderObject::canBeReplacedWithInlineRunIn() const +{ + return true; +} + +#if ENABLE(SVG) + +RenderSVGResourceContainer* RenderObject::toRenderSVGResourceContainer() +{ + ASSERT_NOT_REACHED(); + return 0; } void RenderObject::setNeedsBoundariesUpdate() @@ -2175,144 +2465,37 @@ bool RenderObject::nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const return false; } -RenderNamedFlowFragment* RenderObject::currentRenderNamedFlowFragment() const -{ - RenderFlowThread* flowThread = flowThreadContainingBlock(); - if (!is<RenderNamedFlowThread>(flowThread)) - return nullptr; - - // 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()); - - return downcast<RenderNamedFlowFragment>(flowThread->currentRegion()); -} - -RenderFlowThread* RenderObject::locateFlowThreadContainingBlock() const -{ - RenderBlock* containingBlock = this->containingBlock(); - return containingBlock ? containingBlock->flowThreadContainingBlock() : nullptr; -} - -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 }; +#endif // ENABLE(SVG) - 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); -} +} // namespace WebCore -RenderObject::RareDataHash& RenderObject::rareDataMap() -{ - static NeverDestroyed<RareDataHash> map; - return map; -} +#ifndef NDEBUG -RenderObject::RenderObjectRareData RenderObject::rareData() const +void showTree(const WebCore::RenderObject* object) { - if (!hasRareData()) - return RenderObjectRareData(); - - return rareDataMap().get(this); + if (object) + object->showTreeForThis(); } -RenderObject::RenderObjectRareData& RenderObject::ensureRareData() +void showLineTree(const WebCore::RenderObject* object) { - setHasRareData(true); - return rareDataMap().add(this, RenderObjectRareData()).iterator->value; + if (object) + object->showLineTreeForThis(); } -void RenderObject::removeRareData() +void showRenderTree(const WebCore::RenderObject* object1) { - rareDataMap().remove(this); - setHasRareData(false); + showRenderTree(object1, 0); } -#ifndef NDEBUG -void printRenderTreeForLiveDocuments() +void showRenderTree(const WebCore::RenderObject* object1, const WebCore::RenderObject* object2) { - for (const auto* document : Document::allDocuments()) { - if (!document->renderView() || document->inPageCache()) - continue; - if (document->frame() && document->frame()->isMainFrame()) - fprintf(stderr, "----------------------main frame--------------------------\n"); - fprintf(stderr, "%s", document->url().string().utf8().data()); - showRenderTree(document->renderView()); + if (object1) { + const WebCore::RenderObject* root = object1; + while (root->parent()) + root = root->parent(); + root->showRenderTreeAndMark(object1, "*", object2, "-", 0); } } -#endif -} // 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 |