diff options
author | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-05-30 12:48:17 +0200 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-05-30 12:48:17 +0200 |
commit | 881da28418d380042aa95a97f0cbd42560a64f7c (patch) | |
tree | a794dff3274695e99c651902dde93d934ea7a5af /Source/WebCore/rendering/RenderObject.cpp | |
parent | 7e104c57a70fdf551bb3d22a5d637cdcbc69dbea (diff) | |
parent | 0fcedcd17cc00d3dd44c718b3cb36c1033319671 (diff) | |
download | qtwebkit-881da28418d380042aa95a97f0cbd42560a64f7c.tar.gz |
Merge 'wip/next' into dev
Change-Id: Iff9ee5e23bb326c4371ec8ed81d56f2f05d680e9
Diffstat (limited to 'Source/WebCore/rendering/RenderObject.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderObject.cpp | 3011 |
1 files changed, 1017 insertions, 1994 deletions
diff --git a/Source/WebCore/rendering/RenderObject.cpp b/Source/WebCore/rendering/RenderObject.cpp index b93764828..df2cd277d 100644 --- a/Source/WebCore/rendering/RenderObject.cpp +++ b/Source/WebCore/rendering/RenderObject.cpp @@ -3,7 +3,7 @@ * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2011, 2013 Apple Inc. All rights reserved. * Copyright (C) 2009 Google Inc. All rights reserved. * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * @@ -29,72 +29,62 @@ #include "AXObjectCache.h" #include "AnimationController.h" -#include "ContentData.h" -#include "CursorList.h" #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 "RenderArena.h" +#include "PseudoElement.h" #include "RenderCounter.h" -#include "RenderDeprecatedFlexibleBox.h" -#include "RenderFlexibleBox.h" +#include "RenderFlowThread.h" #include "RenderGeometryMap.h" -#include "RenderGrid.h" -#include "RenderImage.h" -#include "RenderImageResourceStyleImage.h" #include "RenderInline.h" +#include "RenderIterator.h" #include "RenderLayer.h" #include "RenderLayerBacking.h" -#include "RenderListItem.h" -#include "RenderMultiColumnBlock.h" -#include "RenderNamedFlowThread.h" -#include "RenderRegion.h" +#include "RenderMultiColumnFlowThread.h" +#include "RenderNamedFlowFragment.h" +#include "RenderNamedFlowThread.h" #include "RenderRuby.h" -#include "RenderRubyText.h" +#include "RenderSVGResourceContainer.h" #include "RenderScrollbarPart.h" -#include "RenderTableCaption.h" -#include "RenderTableCell.h" -#include "RenderTableCol.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> -#include <wtf/StackStats.h> - -#if USE(ACCELERATED_COMPOSITING) -#include "RenderLayerCompositor.h" -#endif -#if ENABLE(SVG) -#include "RenderSVGResourceContainer.h" -#include "SVGRenderSupport.h" +#if PLATFORM(IOS) +#include "SelectionRect.h" #endif -using namespace std; - namespace WebCore { using namespace HTMLNames; #ifndef NDEBUG -static void* baseOfRenderObjectBeingDeleted; +void printRenderTreeForLiveDocuments(); RenderObject::SetLayoutNeededForbiddenScope::SetLayoutNeededForbiddenScope(RenderObject* renderObject, bool isForbidden) : m_renderObject(renderObject) @@ -111,7 +101,7 @@ RenderObject::SetLayoutNeededForbiddenScope::~SetLayoutNeededForbiddenScope() struct SameSizeAsRenderObject { virtual ~SameSizeAsRenderObject() { } // Allocate vtable pointer. - void* pointers[5]; + void* pointers[4]; #ifndef NDEBUG unsigned m_debugBitfields : 2; #endif @@ -120,179 +110,56 @@ struct SameSizeAsRenderObject { COMPILE_ASSERT(sizeof(RenderObject) == sizeof(SameSizeAsRenderObject), RenderObject_should_stay_small); -// On low-powered/mobile devices, preventing blitting on a scroll can cause noticeable delays -// when scrolling a page with a fixed background image. As an optimization, assuming there are -// no fixed positoned elements on the page, we can acclerate scrolling (via blitting) if we -// ignore the CSS property "background-attachment: fixed". -static bool shouldRepaintFixedBackgroundsOnScroll(FrameView* frameView) -{ -#if !ENABLE(FAST_MOBILE_SCROLLING) && !PLATFORM(QT) - UNUSED_PARAM(frameView); -#endif - - bool repaintFixedBackgroundsOnScroll = true; -#if ENABLE(FAST_MOBILE_SCROLLING) -#if PLATFORM(QT) - if (frameView->delegatesScrolling()) - repaintFixedBackgroundsOnScroll = false; -#else - repaintFixedBackgroundsOnScroll = false; -#endif -#endif - return repaintFixedBackgroundsOnScroll; -} - -bool RenderObject::s_affectsParentBlock = false; -bool RenderObject::s_noLongerAffectsParentBlock = false; - -RenderObjectAncestorLineboxDirtySet* RenderObject::s_ancestorLineboxDirtySet = 0; - -void* RenderObject::operator new(size_t sz, RenderArena* renderArena) -{ - return renderArena->allocate(sz); -} - -void RenderObject::operator delete(void* ptr, size_t sz) -{ - ASSERT(baseOfRenderObjectBeingDeleted == ptr); - - // Stash size where destroy can find it. - *(size_t *)ptr = sz; -} - -RenderObject* RenderObject::createObject(Element* element, RenderStyle* style) -{ - Document* doc = element->document(); - RenderArena* arena = doc->renderArena(); - - // Minimal support for content properties replacing an entire element. - // Works only if we have exactly one piece of content and it's a URL. - // Otherwise acts as if we didn't support this feature. - const ContentData* contentData = style->contentData(); - if (contentData && !contentData->next() && contentData->isImage() && !element->isPseudoElement()) { - RenderImage* image = new (arena) RenderImage(element); - // RenderImageResourceStyleImage requires a style being present on the image but we don't want to - // trigger a style change now as the node is not fully attached. Moving this code to style change - // doesn't make sense as it should be run once at renderer creation. - image->setStyleInternal(style); - if (const StyleImage* styleImage = static_cast<const ImageContentData*>(contentData)->image()) { - image->setImageResource(RenderImageResourceStyleImage::create(const_cast<StyleImage*>(styleImage))); - image->setIsGeneratedContent(); - } else - image->setImageResource(RenderImageResource::create()); - image->setStyleInternal(0); - return image; - } - - if (element->hasTagName(rubyTag)) { - if (style->display() == INLINE) - return new (arena) RenderRubyAsInline(element); - else if (style->display() == BLOCK) - return new (arena) RenderRubyAsBlock(element); - } - // treat <rt> as ruby text ONLY if it still has its default treatment of block - if (element->hasTagName(rtTag) && style->display() == BLOCK) - return new (arena) RenderRubyText(element); - if (doc->cssRegionsEnabled() && style->isDisplayRegionType() && !style->regionThread().isEmpty() && doc->renderView()) - return new (arena) RenderRegion(element, 0); - switch (style->display()) { - case NONE: - return 0; - case INLINE: - return new (arena) RenderInline(element); - case BLOCK: - case INLINE_BLOCK: - case RUN_IN: - case COMPACT: - if ((!style->hasAutoColumnCount() || !style->hasAutoColumnWidth()) && doc->regionBasedColumnsEnabled()) - return new (arena) RenderMultiColumnBlock(element); - return new (arena) RenderBlock(element); - case LIST_ITEM: - return new (arena) RenderListItem(element); - case TABLE: - case INLINE_TABLE: - return new (arena) RenderTable(element); - case TABLE_ROW_GROUP: - case TABLE_HEADER_GROUP: - case TABLE_FOOTER_GROUP: - return new (arena) RenderTableSection(element); - case TABLE_ROW: - return new (arena) RenderTableRow(element); - case TABLE_COLUMN_GROUP: - case TABLE_COLUMN: - return new (arena) RenderTableCol(element); - case TABLE_CELL: - return new (arena) RenderTableCell(element); - case TABLE_CAPTION: - return new (arena) RenderTableCaption(element); - case BOX: - case INLINE_BOX: - return new (arena) RenderDeprecatedFlexibleBox(element); - case FLEX: - case INLINE_FLEX: - return new (arena) RenderFlexibleBox(element); - case GRID: - case INLINE_GRID: - return new (arena) RenderGrid(element); - } - - return 0; -} - DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, renderObjectCounter, ("RenderObject")); -RenderObject::RenderObject(Node* node) +RenderObject::RenderObject(Node& node) : CachedImageClient() - , m_style(0) , m_node(node) - , m_parent(0) - , m_previous(0) - , m_next(0) + , m_parent(nullptr) + , m_previous(nullptr) + , m_next(nullptr) #ifndef NDEBUG , m_hasAXObject(false) , m_setNeedsLayoutForbidden(false) #endif , m_bitfields(node) { + if (RenderView* renderView = node.document().renderView()) + renderView->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()); } -RenderTheme* RenderObject::theme() const +RenderTheme& RenderObject::theme() const { - ASSERT(document()->page()); - - return document()->page()->theme(); + ASSERT(document().page()); + return document().page()->theme(); } -bool RenderObject::isDescendantOf(const RenderObject* obj) const +bool RenderObject::isDescendantOf(const RenderObject* ancestor) const { - for (const RenderObject* r = this; r; r = r->m_parent) { - if (r == obj) + for (const RenderObject* renderer = this; renderer; renderer = renderer->m_parent) { + if (renderer == ancestor) return true; } return false; } -bool RenderObject::isBody() const -{ - return node() && node()->hasTagName(bodyTag); -} - -bool RenderObject::isHR() const -{ - return node() && node()->hasTagName(hrTag); -} - bool RenderObject::isLegend() const { return node() && node()->hasTagName(legendTag); @@ -307,7 +174,7 @@ void RenderObject::setFlowThreadStateIncludingDescendants(FlowThreadState state) { setFlowThreadState(state); - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + for (RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) { // If the child is a fragmentation context it already updated the descendants flag accordingly. if (child->isRenderFlowThread()) continue; @@ -316,73 +183,27 @@ void RenderObject::setFlowThreadStateIncludingDescendants(FlowThreadState state) } } -void RenderObject::addChild(RenderObject* newChild, RenderObject* beforeChild) +void RenderObject::setParent(RenderElement* parent) { - RenderObjectChildList* children = virtualChildren(); - ASSERT(children); - if (!children) - return; + m_parent = parent; - bool needsTable = false; - - if (newChild->isRenderTableCol()) { - RenderTableCol* newTableColumn = toRenderTableCol(newChild); - bool isColumnInColumnGroup = newTableColumn->isTableColumn() && isRenderTableCol(); - needsTable = !isTable() && !isColumnInColumnGroup; - } else if (newChild->isTableCaption()) - needsTable = !isTable(); - else if (newChild->isTableSection()) - needsTable = !isTable(); - else if (newChild->isTableRow()) - needsTable = !isTableSection(); - else if (newChild->isTableCell()) - needsTable = !isTableRow(); - - if (needsTable) { - RenderTable* table; - RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : children->lastChild(); - if (afterChild && afterChild->isAnonymous() && afterChild->isTable() && !afterChild->isBeforeContent()) - table = toRenderTable(afterChild); - else { - table = RenderTable::createAnonymousWithParentRenderer(this); - addChild(table, beforeChild); - } - table->addChild(newChild); - } else - children->insertChildNode(this, newChild, beforeChild); - - if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) - toRenderText(newChild)->transformText(); - - // SVG creates renderers for <g display="none">, as SVG requires children of hidden - // <g>s to have renderers - at least that's how our implementation works. Consider: - // <g display="none"><foreignObject><body style="position: relative">FOO... - // - requiresLayer() would return true for the <body>, creating a new RenderLayer - // - when the document is painted, both layers are painted. The <body> layer doesn't - // know that it's inside a "hidden SVG subtree", and thus paints, even if it shouldn't. - // To avoid the problem alltogether, detect early if we're inside a hidden SVG subtree - // and stop creating layers at all for these cases - they're not used anyways. - if (newChild->hasLayer() && !layerCreationAllowedForSubtree()) - toRenderLayerModelObject(newChild)->layer()->removeOnlyThisLayer(); - -#if ENABLE(SVG) - SVGRenderSupport::childAdded(this, newChild); -#endif + // Only update if our flow thread state is different from our new parent and if we're not a RenderFlowThread. + // A RenderFlowThread is always considered to be inside itself, so it never has to change its state + // in response to parent changes. + FlowThreadState newState = parent ? parent->flowThreadState() : NotInsideFlowThread; + if (newState != flowThreadState() && !isRenderFlowThread()) + setFlowThreadStateIncludingDescendants(newState); } -void RenderObject::removeChild(RenderObject* oldChild) +void RenderObject::removeFromParent() { - RenderObjectChildList* children = virtualChildren(); - ASSERT(children); - if (!children) - return; - - children->removeChildNode(this, oldChild); + if (parent()) + parent()->removeChild(*this); } RenderObject* RenderObject::nextInPreOrder() const { - if (RenderObject* o = firstChild()) + if (RenderObject* o = firstChildSlow()) return o; return nextInPreOrderAfterChildren(); @@ -404,7 +225,7 @@ RenderObject* RenderObject::nextInPreOrderAfterChildren() const RenderObject* RenderObject::nextInPreOrder(const RenderObject* stayWithin) const { - if (RenderObject* o = firstChild()) + if (RenderObject* o = firstChildSlow()) return o; return nextInPreOrderAfterChildren(stayWithin); @@ -413,14 +234,14 @@ RenderObject* RenderObject::nextInPreOrder(const RenderObject* stayWithin) const RenderObject* RenderObject::nextInPreOrderAfterChildren(const RenderObject* stayWithin) const { if (this == stayWithin) - return 0; + return nullptr; const RenderObject* current = this; RenderObject* next; while (!(next = current->nextSibling())) { current = current->parent(); if (!current || current == stayWithin) - return 0; + return nullptr; } return next; } @@ -428,8 +249,8 @@ RenderObject* RenderObject::nextInPreOrderAfterChildren(const RenderObject* stay RenderObject* RenderObject::previousInPreOrder() const { if (RenderObject* o = previousSibling()) { - while (o->lastChild()) - o = o->lastChild(); + while (RenderObject* last = o->lastChildSlow()) + o = last; return o; } @@ -439,14 +260,14 @@ RenderObject* RenderObject::previousInPreOrder() const RenderObject* RenderObject::previousInPreOrder(const RenderObject* stayWithin) const { if (this == stayWithin) - return 0; + return nullptr; return previousInPreOrder(); } RenderObject* RenderObject::childAt(unsigned index) const { - RenderObject* child = firstChild(); + RenderObject* child = firstChildSlow(); for (unsigned i = 0; child && i < index; i++) child = child->nextSibling(); return child; @@ -454,10 +275,10 @@ RenderObject* RenderObject::childAt(unsigned index) const RenderObject* RenderObject::firstLeafChild() const { - RenderObject* r = firstChild(); + RenderObject* r = firstChildSlow(); while (r) { - RenderObject* n = 0; - n = r->firstChild(); + RenderObject* n = nullptr; + n = r->firstChildSlow(); if (!n) break; r = n; @@ -467,10 +288,10 @@ RenderObject* RenderObject::firstLeafChild() const RenderObject* RenderObject::lastLeafChild() const { - RenderObject* r = lastChild(); + RenderObject* r = lastChildSlow(); while (r) { - RenderObject* n = 0; - n = r->lastChild(); + RenderObject* n = nullptr; + n = r->lastChildSlow(); if (!n) break; r = n; @@ -478,113 +299,198 @@ RenderObject* RenderObject::lastLeafChild() const return r; } -static void addLayers(RenderObject* obj, RenderLayer* parentLayer, RenderObject*& newObject, - RenderLayer*& beforeChild) +#if ENABLE(IOS_TEXT_AUTOSIZING) +// Inspired by Node::traverseNextNode. +RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin) const { - if (obj->hasLayer()) { - if (!beforeChild && newObject) { - // We need to figure out the layer that follows newObject. We only do - // this the first time we find a child layer, and then we update the - // pointer values for newObject and beforeChild used by everyone else. - beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject); - newObject = 0; + RenderObject* child = firstChildSlow(); + if (child) { + ASSERT(!stayWithin || child->isDescendantOf(stayWithin)); + return child; + } + if (this == stayWithin) + return nullptr; + if (nextSibling()) { + ASSERT(!stayWithin || nextSibling()->isDescendantOf(stayWithin)); + return nextSibling(); + } + const RenderObject* n = this; + while (n && !n->nextSibling() && (!stayWithin || n->parent() != stayWithin)) + n = n->parent(); + if (n) { + ASSERT(!stayWithin || !n->nextSibling() || n->nextSibling()->isDescendantOf(stayWithin)); + return n->nextSibling(); + } + return nullptr; +} + +// Non-recursive version of the DFS search. +RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, HeightTypeTraverseNextInclusionFunction inclusionFunction, int& currentDepth, int& newFixedDepth) const +{ + BlockContentHeightType overflowType; + + // Check for suitable children. + for (RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) { + overflowType = inclusionFunction(child); + if (overflowType != FixedHeight) { + currentDepth++; + if (overflowType == OverflowHeight) + newFixedDepth = currentDepth; + ASSERT(!stayWithin || child->isDescendantOf(stayWithin)); + return child; } - parentLayer->addChild(toRenderLayerModelObject(obj)->layer(), beforeChild); - return; } - for (RenderObject* curr = obj->firstChild(); curr; curr = curr->nextSibling()) - addLayers(curr, parentLayer, newObject, beforeChild); + if (this == stayWithin) + return nullptr; + + // Now we traverse other nodes if they exist, otherwise + // we go to the parent node and try doing the same. + const RenderObject* n = this; + while (n) { + while (n && !n->nextSibling() && (!stayWithin || n->parent() != stayWithin)) { + n = n->parent(); + currentDepth--; + } + if (!n) + return nullptr; + for (RenderObject* sibling = n->nextSibling(); sibling; sibling = sibling->nextSibling()) { + overflowType = inclusionFunction(sibling); + if (overflowType != FixedHeight) { + if (overflowType == OverflowHeight) + newFixedDepth = currentDepth; + ASSERT(!stayWithin || !n->nextSibling() || n->nextSibling()->isDescendantOf(stayWithin)); + return sibling; + } + } + if (!stayWithin || n->parent() != stayWithin) { + n = n->parent(); + currentDepth--; + } else + return nullptr; + } + return nullptr; } -void RenderObject::addLayers(RenderLayer* parentLayer) +RenderObject* RenderObject::traverseNext(const RenderObject* stayWithin, TraverseNextInclusionFunction inclusionFunction) const { - if (!parentLayer) - return; + for (RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) { + if (inclusionFunction(child)) { + ASSERT(!stayWithin || child->isDescendantOf(stayWithin)); + return child; + } + } - RenderObject* object = this; - RenderLayer* beforeChild = 0; - WebCore::addLayers(this, parentLayer, object, beforeChild); -} + if (this == stayWithin) + return nullptr; -void RenderObject::removeLayers(RenderLayer* parentLayer) -{ - if (!parentLayer) - return; + for (RenderObject* sibling = nextSibling(); sibling; sibling = sibling->nextSibling()) { + if (inclusionFunction(sibling)) { + ASSERT(!stayWithin || sibling->isDescendantOf(stayWithin)); + return sibling; + } + } - if (hasLayer()) { - parentLayer->removeChild(toRenderLayerModelObject(this)->layer()); - return; + const RenderObject* n = this; + while (n) { + while (n && !n->nextSibling() && (!stayWithin || n->parent() != stayWithin)) + n = n->parent(); + if (n) { + for (RenderObject* sibling = n->nextSibling(); sibling; sibling = sibling->nextSibling()) { + if (inclusionFunction(sibling)) { + ASSERT(!stayWithin || !n->nextSibling() || n->nextSibling()->isDescendantOf(stayWithin)); + return sibling; + } + } + if ((!stayWithin || n->parent() != stayWithin)) + n = n->parent(); + else + return nullptr; + } } + return nullptr; +} - for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) - curr->removeLayers(parentLayer); +static RenderObject::BlockContentHeightType includeNonFixedHeight(const RenderObject* renderer) +{ + const RenderStyle& style = renderer->style(); + if (style.height().type() == Fixed) { + if (is<RenderBlock>(*renderer)) { + // 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()) + return RenderObject::OverflowHeight; + } + return RenderObject::FixedHeight; + } + return RenderObject::FlexibleHeight; } -void RenderObject::moveLayers(RenderLayer* oldParent, RenderLayer* newParent) + +void RenderObject::adjustComputedFontSizesOnBlocks(float size, float visibleWidth) { - if (!newParent) + Document* document = view().frameView().frame().document(); + if (!document) return; - if (hasLayer()) { - RenderLayer* layer = toRenderLayerModelObject(this)->layer(); - ASSERT(oldParent == layer->parent()); - if (oldParent) - oldParent->removeChild(layer); - newParent->addChild(layer); - return; + Vector<int> depthStack; + int currentDepth = 0; + int newFixedDepth = 0; + + // We don't apply autosizing to nodes with fixed height normally. + // But we apply it to nodes which are located deep enough + // (nesting depth is greater than some const) inside of a parent block + // which has fixed height but its content overflows intentionally. + for (RenderObject* descendent = traverseNext(this, includeNonFixedHeight, currentDepth, newFixedDepth); descendent; descendent = descendent->traverseNext(this, includeNonFixedHeight, currentDepth, newFixedDepth)) { + while (depthStack.size() > 0 && currentDepth <= depthStack[depthStack.size() - 1]) + depthStack.remove(depthStack.size() - 1); + if (newFixedDepth) + depthStack.append(newFixedDepth); + + int stackSize = depthStack.size(); + if (is<RenderBlockFlow>(*descendent) && !descendent->isListItem() && (!stackSize || currentDepth - depthStack[stackSize - 1] > TextAutoSizingFixedHeightDepth)) + downcast<RenderBlockFlow>(*descendent).adjustComputedFontSizes(size, visibleWidth); + newFixedDepth = 0; } - for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) - curr->moveLayers(oldParent, newParent); + // Remove style from auto-sizing table that are no longer valid. + document->validateAutoSizingNodes(); } -RenderLayer* RenderObject::findNextLayer(RenderLayer* parentLayer, RenderObject* startPoint, - bool checkParent) +void RenderObject::resetTextAutosizing() { - // Error check the parent layer passed in. If it's null, we can't find anything. - if (!parentLayer) - return 0; - - // Step 1: If our layer is a child of the desired parent, then return our layer. - RenderLayer* ourLayer = hasLayer() ? toRenderLayerModelObject(this)->layer() : 0; - if (ourLayer && ourLayer->parent() == parentLayer) - return ourLayer; + Document* document = view().frameView().frame().document(); + if (!document) + return; - // Step 2: If we don't have a layer, or our layer is the desired parent, then descend - // into our siblings trying to find the next layer whose parent is the desired parent. - if (!ourLayer || ourLayer == parentLayer) { - for (RenderObject* curr = startPoint ? startPoint->nextSibling() : firstChild(); - curr; curr = curr->nextSibling()) { - RenderLayer* nextLayer = curr->findNextLayer(parentLayer, 0, false); - if (nextLayer) - return nextLayer; - } - } + document->resetAutoSizingNodes(); - // Step 3: If our layer is the desired parent layer, then we're finished. We didn't - // find anything. - if (parentLayer == ourLayer) - return 0; + Vector<int> depthStack; + int currentDepth = 0; + int newFixedDepth = 0; - // Step 4: If |checkParent| is set, climb up to our parent and check its siblings that - // follow us to see if we can locate a layer. - if (checkParent && parent()) - return parent()->findNextLayer(parentLayer, this, true); + for (RenderObject* descendent = traverseNext(this, includeNonFixedHeight, currentDepth, newFixedDepth); descendent; descendent = descendent->traverseNext(this, includeNonFixedHeight, currentDepth, newFixedDepth)) { + while (depthStack.size() > 0 && currentDepth <= depthStack[depthStack.size() - 1]) + depthStack.remove(depthStack.size() - 1); + if (newFixedDepth) + depthStack.append(newFixedDepth); - return 0; + int stackSize = depthStack.size(); + if (is<RenderBlockFlow>(*descendent) && !descendent->isListItem() && (!stackSize || currentDepth - depthStack[stackSize - 1] > TextAutoSizingFixedHeightDepth)) + downcast<RenderBlockFlow>(*descendent).resetComputedFontSize(); + newFixedDepth = 0; + } } +#endif // ENABLE(IOS_TEXT_AUTOSIZING) RenderLayer* RenderObject::enclosingLayer() const { - const RenderObject* curr = this; - while (curr) { - RenderLayer* layer = curr->hasLayer() ? toRenderLayerModelObject(curr)->layer() : 0; - if (layer) - return layer; - curr = curr->parent(); + for (auto& renderer : lineageOfType<RenderLayerModelObject>(*this)) { + if (renderer.hasLayer()) + return renderer.layer(); } - return 0; + return nullptr; } bool RenderObject::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignment& alignX, const ScrollAlignment& alignY) @@ -597,80 +503,58 @@ bool RenderObject::scrollRectToVisible(const LayoutRect& rect, const ScrollAlign return true; } -RenderBox* RenderObject::enclosingBox() const +RenderBox& RenderObject::enclosingBox() const { - RenderObject* curr = const_cast<RenderObject*>(this); - while (curr) { - if (curr->isBox()) - return toRenderBox(curr); - curr = curr->parent(); - } - - ASSERT_NOT_REACHED(); - return 0; + return *lineageOfType<RenderBox>(const_cast<RenderObject&>(*this)).first(); } -RenderBoxModelObject* RenderObject::enclosingBoxModelObject() const +RenderBoxModelObject& RenderObject::enclosingBoxModelObject() const { - RenderObject* curr = const_cast<RenderObject*>(this); - while (curr) { - if (curr->isBoxModelObject()) - return toRenderBoxModelObject(curr); - curr = curr->parent(); - } + return *lineageOfType<RenderBoxModelObject>(const_cast<RenderObject&>(*this)).first(); +} - ASSERT_NOT_REACHED(); - return 0; +bool RenderObject::fixedPositionedWithNamedFlowContainingBlock() const +{ + return ((flowThreadState() == RenderObject::InsideOutOfFlowThread) + && (style().position() == FixedPosition) + && (containingBlock()->isOutOfFlowRenderFlowThread())); } -RenderFlowThread* RenderObject::locateFlowThreadContainingBlock() const +static bool hasFixedPosInNamedFlowContainingBlock(const RenderObject* renderer) { - ASSERT(flowThreadState() != NotInsideFlowThread); + ASSERT(renderer->flowThreadState() != RenderObject::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); + RenderObject* curr = const_cast<RenderObject*>(renderer); + while (curr && !is<RenderView>(*curr)) { + if (curr->fixedPositionedWithNamedFlowContainingBlock()) + return true; 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; + return false; } RenderBlock* RenderObject::firstLineBlock() const { - return 0; + return nullptr; } -static inline bool objectIsRelayoutBoundary(const RenderObject* object) +static inline bool objectIsRelayoutBoundary(const RenderElement* object) { // FIXME: In future it may be possible to broaden these conditions in order to improve performance. + if (object->isRenderView()) + return true; + if (object->isTextControl()) return true; -#if ENABLE(SVG) if (object->isSVGRoot()) return true; -#endif if (!object->hasOverflowClip()) return false; - if (object->style()->width().isIntrinsicOrAuto() || object->style()->height().isIntrinsicOrAuto() || object->style()->height().isPercent()) + if (object->style().width().isIntrinsicOrAuto() || object->style().height().isIntrinsicOrAuto() || object->style().height().isPercentOrCalculated()) return false; // Table parts can't be relayout roots since the table is responsible for layouting all the parts. @@ -680,62 +564,87 @@ static inline bool objectIsRelayoutBoundary(const RenderObject* object) return true; } -void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, RenderObject* newRoot) +void RenderObject::clearNeedsLayout() +{ + m_bitfields.setNeedsLayout(false); + setEverHadLayout(true); + setPosChildNeedsLayoutBit(false); + setNeedsSimplifiedNormalFlowLayoutBit(false); + setNormalChildNeedsLayoutBit(false); + setNeedsPositionedMovementLayoutBit(false); + if (is<RenderElement>(*this)) + downcast<RenderElement>(*this).setAncestorLineBoxDirty(false); +#ifndef NDEBUG + checkBlockPositionedObjectsNeedLayout(); +#endif +} + +static void scheduleRelayoutForSubtree(RenderElement& renderer) { - ASSERT(!scheduleRelayout || !newRoot); + if (is<RenderView>(renderer)) { + downcast<RenderView>(renderer).frameView().scheduleRelayout(); + return; + } + + if (renderer.isRooted()) + renderer.view().frameView().scheduleRelayoutOfSubtree(renderer); +} + +void RenderObject::markContainingBlocksForLayout(ScheduleRelayout scheduleRelayout, RenderElement* newRoot) +{ + ASSERT(scheduleRelayout == ScheduleRelayout::No || !newRoot); ASSERT(!isSetNeedsLayoutForbidden()); - RenderObject* object = container(); - RenderObject* last = this; + auto ancestor = container(); bool simplifiedNormalFlowLayout = needsSimplifiedNormalFlowLayout() && !selfNeedsLayout() && !normalChildNeedsLayout(); + bool hasOutOfFlowPosition = !isText() && style().hasOutOfFlowPosition(); - while (object) { + while (ancestor) { #ifndef NDEBUG // FIXME: Remove this once we remove the special cases for counters, quotes and mathml // calling setNeedsLayout during preferred width computation. - SetLayoutNeededForbiddenScope layoutForbiddenScope(object, isSetNeedsLayoutForbidden()); + SetLayoutNeededForbiddenScope layoutForbiddenScope(ancestor, isSetNeedsLayoutForbidden()); #endif // Don't mark the outermost object of an unrooted subtree. That object will be // marked when the subtree is added to the document. - RenderObject* container = object->container(); - if (!container && !object->isRenderView()) + auto container = ancestor->container(); + if (!container && !ancestor->isRenderView()) return; - if (!last->isText() && last->style()->hasOutOfFlowPosition()) { - bool willSkipRelativelyPositionedInlines = !object->isRenderBlock() || object->isAnonymousBlock(); + if (hasOutOfFlowPosition) { + bool willSkipRelativelyPositionedInlines = !ancestor->isRenderBlock() || ancestor->isAnonymousBlock(); // Skip relatively positioned inlines and anonymous blocks to get to the enclosing RenderBlock. - while (object && (!object->isRenderBlock() || object->isAnonymousBlock())) - object = object->container(); - if (!object || object->posChildNeedsLayout()) + while (ancestor && (!ancestor->isRenderBlock() || ancestor->isAnonymousBlock())) + ancestor = ancestor->container(); + if (!ancestor || ancestor->posChildNeedsLayout()) return; if (willSkipRelativelyPositionedInlines) - container = object->container(); - object->setPosChildNeedsLayout(true); + container = ancestor->container(); + ancestor->setPosChildNeedsLayoutBit(true); simplifiedNormalFlowLayout = true; - ASSERT(!object->isSetNeedsLayoutForbidden()); } else if (simplifiedNormalFlowLayout) { - if (object->needsSimplifiedNormalFlowLayout()) + if (ancestor->needsSimplifiedNormalFlowLayout()) return; - object->setNeedsSimplifiedNormalFlowLayout(true); - ASSERT(!object->isSetNeedsLayoutForbidden()); + ancestor->setNeedsSimplifiedNormalFlowLayoutBit(true); } else { - if (object->normalChildNeedsLayout()) + if (ancestor->normalChildNeedsLayout()) return; - object->setNormalChildNeedsLayout(true); - ASSERT(!object->isSetNeedsLayoutForbidden()); + ancestor->setNormalChildNeedsLayoutBit(true); } + ASSERT(!ancestor->isSetNeedsLayoutForbidden()); - if (object == newRoot) + if (ancestor == newRoot) return; - last = object; - if (scheduleRelayout && objectIsRelayoutBoundary(last)) + if (scheduleRelayout == ScheduleRelayout::Yes && objectIsRelayoutBoundary(ancestor)) break; - object = container; + + hasOutOfFlowPosition = ancestor->style().hasOutOfFlowPosition(); + ancestor = container; } - if (scheduleRelayout) - last->scheduleRelayout(); + if (scheduleRelayout == ScheduleRelayout::Yes && ancestor) + scheduleRelayoutForSubtree(*ancestor); } #ifndef NDEBUG @@ -743,8 +652,8 @@ void RenderObject::checkBlockPositionedObjectsNeedLayout() { ASSERT(!needsLayout()); - if (isRenderBlock()) - toRenderBlock(this)->checkPositionedObjectsNeedLayout(); + if (is<RenderBlock>(*this)) + downcast<RenderBlock>(*this).checkPositionedObjectsNeedLayout(); } #endif @@ -752,7 +661,7 @@ void RenderObject::setPreferredLogicalWidthsDirty(bool shouldBeDirty, MarkingBeh { bool alreadyDirty = preferredLogicalWidthsDirty(); m_bitfields.setPreferredLogicalWidthsDirty(shouldBeDirty); - if (shouldBeDirty && !alreadyDirty && markParents == MarkContainingBlockChain && (isText() || !style()->hasOutOfFlowPosition())) + if (shouldBeDirty && !alreadyDirty && markParents == MarkContainingBlockChain && (isText() || !style().hasOutOfFlowPosition())) invalidateContainerPreferredLogicalWidths(); } @@ -760,16 +669,16 @@ void RenderObject::invalidateContainerPreferredLogicalWidths() { // In order to avoid pathological behavior when inlines are deeply nested, we do include them // in the chain that we mark dirty (even though they're kind of irrelevant). - RenderObject* o = isTableCell() ? containingBlock() : container(); + auto o = isTableCell() ? containingBlock() : container(); while (o && !o->preferredLogicalWidthsDirty()) { // Don't invalidate the outermost object of an unrooted subtree. That object will be // invalidated when the subtree is added to the document. - RenderObject* container = o->isTableCell() ? o->containingBlock() : o->container(); + auto container = o->isTableCell() ? o->containingBlock() : o->container(); if (!container && !o->isRenderView()) break; o->m_bitfields.setPreferredLogicalWidthsDirty(true); - if (o->style()->hasOutOfFlowPosition()) + if (o->style().hasOutOfFlowPosition()) // A positioned object has no effect on the min/max width of its containing block ever. // We can optimize this case and not go up any further. break; @@ -780,416 +689,88 @@ void RenderObject::invalidateContainerPreferredLogicalWidths() void RenderObject::setLayerNeedsFullRepaint() { ASSERT(hasLayer()); - toRenderLayerModelObject(this)->layer()->setRepaintStatus(NeedsFullRepaint); + downcast<RenderLayerModelObject>(*this).layer()->setRepaintStatus(NeedsFullRepaint); } void RenderObject::setLayerNeedsFullRepaintForPositionedMovementLayout() { ASSERT(hasLayer()); - toRenderLayerModelObject(this)->layer()->setRepaintStatus(NeedsFullRepaintForPositionedMovementLayout); + downcast<RenderLayerModelObject>(*this).layer()->setRepaintStatus(NeedsFullRepaintForPositionedMovementLayout); } RenderBlock* RenderObject::containingBlock() const { - RenderObject* o = parent(); - if (!o && isRenderScrollbarPart()) - o = toRenderScrollbarPart(this)->rendererOwningScrollbar(); + auto parent = this->parent(); + if (!parent && is<RenderScrollbarPart>(*this)) + parent = downcast<RenderScrollbarPart>(*this).rendererOwningScrollbar(); - if (!isText() && m_style->position() == FixedPosition) - o = containingBlockForFixedPosition(o); - else if (!isText() && m_style->position() == AbsolutePosition) - o = containingBlockForAbsolutePosition(o); + 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); else - o = containingBlockForObjectInFlow(o); - - if (!o || !o->isRenderBlock()) - return 0; // This can still happen in case of an orphaned tree + parent = containingBlockForObjectInFlow(parent); - return toRenderBlock(o); + // This can still happen in case of an detached tree + if (!parent) + return nullptr; + return downcast<RenderBlock>(parent); } -static bool mustRepaintFillLayers(const RenderObject* renderer, const FillLayer* layer) +void RenderObject::addPDFURLRect(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { - // Nobody will use multiple layers without wanting fancy positioning. - if (layer->next()) - return true; - - // Make sure we have a valid image. - StyleImage* img = layer->image(); - if (!img || !img->canRender(renderer, renderer->style()->effectiveZoom())) - return false; - - if (!layer->xPosition().isZero() || !layer->yPosition().isZero()) - return true; - - EFillSizeType sizeType = layer->sizeType(); - - if (sizeType == Contain || sizeType == Cover) - return true; - - if (sizeType == SizeLength) { - LengthSize size = layer->sizeLength(); - if (size.width().isPercent() || size.height().isPercent()) - return true; - // If the image has neither an intrinsic width nor an intrinsic height, its size is determined as for 'contain'. - if ((size.width().isAuto() || size.height().isAuto()) && img->isGeneratedImage()) - return true; - } else if (img->usesImageContainerSize()) - return true; - - return false; -} - -bool RenderObject::borderImageIsLoadedAndCanBeRendered() const -{ - ASSERT(style()->hasBorder()); - - StyleImage* borderImage = style()->borderImage().image(); - return borderImage && borderImage->canRender(this, style()->effectiveZoom()) && borderImage->isLoaded(); -} - -bool RenderObject::mustRepaintBackgroundOrBorder() const -{ - if (hasMask() && mustRepaintFillLayers(this, style()->maskLayers())) - return true; - - // If we don't have a background/border/mask, then nothing to do. - if (!hasBoxDecorations()) - return false; - - if (mustRepaintFillLayers(this, style()->backgroundLayers())) - return true; - - // Our fill layers are ok. Let's check border. - if (style()->hasBorder() && borderImageIsLoadedAndCanBeRendered()) - return true; - - return false; -} - -void RenderObject::drawLineForBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, - BoxSide side, Color color, EBorderStyle style, - int adjacentWidth1, int adjacentWidth2, bool antialias) -{ - 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 (style == DOUBLE && thickness < 3) - style = SOLID; - - switch (style) { - 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, m_style->colorSpace()); - graphicsContext->setStrokeThickness(thickness); - graphicsContext->setStrokeStyle(style == 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, m_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 + max((-adjacentWidth1 * 2 + 1) / 3, 0), - y1, x2 - max((-adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness, - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - drawLineForBoxSide(graphicsContext, x1 + max((adjacentWidth1 * 2 + 1) / 3, 0), - y2 - thirdOfThickness, x2 - max((adjacentWidth2 * 2 + 1) / 3, 0), y2, - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - break; - case BSLeft: - drawLineForBoxSide(graphicsContext, x1, y1 + max((-adjacentWidth1 * 2 + 1) / 3, 0), - x1 + thirdOfThickness, y2 - max((-adjacentWidth2 * 2 + 1) / 3, 0), - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, y1 + max((adjacentWidth1 * 2 + 1) / 3, 0), - x2, y2 - max((adjacentWidth2 * 2 + 1) / 3, 0), - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - break; - case BSBottom: - drawLineForBoxSide(graphicsContext, x1 + max((adjacentWidth1 * 2 + 1) / 3, 0), - y1, x2 - max((adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness, - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - drawLineForBoxSide(graphicsContext, x1 + max((-adjacentWidth1 * 2 + 1) / 3, 0), - y2 - thirdOfThickness, x2 - max((-adjacentWidth2 * 2 + 1) / 3, 0), y2, - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - break; - case BSRight: - drawLineForBoxSide(graphicsContext, x1, y1 + max((adjacentWidth1 * 2 + 1) / 3, 0), - x1 + thirdOfThickness, y2 - max((adjacentWidth2 * 2 + 1) / 3, 0), - side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); - drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, y1 + max((-adjacentWidth1 * 2 + 1) / 3, 0), - x2, y2 - 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 (style == 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 + max(-adjacentWidth1, 0) / 2, y1, x2 - max(-adjacentWidth2, 0) / 2, (y1 + y2 + 1) / 2, - side, color, s1, adjacent1BigHalf, adjacent2BigHalf, antialias); - drawLineForBoxSide(graphicsContext, x1 + max(adjacentWidth1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(adjacentWidth2 + 1, 0) / 2, y2, - side, color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); - break; - case BSLeft: - drawLineForBoxSide(graphicsContext, x1, y1 + max(-adjacentWidth1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(-adjacentWidth2, 0) / 2, - side, color, s1, adjacent1BigHalf, adjacent2BigHalf, antialias); - drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(adjacentWidth1 + 1, 0) / 2, x2, y2 - max(adjacentWidth2 + 1, 0) / 2, - side, color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); - break; - case BSBottom: - drawLineForBoxSide(graphicsContext, x1 + max(adjacentWidth1, 0) / 2, y1, x2 - max(adjacentWidth2, 0) / 2, (y1 + y2 + 1) / 2, - side, color, s2, adjacent1BigHalf, adjacent2BigHalf, antialias); - drawLineForBoxSide(graphicsContext, x1 + max(-adjacentWidth1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(-adjacentWidth2 + 1, 0) / 2, y2, - side, color, s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); - break; - case BSRight: - drawLineForBoxSide(graphicsContext, x1, y1 + max(adjacentWidth1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(adjacentWidth2, 0) / 2, - side, color, s2, adjacent1BigHalf, adjacent2BigHalf, antialias); - drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(-adjacentWidth1 + 1, 0) / 2, x2, y2 - 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(); - // fall through - case OUTSET: - if (style == OUTSET && (side == BSBottom || side == BSRight)) - color = color.dark(); - // fall through - case SOLID: { - StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); - graphicsContext->setStrokeStyle(NoStroke); - graphicsContext->setFillColor(color, m_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 + max(-adjacentWidth1, 0), y1); - quad[1] = FloatPoint(x1 + max(adjacentWidth1, 0), y2); - quad[2] = FloatPoint(x2 - max(adjacentWidth2, 0), y2); - quad[3] = FloatPoint(x2 - max(-adjacentWidth2, 0), y1); - break; - case BSBottom: - quad[0] = FloatPoint(x1 + max(adjacentWidth1, 0), y1); - quad[1] = FloatPoint(x1 + max(-adjacentWidth1, 0), y2); - quad[2] = FloatPoint(x2 - max(-adjacentWidth2, 0), y2); - quad[3] = FloatPoint(x2 - max(adjacentWidth2, 0), y1); - break; - case BSLeft: - quad[0] = FloatPoint(x1, y1 + max(-adjacentWidth1, 0)); - quad[1] = FloatPoint(x1, y2 - max(-adjacentWidth2, 0)); - quad[2] = FloatPoint(x2, y2 - max(adjacentWidth2, 0)); - quad[3] = FloatPoint(x2, y1 + max(adjacentWidth1, 0)); - break; - case BSRight: - quad[0] = FloatPoint(x1, y1 + max(adjacentWidth1, 0)); - quad[1] = FloatPoint(x1, y2 - max(adjacentWidth2, 0)); - quad[2] = FloatPoint(x2, y2 - max(-adjacentWidth2, 0)); - quad[3] = FloatPoint(x2, y1 + 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; + Vector<LayoutRect> focusRingRects; addFocusRingRects(focusRingRects, paintOffset, paintInfo.paintContainer); - if (style->outlineStyleIsAuto()) - paintInfo.context->drawFocusRing(focusRingRects, style->outlineWidth(), style->outlineOffset(), style->visitedDependentColor(CSSPropertyOutlineColor)); - else - addPDFURLRect(paintInfo.context, unionRect(focusRingRects)); -} + LayoutRect urlRect = unionRect(focusRingRects); -void RenderObject::addPDFURLRect(GraphicsContext* context, const LayoutRect& rect) -{ - if (rect.isEmpty()) + if (urlRect.isEmpty()) return; - Node* n = node(); - if (!n || !n->isLink() || !n->isElementNode()) + Node* node = this->node(); + if (!is<Element>(node) || !node->isLink()) return; - const AtomicString& href = toElement(n)->getAttribute(hrefAttr); + const AtomicString& href = downcast<Element>(*node).getAttribute(hrefAttr); if (href.isNull()) return; - context->setURLForRect(n->document()->completeURL(href), pixelSnappedIntRect(rect)); + paintInfo.context().setURLForRect(node->document().completeURL(href), snappedIntRect(urlRect)); } -void RenderObject::paintOutline(PaintInfo& paintInfo, const LayoutRect& paintRect) +#if PLATFORM(IOS) +// This function is similar in spirit to RenderText::absoluteRectsForRange, but returns rectangles +// which are annotated with additional state which helps iOS draw selections in its unique way. +// No annotations are added in this class. +// FIXME: Move to RenderText with absoluteRectsForRange()? +void RenderObject::collectSelectionRects(Vector<SelectionRect>& rects, unsigned start, unsigned end) { - if (!hasOutline()) - return; - - RenderStyle* styleToUse = style(); - LayoutUnit outlineWidth = styleToUse->outlineWidth(); - - int outlineOffset = styleToUse->outlineOffset(); + Vector<FloatQuad> quads; - 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 (!firstChildSlow()) { + // FIXME: WebKit's position for an empty span after a BR is incorrect, so we can't trust + // quads for them. We don't need selection rects for those anyway though, since they + // are just empty containers. See <https://bugs.webkit.org/show_bug.cgi?id=49358>. + RenderObject* previous = previousSibling(); + Node* node = this->node(); + if (!previous || !previous->isBR() || !node || !node->isContainerNode() || !isInline()) { + // For inline elements we don't use absoluteQuads, since it takes into account continuations and leads to wrong results. + absoluteQuadsForSelection(quads); } + } else { + unsigned offset = start; + for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) + child->absoluteQuads(quads); } - 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(); + unsigned numberOfQuads = quads.size(); + for (unsigned i = 0; i < numberOfQuads; ++i) + rects.append(SelectionRect(quads[i].enclosingBoundingBox(), isHorizontalWritingMode(), view().pageNumberForBlockProgressionOffset(quads[i].enclosingBoundingBox().x()))); } +#endif -IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms) const +IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms, bool* wasFixed) const { if (useTransforms) { Vector<FloatQuad> quads; - absoluteQuads(quads); + absoluteQuads(quads, wasFixed); size_t n = quads.size(); if (!n) @@ -1201,7 +782,7 @@ IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms) const return result; } - FloatPoint absPos = localToAbsolute(); + FloatPoint absPos = localToAbsolute(FloatPoint(), 0 /* ignore transforms */, wasFixed); Vector<IntRect> rects; absoluteRects(rects, flooredLayoutPoint(absPos)); @@ -1212,36 +793,34 @@ IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms) const LayoutRect result = rects[0]; for (size_t i = 1; i < n; ++i) result.unite(rects[i]); - return pixelSnappedIntRect(result); + return snappedIntRect(result); } void RenderObject::absoluteFocusRingQuads(Vector<FloatQuad>& quads) { - Vector<IntRect> rects; + Vector<LayoutRect> rects; // FIXME: addFocusRingRects() needs to be passed this transform-unaware // localToAbsolute() offset here because RenderInline::addFocusRingRects() // implicitly assumes that. This doesn't work correctly with transformed // descendants. FloatPoint absolutePoint = localToAbsolute(); addFocusRingRects(rects, flooredLayoutPoint(absolutePoint)); - size_t count = rects.size(); - for (size_t i = 0; i < count; ++i) { - IntRect rect = rects[i]; - rect.move(-absolutePoint.x(), -absolutePoint.y()); - quads.append(localToAbsoluteQuad(FloatQuad(rect))); + float deviceScaleFactor = document().deviceScaleFactor(); + for (auto rect : rects) { + rect.moveBy(LayoutPoint(-absolutePoint)); + quads.append(localToAbsoluteQuad(FloatQuad(snapRectToDevicePixels(rect, deviceScaleFactor)))); } } FloatRect RenderObject::absoluteBoundingBoxRectForRange(const Range* range) { - if (!range || !range->startContainer()) + if (!range) return FloatRect(); - if (range->ownerDocument()) - range->ownerDocument()->updateLayout(); + range->ownerDocument().updateLayout(); Vector<FloatQuad> quads; - range->textQuads(quads); + range->absoluteTextQuads(quads); if (quads.isEmpty()) return FloatRect(); @@ -1257,7 +836,7 @@ void RenderObject::addAbsoluteRectForLayer(LayoutRect& result) { if (hasLayer()) result.unite(absoluteBoundingBoxRectIgnoringTransforms()); - for (RenderObject* current = firstChild(); current; current = current->nextSibling()) + for (RenderObject* current = firstChildSlow(); current; current = current->nextSibling()) current->addAbsoluteRectForLayer(result); } @@ -1266,255 +845,187 @@ LayoutRect RenderObject::paintingRootRect(LayoutRect& topLevelRect) { LayoutRect result = absoluteBoundingBoxRectIgnoringTransforms(); topLevelRect = result; - for (RenderObject* current = firstChild(); current; current = current->nextSibling()) + for (RenderObject* current = firstChildSlow(); current; current = current->nextSibling()) current->addAbsoluteRectForLayer(result); return result; } -void RenderObject::paint(PaintInfo&, const LayoutPoint&) -{ -} - RenderLayerModelObject* RenderObject::containerForRepaint() const { - RenderView* v = view(); - if (!v) - return 0; - - RenderLayerModelObject* repaintContainer = 0; + RenderLayerModelObject* repaintContainer = nullptr; -#if USE(ACCELERATED_COMPOSITING) - if (v->usesCompositing()) { + if (view().usesCompositing()) { if (RenderLayer* parentLayer = enclosingLayer()) { RenderLayer* compLayer = parentLayer->enclosingCompositingLayerForRepaint(); if (compLayer) - repaintContainer = compLayer->renderer(); + repaintContainer = &compLayer->renderer(); } } -#endif - -#if ENABLE(CSS_FILTERS) - if (document()->view()->hasSoftwareFilters()) { + if (view().hasSoftwareFilters()) { if (RenderLayer* parentLayer = enclosingLayer()) { RenderLayer* enclosingFilterLayer = parentLayer->enclosingFilterLayer(); if (enclosingFilterLayer) - return enclosingFilterLayer->renderer(); + 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 // repainting to do individual region repaints. RenderFlowThread* parentRenderFlowThread = flowThreadContainingBlock(); if (parentRenderFlowThread) { - // 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()) + // If the element has a fixed positioned element with named flow as CB along the CB chain + // then the repaint container is not the flow thread. + if (hasFixedPosInNamedFlowContainingBlock(this)) return repaintContainer; // If we have already found a repaint container then we will repaint into that container only if it is part of the same // flow thread. Otherwise we will need to catch the repaint call and send it to the flow thread. - RenderFlowThread* repaintContainerFlowThread = repaintContainer ? repaintContainer->flowThreadContainingBlock() : 0; + RenderFlowThread* repaintContainerFlowThread = repaintContainer ? repaintContainer->flowThreadContainingBlock() : nullptr; if (!repaintContainerFlowThread || repaintContainerFlowThread != parentRenderFlowThread) repaintContainer = parentRenderFlowThread; } return repaintContainer; } -void RenderObject::repaintUsingContainer(const RenderLayerModelObject* repaintContainer, const IntRect& r, bool immediate) const +void RenderObject::propagateRepaintToParentWithOutlineAutoIfNeeded(const RenderLayerModelObject& repaintContainer, const LayoutRect& repaintRect) const { - if (!repaintContainer) { - view()->repaintViewRectangle(r, immediate); + if (!hasOutlineAutoAncestor()) + return; + + // FIXME: We should really propagate only when the the child renderer sticks out. + bool repaintRectNeedsConverting = false; + // Issue repaint on the renderer with outline: auto. + for (const auto* renderer = this; renderer; renderer = renderer->parent()) { + bool rendererHasOutlineAutoAncestor = renderer->hasOutlineAutoAncestor(); + ASSERT(rendererHasOutlineAutoAncestor + || renderer->outlineStyleForRepaint().outlineStyleIsAuto() + || (is<RenderElement>(*renderer) && downcast<RenderElement>(*renderer).hasContinuation())); + if (renderer == &repaintContainer && rendererHasOutlineAutoAncestor) + repaintRectNeedsConverting = true; + if (rendererHasOutlineAutoAncestor) + continue; + // Issue repaint on the correct repaint container. + LayoutRect adjustedRepaintRect = repaintRect; + adjustedRepaintRect.inflate(renderer->outlineStyleForRepaint().outlineSize()); + if (!repaintRectNeedsConverting) + repaintContainer.repaintRectangle(adjustedRepaintRect); + else if (is<RenderLayerModelObject>(renderer)) { + const auto& rendererWithOutline = downcast<RenderLayerModelObject>(*renderer); + adjustedRepaintRect = LayoutRect(repaintContainer.localToContainerQuad(FloatRect(adjustedRepaintRect), &rendererWithOutline).boundingBox()); + rendererWithOutline.repaintRectangle(adjustedRepaintRect); + } return; } + ASSERT_NOT_REACHED(); +} - if (repaintContainer->isRenderFlowThread()) { - toRenderFlowThread(repaintContainer)->repaintRectangleInRegions(r, immediate); +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); return; } -#if ENABLE(CSS_FILTERS) + propagateRepaintToParentWithOutlineAutoIfNeeded(*repaintContainer, r); + if (repaintContainer->hasFilter() && repaintContainer->layer() && repaintContainer->layer()->requiresFullLayerImageForFilters()) { - repaintContainer->layer()->setFilterBackendNeedsRepaintingInRect(r, immediate); + repaintContainer->layer()->setFilterBackendNeedsRepaintingInRect(r); return; } -#endif -#if USE(ACCELERATED_COMPOSITING) - RenderView* v = view(); if (repaintContainer->isRenderView()) { - ASSERT(repaintContainer == v); - bool viewHasCompositedLayer = v->hasLayer() && v->layer()->isComposited(); - if (!viewHasCompositedLayer || v->layer()->backing()->paintsIntoWindow()) { - v->repaintViewRectangle(viewHasCompositedLayer && v->layer()->transform() ? v->layer()->transform()->mapRect(r) : r, immediate); + RenderView& view = this->view(); + ASSERT(repaintContainer == &view); + bool viewHasCompositedLayer = view.isComposited(); + if (!viewHasCompositedLayer || view.layer()->backing()->paintsIntoWindow()) { + LayoutRect rect = r; + if (viewHasCompositedLayer && view.layer()->transform()) + rect = LayoutRect(view.layer()->transform()->mapRect(snapRectToDevicePixels(rect, document().deviceScaleFactor()))); + view.repaintViewRectangle(rect); return; } } - - if (v->usesCompositing()) { - ASSERT(repaintContainer->hasLayer() && repaintContainer->layer()->isComposited()); - repaintContainer->layer()->setBackingNeedsRepaintInRect(r); + + if (view().usesCompositing()) { + ASSERT(repaintContainer->isComposited()); + repaintContainer->layer()->setBackingNeedsRepaintInRect(r, shouldClipToLayer ? GraphicsLayer::ClipToLayer : GraphicsLayer::DoNotClipToLayer); } -#else - if (repaintContainer->isRenderView()) - toRenderView(repaintContainer)->repaintViewRectangle(r, immediate); -#endif } -void RenderObject::repaint(bool immediate) const +void RenderObject::repaint() const { // Don't repaint if we're unrooted (note that view() still returns the view when unrooted) - RenderView* view; - if (!isRooted(&view)) + if (!isRooted()) return; - if (view->printing()) - return; // Don't repaint if we're printing. + const RenderView& view = this->view(); + if (view.printing()) + return; RenderLayerModelObject* repaintContainer = containerForRepaint(); - repaintUsingContainer(repaintContainer ? repaintContainer : view, pixelSnappedIntRect(clippedOverflowRectForRepaint(repaintContainer)), immediate); + repaintUsingContainer(repaintContainer, clippedOverflowRectForRepaint(repaintContainer)); } -void RenderObject::repaintRectangle(const LayoutRect& r, bool immediate) const +void RenderObject::repaintRectangle(const LayoutRect& r, bool shouldClipToLayer) const { // Don't repaint if we're unrooted (note that view() still returns the view when unrooted) - RenderView* view; - if (!isRooted(&view)) + if (!isRooted()) return; - if (view->printing()) - return; // Don't repaint if we're printing. + const RenderView& view = this->view(); + if (view.printing()) + return; LayoutRect dirtyRect(r); - // FIXME: layoutDelta needs to be applied in parts before/after transforms and // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 - dirtyRect.move(view->layoutDelta()); + dirtyRect.move(view.layoutDelta()); RenderLayerModelObject* repaintContainer = containerForRepaint(); - computeRectForRepaint(repaintContainer, dirtyRect); - repaintUsingContainer(repaintContainer ? repaintContainer : view, pixelSnappedIntRect(dirtyRect), immediate); -} - -IntRect RenderObject::pixelSnappedAbsoluteClippedOverflowRect() const -{ - return pixelSnappedIntRect(absoluteClippedOverflowRect()); + repaintUsingContainer(repaintContainer, computeRectForRepaint(dirtyRect, repaintContainer), shouldClipToLayer); } -bool RenderObject::repaintAfterLayoutIfNeeded(const RenderLayerModelObject* repaintContainer, const LayoutRect& oldBounds, const LayoutRect& oldOutlineBox, const LayoutRect* newBoundsPtr, const LayoutRect* newOutlineBoxRectPtr) +void RenderObject::repaintSlowRepaintObject() const { - RenderView* v = view(); - if (v->printing()) - return false; // Don't repaint if we're printing. - - // This ASSERT fails due to animations. See https://bugs.webkit.org/show_bug.cgi?id=37048 - // ASSERT(!newBoundsPtr || *newBoundsPtr == clippedOverflowRectForRepaint(repaintContainer)); - LayoutRect newBounds = newBoundsPtr ? *newBoundsPtr : clippedOverflowRectForRepaint(repaintContainer); - LayoutRect newOutlineBox; + // Don't repaint if we're unrooted (note that view() still returns the view when unrooted) + if (!isRooted()) + return; - bool fullRepaint = selfNeedsLayout(); - // Presumably a background or a border exists if border-fit:lines was specified. - if (!fullRepaint && style()->borderFit() == BorderFitLines) - fullRepaint = true; - if (!fullRepaint) { - // This ASSERT fails due to animations. See https://bugs.webkit.org/show_bug.cgi?id=37048 - // ASSERT(!newOutlineBoxRectPtr || *newOutlineBoxRectPtr == outlineBoundsForRepaint(repaintContainer)); - newOutlineBox = newOutlineBoxRectPtr ? *newOutlineBoxRectPtr : outlineBoundsForRepaint(repaintContainer); - if (newOutlineBox.location() != oldOutlineBox.location() || (mustRepaintBackgroundOrBorder() && (newBounds != oldBounds || newOutlineBox != oldOutlineBox))) - fullRepaint = true; - } + const RenderView& view = this->view(); + if (view.printing()) + return; + const RenderLayerModelObject* repaintContainer = containerForRepaint(); if (!repaintContainer) - repaintContainer = v; - - if (fullRepaint) { - repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds)); - if (newBounds != oldBounds) - repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds)); - return true; - } - - if (newBounds == oldBounds && newOutlineBox == oldOutlineBox) - return false; + 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)); - LayoutUnit deltaLeft = newBounds.x() - oldBounds.x(); - if (deltaLeft > 0) - repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds.x(), oldBounds.y(), deltaLeft, oldBounds.height())); - else if (deltaLeft < 0) - repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds.x(), newBounds.y(), -deltaLeft, newBounds.height())); - - LayoutUnit deltaRight = newBounds.maxX() - oldBounds.maxX(); - if (deltaRight > 0) - repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds.maxX(), newBounds.y(), deltaRight, newBounds.height())); - else if (deltaRight < 0) - repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds.maxX(), oldBounds.y(), -deltaRight, oldBounds.height())); - - LayoutUnit deltaTop = newBounds.y() - oldBounds.y(); - if (deltaTop > 0) - repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds.x(), oldBounds.y(), oldBounds.width(), deltaTop)); - else if (deltaTop < 0) - repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds.x(), newBounds.y(), newBounds.width(), -deltaTop)); - - LayoutUnit deltaBottom = newBounds.maxY() - oldBounds.maxY(); - if (deltaBottom > 0) - repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds.x(), oldBounds.maxY(), newBounds.width(), deltaBottom)); - else if (deltaBottom < 0) - repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds.x(), newBounds.maxY(), oldBounds.width(), -deltaBottom)); - - if (newOutlineBox == oldOutlineBox) - return false; + repaintUsingContainer(repaintContainer, repaintRect, shouldClipToLayer); +} - // We didn't move, but we did change size. Invalidate the delta, which will consist of possibly - // two rectangles (but typically only one). - RenderStyle* outlineStyle = outlineStyleForRepaint(); - LayoutUnit outlineWidth = outlineStyle->outlineSize(); - LayoutBoxExtent insetShadowExtent = style()->getBoxShadowInsetExtent(); - LayoutUnit width = absoluteValue(newOutlineBox.width() - oldOutlineBox.width()); - if (width) { - LayoutUnit shadowLeft; - LayoutUnit shadowRight; - style()->getBoxShadowHorizontalExtent(shadowLeft, shadowRight); - int borderRight = isBox() ? toRenderBox(this)->borderRight() : 0; - LayoutUnit boxWidth = isBox() ? toRenderBox(this)->width() : LayoutUnit(); - LayoutUnit minInsetRightShadowExtent = min<LayoutUnit>(-insetShadowExtent.right(), min<LayoutUnit>(newBounds.width(), oldBounds.width())); - LayoutUnit borderWidth = max<LayoutUnit>(borderRight, max<LayoutUnit>(valueForLength(style()->borderTopRightRadius().width(), boxWidth, v), valueForLength(style()->borderBottomRightRadius().width(), boxWidth, v))); - LayoutUnit decorationsWidth = max<LayoutUnit>(-outlineStyle->outlineOffset(), borderWidth + minInsetRightShadowExtent) + max<LayoutUnit>(outlineWidth, shadowRight); - LayoutRect rightRect(newOutlineBox.x() + min(newOutlineBox.width(), oldOutlineBox.width()) - decorationsWidth, - newOutlineBox.y(), - width + decorationsWidth, - max(newOutlineBox.height(), oldOutlineBox.height())); - LayoutUnit right = min<LayoutUnit>(newBounds.maxX(), oldBounds.maxX()); - if (rightRect.x() < right) { - rightRect.setWidth(min(rightRect.width(), right - rightRect.x())); - repaintUsingContainer(repaintContainer, pixelSnappedIntRect(rightRect)); - } - } - LayoutUnit height = absoluteValue(newOutlineBox.height() - oldOutlineBox.height()); - if (height) { - LayoutUnit shadowTop; - LayoutUnit shadowBottom; - style()->getBoxShadowVerticalExtent(shadowTop, shadowBottom); - int borderBottom = isBox() ? toRenderBox(this)->borderBottom() : 0; - LayoutUnit boxHeight = isBox() ? toRenderBox(this)->height() : LayoutUnit(); - LayoutUnit minInsetBottomShadowExtent = min<LayoutUnit>(-insetShadowExtent.bottom(), min<LayoutUnit>(newBounds.height(), oldBounds.height())); - LayoutUnit borderHeight = max<LayoutUnit>(borderBottom, max<LayoutUnit>(valueForLength(style()->borderBottomLeftRadius().height(), boxHeight, v), valueForLength(style()->borderBottomRightRadius().height(), boxHeight, v))); - LayoutUnit decorationsHeight = max<LayoutUnit>(-outlineStyle->outlineOffset(), borderHeight + minInsetBottomShadowExtent) + max<LayoutUnit>(outlineWidth, shadowBottom); - LayoutRect bottomRect(newOutlineBox.x(), - min(newOutlineBox.maxY(), oldOutlineBox.maxY()) - decorationsHeight, - max(newOutlineBox.width(), oldOutlineBox.width()), - height + decorationsHeight); - LayoutUnit bottom = min(newBounds.maxY(), oldBounds.maxY()); - if (bottomRect.y() < bottom) { - bottomRect.setHeight(min(bottomRect.height(), bottom - bottomRect.y())); - repaintUsingContainer(repaintContainer, pixelSnappedIntRect(bottomRect)); - } - } - return false; +IntRect RenderObject::pixelSnappedAbsoluteClippedOverflowRect() const +{ + return snappedIntRect(absoluteClippedOverflowRect()); } bool RenderObject::checkForRepaintDuringLayout() const { - return !document()->view()->needsFullRepaint() && !hasLayer() && everHadLayout(); + return !document().view()->needsFullRepaint() && !hasLayer() && everHadLayout(); } LayoutRect RenderObject::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const @@ -1530,587 +1041,251 @@ LayoutRect RenderObject::clippedOverflowRectForRepaint(const RenderLayerModelObj return LayoutRect(); } -void RenderObject::computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect& rect, bool fixed) const +LayoutRect RenderObject::computeRectForRepaint(const LayoutRect& rect, const RenderLayerModelObject* repaintContainer, bool fixed) const { if (repaintContainer == this) - return; + return rect; - if (RenderObject* o = parent()) { - if (o->isBlockFlow()) { - RenderBlock* cb = toRenderBlock(o); - if (cb->hasColumns()) - cb->adjustRectForColumns(rect); - } + auto* parent = this->parent(); + if (!parent) + return rect; - if (o->hasOverflowClip()) { - RenderBox* boxParent = toRenderBox(o); - boxParent->applyCachedClipAndScrollOffsetForRepaint(rect); - if (rect.isEmpty()) - return; - } - - o->computeRectForRepaint(repaintContainer, rect, fixed); + LayoutRect adjustedRect = rect; + if (parent->hasOverflowClip()) { + downcast<RenderBox>(*parent).applyCachedClipAndScrollOffsetForRepaint(adjustedRect); + if (adjustedRect.isEmpty()) + return adjustedRect; } + return parent->computeRectForRepaint(adjustedRect, repaintContainer, fixed); } -void RenderObject::computeFloatRectForRepaint(const RenderLayerModelObject*, FloatRect&, bool) const +FloatRect RenderObject::computeFloatRectForRepaint(const FloatRect&, const RenderLayerModelObject*, bool) const { ASSERT_NOT_REACHED(); + return FloatRect(); } -void RenderObject::dirtyLinesFromChangedChild(RenderObject*) +#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::showTreeForThis() const +void RenderObject::showNodeTreeForThis() const { - if (node()) - node()->showTreeForThis(); + if (!node()) + return; + node()->showTreeForThis(); } void RenderObject::showRenderTreeForThis() const { - showRenderTree(this, 0); + const WebCore::RenderObject* root = this; + while (root->parent()) + root = root->parent(); + showRenderTreeLegend(); + root->showRenderSubTreeAndMark(this, 1); } void RenderObject::showLineTreeForThis() const { - if (containingBlock()) - containingBlock()->showLineTreeAndMark(0, 0, 0, 0, this); -} - -void RenderObject::showRenderObject() const -{ - showRenderObject(0); -} - -void RenderObject::showRenderObject(int printedCharacters) const -{ - // As this function is intended to be used when debugging, the - // this pointer may be 0. - if (!this) { - fputs("(null)\n", stderr); + if (!is<RenderBlockFlow>(*this)) return; - } - - printedCharacters += fprintf(stderr, "%s %p", renderName(), this); - - if (node()) { - if (printedCharacters) - for (; printedCharacters < showTreeCharacterOffset; printedCharacters++) - fputc(' ', stderr); - fputc('\t', stderr); - node()->showNode(); - } else - fputc('\n', stderr); -} - -void RenderObject::showRenderTreeAndMark(const RenderObject* markedObject1, const char* markedLabel1, const RenderObject* markedObject2, const char* markedLabel2, int depth) const -{ - int printedCharacters = 0; - if (markedObject1 == this && markedLabel1) - printedCharacters += fprintf(stderr, "%s", markedLabel1); - if (markedObject2 == this && markedLabel2) - printedCharacters += fprintf(stderr, "%s", markedLabel2); - for (; printedCharacters < depth * 2; printedCharacters++) - fputc(' ', stderr); - - showRenderObject(printedCharacters); - if (!this) - return; - - for (const RenderObject* child = firstChild(); child; child = child->nextSibling()) - child->showRenderTreeAndMark(markedObject1, markedLabel1, markedObject2, markedLabel2, depth + 1); -} - -#endif // NDEBUG - -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(); - } - } - - return color; + showRenderTreeLegend(); + showRenderObject(false, 1); + downcast<RenderBlockFlow>(*this).showLineTreeAndMark(nullptr, 2); } -Color RenderObject::selectionColor(int colorProperty) const +static const RenderFlowThread* flowThreadContainingBlockFromRenderer(const RenderObject* renderer) { - 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 - || (frame()->view()->paintBehavior() & PaintBehaviorSelectionOnly)) - return color; + if (!renderer) + return nullptr; - if (RefPtr<RenderStyle> pseudoStyle = getUncachedPseudoStyle(PseudoStyleRequest(SELECTION))) { - color = pseudoStyle->visitedDependentColor(colorProperty); - if (!color.isValid()) - color = pseudoStyle->visitedDependentColor(CSSPropertyColor); - } else - color = frame()->selection()->isFocusedAndActive() ? - theme()->activeSelectionForegroundColor() : - theme()->inactiveSelectionForegroundColor(); + if (renderer->flowThreadState() == RenderObject::NotInsideFlowThread) + return nullptr; - return color; -} + if (is<RenderFlowThread>(*renderer)) + return downcast<RenderFlowThread>(renderer); -Color RenderObject::selectionForegroundColor() const -{ - return selectionColor(CSSPropertyWebkitTextFillColor); -} + if (is<RenderBlock>(*renderer)) + return downcast<RenderBlock>(*renderer).cachedFlowThreadContainingBlock(); -Color RenderObject::selectionEmphasisMarkColor() const -{ - return selectionColor(CSSPropertyWebkitTextEmphasisColor); + return nullptr; } -void RenderObject::selectionStartEnd(int& spos, int& epos) const +void RenderObject::showRegionsInformation() const { - view()->selectionStartEnd(spos, epos); -} + const RenderFlowThread* ftcb = flowThreadContainingBlockFromRenderer(this); -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(); - RenderObjectChildList* childlist = parent()->virtualChildren(); - childlist->insertChildNode(parent(), block, this); - block->children()->appendChildNode(block, childlist->removeChildNode(parent(), 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()); } -} -void RenderObject::removeAnonymousWrappersForInlinesIfNecessary() -{ - // 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) + if (!ftcb) return; - curr = parent()->firstChild(); - RenderBlock* parentBlock = toRenderBlock(parent()); - while (curr) { - RenderObject* next = curr->nextSibling(); - if (curr->isAnonymousBlock()) - parentBlock->collapseAnonymousBoxChild(parentBlock, toRenderBlock(curr)); - curr = next; - } + RenderRegion* startRegion = nullptr; + RenderRegion* endRegion = nullptr; + ftcb->getRegionRangeForBox(downcast<RenderBox>(this), startRegion, endRegion); + fprintf(stderr, " [Rs:%p Re:%p]", startRegion, endRegion); } -void RenderObject::setAnimatableStyle(PassRefPtr<RenderStyle> style) +void RenderObject::showRenderObject(bool mark, int depth) const { - if (!isText() && style) - setStyle(animation()->updateAnimations(this, style.get())); + if (isInlineBlockOrInlineTable()) + fputc('N', stderr); + else if (isInline()) + fputc('I', stderr); else - setStyle(style); -} - -StyleDifference RenderObject::adjustStyleDifference(StyleDifference diff, unsigned contextSensitiveProperties) const -{ -#if USE(ACCELERATED_COMPOSITING) - // If transform changed, and we are not composited, need to do a layout. - if (contextSensitiveProperties & ContextSensitivePropertyTransform) { - // Text nodes share style with their parents but transforms don't apply to them, - // hence the !isText() check. - // FIXME: when transforms are taken into account for overflow, we will need to do a layout. - if (!isText() && (!hasLayer() || !toRenderLayerModelObject(this)->layer()->isComposited())) { - // We need to set at least SimplifiedLayout, but if PositionedMovementOnly is already set - // then we actually need SimplifiedLayoutAndPositionedMovement. - if (!hasLayer()) - diff = StyleDifferenceLayout; // FIXME: Do this for now since SimplifiedLayout cannot handle updating floating objects lists. - else if (diff < StyleDifferenceLayoutPositionedMovementOnly) - diff = StyleDifferenceSimplifiedLayout; - else if (diff < StyleDifferenceSimplifiedLayout) - diff = StyleDifferenceSimplifiedLayoutAndPositionedMovement; - } else if (diff < StyleDifferenceRecompositeLayer) - diff = StyleDifferenceRecompositeLayer; - } - - // If opacity changed, and we are not composited, need to repaint (also - // ignoring text nodes) - if (contextSensitiveProperties & ContextSensitivePropertyOpacity) { - if (!isText() && (!hasLayer() || !toRenderLayerModelObject(this)->layer()->isComposited())) - diff = StyleDifferenceRepaintLayer; - else if (diff < StyleDifferenceRecompositeLayer) - diff = StyleDifferenceRecompositeLayer; - } - -#if ENABLE(CSS_FILTERS) - if ((contextSensitiveProperties & ContextSensitivePropertyFilter) && hasLayer()) { - RenderLayer* layer = toRenderLayerModelObject(this)->layer(); - if (!layer->isComposited() || layer->paintsWithFilters()) - diff = StyleDifferenceRepaintLayer; - else if (diff < StyleDifferenceRecompositeLayer) - diff = StyleDifferenceRecompositeLayer; - } -#endif + fputc('B', stderr); - // The answer to requiresLayer() for plugins, iframes, and canvas can change without the actual - // style changing, since it depends on whether we decide to composite these elements. When the - // layer status of one of these elements changes, we need to force a layout. - if (diff == StyleDifferenceEqual && style() && isLayerModelObject()) { - if (hasLayer() != toRenderLayerModelObject(this)->requiresLayer()) - diff = StyleDifferenceLayout; - } -#else - UNUSED_PARAM(contextSensitiveProperties); -#endif - - // If we have no layer(), just treat a RepaintLayer hint as a normal Repaint. - if (diff == StyleDifferenceRepaintLayer && !hasLayer()) - diff = StyleDifferenceRepaint; - - return diff; -} - -void RenderObject::setPseudoStyle(PassRefPtr<RenderStyle> pseudoStyle) -{ - ASSERT(pseudoStyle->styleType() == BEFORE || pseudoStyle->styleType() == AFTER); - - // Images are special and must inherit the pseudoStyle so the width and height of - // the pseudo element doesn't change the size of the image. In all other cases we - // can just share the style. - if (isImage()) { - RefPtr<RenderStyle> style = RenderStyle::create(); - style->inheritFrom(pseudoStyle.get()); - setStyle(style.release()); - return; - } - - setStyle(pseudoStyle); -} - -inline bool RenderObject::hasImmediateNonWhitespaceTextChild() const -{ - for (const RenderObject* r = firstChild(); r; r = r->nextSibling()) { - if (r->isText() && !toRenderText(r)->isAllCollapsibleWhitespace()) - return true; - } - return false; -} - -inline bool RenderObject::shouldRepaintForStyleDifference(StyleDifference diff) const -{ - return diff == StyleDifferenceRepaint || (diff == StyleDifferenceRepaintIfText && hasImmediateNonWhitespaceTextChild()); -} - -void RenderObject::setStyle(PassRefPtr<RenderStyle> style) -{ - if (m_style == style) { -#if USE(ACCELERATED_COMPOSITING) - // We need to run through adjustStyleDifference() for iframes, plugins, and canvas so - // style sharing is disabled for them. That should ensure that we never hit this code path. - ASSERT(!isRenderIFrame() && !isEmbeddedObject() && !isCanvas()); -#endif - return; - } + 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); - StyleDifference diff = StyleDifferenceEqual; - unsigned contextSensitiveProperties = ContextSensitivePropertyNone; - if (m_style) - diff = m_style->diff(style.get(), contextSensitiveProperties); + if (hasOverflowClip()) + fputc('O', stderr); + else + fputc('-', stderr); - diff = adjustStyleDifference(diff, contextSensitiveProperties); + if (isAnonymous()) + fputc('A', stderr); + else + fputc('-', stderr); - styleWillChange(diff, style.get()); - - RefPtr<RenderStyle> oldStyle = m_style.release(); - setStyleInternal(style); + if (isPseudoElement() || isAnonymous()) + fputc('G', stderr); + else + fputc('-', stderr); - updateFillImages(oldStyle ? oldStyle->backgroundLayers() : 0, m_style ? m_style->backgroundLayers() : 0); - updateFillImages(oldStyle ? oldStyle->maskLayers() : 0, m_style ? m_style->maskLayers() : 0); + if (isFloating()) + fputc('F', stderr); + else + fputc('-', stderr); - updateImage(oldStyle ? oldStyle->borderImage().image() : 0, m_style ? m_style->borderImage().image() : 0); - updateImage(oldStyle ? oldStyle->maskBoxImage().image() : 0, m_style ? m_style->maskBoxImage().image() : 0); + if (hasLayer()) + fputc('L', stderr); + else + fputc('-', stderr); - // We need to ensure that view->maximalOutlineSize() is valid for any repaints that happen - // during styleDidChange (it's used by clippedOverflowRectForRepaint()). - if (m_style->outlineWidth() > 0 && m_style->outlineSize() > maximalOutlineSize(PaintPhaseOutline)) - toRenderView(document()->renderer())->setMaximalOutlineSize(m_style->outlineSize()); + if (isComposited()) + fputc('C', stderr); + else + fputc('-', stderr); - bool doesNotNeedLayout = !m_parent || isText(); + fputc(' ', stderr); - styleDidChange(diff, oldStyle.get()); + if (needsLayout()) + fputc('D', stderr); + else + fputc('-', stderr); - // FIXME: |this| might be destroyed here. This can currently happen for a RenderTextFragment when - // its first-letter block gets an update in RenderTextFragment::styleDidChange. For RenderTextFragment(s), - // we will safely bail out with the doesNotNeedLayout flag. We might want to broaden this condition - // in the future as we move renderer changes out of layout and into style changes. - if (doesNotNeedLayout) - return; + if (node() && node()->needsStyleRecalc()) + fputc('S', stderr); + else + fputc('-', stderr); - // Now that the layer (if any) has been updated, we need to adjust the diff again, - // check whether we should layout now, and decide if we need to repaint. - StyleDifference updatedDiff = adjustStyleDifference(diff, contextSensitiveProperties); - - if (diff <= StyleDifferenceLayoutPositionedMovementOnly) { - if (updatedDiff == StyleDifferenceLayout) - setNeedsLayoutAndPrefWidthsRecalc(); - else if (updatedDiff == StyleDifferenceLayoutPositionedMovementOnly) - setNeedsPositionedMovementLayout(oldStyle.get()); - else if (updatedDiff == StyleDifferenceSimplifiedLayoutAndPositionedMovement) { - setNeedsPositionedMovementLayout(oldStyle.get()); - setNeedsSimplifiedNormalFlowLayout(); - } else if (updatedDiff == StyleDifferenceSimplifiedLayout) - setNeedsSimplifiedNormalFlowLayout(); + int printedCharacters = 0; + if (mark) { + fprintf(stderr, "*"); + ++printedCharacters; } - if (updatedDiff == StyleDifferenceRepaintLayer || shouldRepaintForStyleDifference(updatedDiff)) { - // Do a repaint with the new style now, e.g., for example if we go from - // not having an outline to having an outline. - repaint(); - } -} - -static inline bool rendererHasBackground(const RenderObject* renderer) -{ - return renderer && renderer->hasBackground(); -} + while (++printedCharacters <= depth * 2) + fputc(' ', stderr); -void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) -{ - if (m_style) { - // If our z-index changes value or our visibility changes, - // we need to dirty our stacking context's z-order list. - if (newStyle) { - bool visibilityChanged = m_style->visibility() != newStyle->visibility() - || m_style->zIndex() != newStyle->zIndex() - || m_style->hasAutoZIndex() != newStyle->hasAutoZIndex(); -#if ENABLE(DASHBOARD_SUPPORT) || ENABLE(DRAGGABLE_REGION) - if (visibilityChanged) - document()->setAnnotatedRegionsDirty(true); -#endif - if (visibilityChanged) { - if (AXObjectCache* cache = document()->existingAXObjectCache()) - cache->childrenChanged(parent()); - } + if (node()) + fprintf(stderr, "%s ", node()->nodeName().utf8().data()); - // Keep layer hierarchy visibility bits up to date if visibility changes. - if (m_style->visibility() != newStyle->visibility()) { - if (RenderLayer* l = enclosingLayer()) { - if (newStyle->visibility() == VISIBLE) - l->setHasVisibleContent(); - else if (l->hasVisibleContent() && (this == l->renderer() || l->renderer()->style()->visibility() != VISIBLE)) { - l->dirtyVisibleContentStatus(); - if (diff > StyleDifferenceRepaintLayer) - repaint(); - } - } - } - } + String name = renderName(); + // FIXME: Renderer's name should not include property value listing. + int pos = name.find('('); + if (pos > 0) + fprintf(stderr, "%s", name.left(pos - 1).utf8().data()); + else + fprintf(stderr, "%s", name.utf8().data()); - if (m_parent && (newStyle->outlineSize() < m_style->outlineSize() || shouldRepaintForStyleDifference(diff))) - repaint(); - if (isFloating() && (m_style->floating() != newStyle->floating())) - // For changes in float styles, we need to conceivably remove ourselves - // from the floating objects list. - toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists(); - else if (isOutOfFlowPositioned() && (m_style->position() != newStyle->position())) - // For changes in positioning styles, we need to conceivably remove ourselves - // from the positioned objects list. - toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists(); - - s_affectsParentBlock = isFloatingOrOutOfFlowPositioned() - && (!newStyle->isFloating() && !newStyle->hasOutOfFlowPosition()) - && parent() && (parent()->isBlockFlow() || parent()->isRenderInline()); - - s_noLongerAffectsParentBlock = ((!isFloating() && newStyle->isFloating()) || (!isOutOfFlowPositioned() && newStyle->hasOutOfFlowPosition())) - && parent() && parent()->isRenderBlock(); - - // reset style flags - if (diff == StyleDifferenceLayout || diff == StyleDifferenceLayoutPositionedMovementOnly) { - setFloating(false); - clearPositionedState(); - } - setHorizontalWritingMode(true); - setHasBoxDecorations(false); - setHasOverflowClip(false); - setHasTransform(false); - setHasReflection(false); - } else { - s_affectsParentBlock = false; - s_noLongerAffectsParentBlock = false; + 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()); } - if (FrameView* frameView = view()->frameView()) { - bool repaintFixedBackgroundsOnScroll = shouldRepaintFixedBackgroundsOnScroll(frameView); - - bool newStyleSlowScroll = newStyle && repaintFixedBackgroundsOnScroll && newStyle->hasFixedBackgroundImage(); - bool oldStyleSlowScroll = m_style && repaintFixedBackgroundsOnScroll && m_style->hasFixedBackgroundImage(); - -#if USE(ACCELERATED_COMPOSITING) - bool drawsRootBackground = isRoot() || (isBody() && !rendererHasBackground(document()->documentElement()->renderer())); - if (drawsRootBackground && repaintFixedBackgroundsOnScroll) { - if (view()->compositor()->supportsFixedRootBackgroundCompositing()) { - if (newStyleSlowScroll && newStyle->hasEntirelyFixedBackground()) - newStyleSlowScroll = false; - - if (oldStyleSlowScroll && m_style->hasEntirelyFixedBackground()) - oldStyleSlowScroll = false; - } - } -#endif - if (oldStyleSlowScroll != newStyleSlowScroll) { - if (oldStyleSlowScroll) - frameView->removeSlowRepaintObject(this); - - if (newStyleSlowScroll) - frameView->addSlowRepaintObject(this); + 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()); } } -} - -static bool areNonIdenticalCursorListsEqual(const RenderStyle* a, const RenderStyle* b) -{ - ASSERT(a->cursors() != b->cursors()); - return a->cursors() && b->cursors() && *a->cursors() == *b->cursors(); -} -static inline bool areCursorsEqual(const RenderStyle* a, const RenderStyle* b) -{ - return a->cursor() == b->cursor() && (a->cursors() == b->cursors() || areNonIdenticalCursorListsEqual(a, b)); + showRegionsInformation(); + fprintf(stderr, "\n"); } -void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +void RenderObject::showRenderSubTreeAndMark(const RenderObject* markedObject, int depth) const { - if (s_affectsParentBlock) - handleDynamicFloatPositionChange(); - - if (s_noLongerAffectsParentBlock) - removeAnonymousWrappersForInlinesIfNecessary(); -#if ENABLE(SVG) - SVGRenderSupport::styleChanged(this); -#endif - - if (!m_parent) - return; - - if (diff == StyleDifferenceLayout || diff == StyleDifferenceSimplifiedLayout) { - RenderCounter::rendererStyleChanged(this, oldStyle, m_style.get()); + showRenderObject(markedObject == this, depth); + if (is<RenderBlockFlow>(*this)) + downcast<RenderBlockFlow>(*this).showLineTreeAndMark(nullptr, depth + 1); - // If the object already needs layout, then setNeedsLayout won't do - // any work. But if the containing block has changed, then we may need - // to mark the new containing blocks for layout. The change that can - // directly affect the containing block of this object is a change to - // the position style. - if (needsLayout() && oldStyle->position() != m_style->position()) - markContainingBlocksForLayout(); - - if (diff == StyleDifferenceLayout) - setNeedsLayoutAndPrefWidthsRecalc(); - else - setNeedsSimplifiedNormalFlowLayout(); - } else if (diff == StyleDifferenceSimplifiedLayoutAndPositionedMovement) { - setNeedsPositionedMovementLayout(oldStyle); - setNeedsSimplifiedNormalFlowLayout(); - } else if (diff == StyleDifferenceLayoutPositionedMovementOnly) - setNeedsPositionedMovementLayout(oldStyle); - - // Don't check for repaint here; we need to wait until the layer has been - // updated by subclasses before we know if we have to repaint (in setStyle()). - - if (oldStyle && !areCursorsEqual(oldStyle, style())) { - if (Frame* frame = this->frame()) - frame->eventHandler()->scheduleCursorUpdate(); - } + for (const RenderObject* child = firstChildSlow(); child; child = child->nextSibling()) + child->showRenderSubTreeAndMark(markedObject, depth + 1); } -void RenderObject::propagateStyleToAnonymousChildren(bool blockChildrenOnly) -{ - // FIXME: We could save this call when the change only affected non-inherited properties. - for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { - if (!child->isAnonymous() || child->style()->styleType() != NOPSEUDO) - continue; - - if (blockChildrenOnly && !child->isRenderBlock()) - continue; - -#if ENABLE(FULLSCREEN_API) - if (child->isRenderFullScreen() || child->isRenderFullScreenPlaceholder()) - continue; -#endif - - RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), child->style()->display()); - if (style()->specifiesColumns()) { - if (child->style()->specifiesColumns()) - newStyle->inheritColumnPropertiesFrom(style()); - if (child->style()->columnSpan()) - newStyle->setColumnSpan(ColumnSpanAll); - } - - // Preserve the position style of anonymous block continuations as they can have relative or sticky position when - // they contain block descendants of relative or sticky positioned inlines. - if (child->isInFlowPositioned() && toRenderBlock(child)->isAnonymousBlockContinuation()) - newStyle->setPosition(child->style()->position()); - - child->setStyle(newStyle.release()); - } -} +#endif // NDEBUG -void RenderObject::updateFillImages(const FillLayer* oldLayers, const FillLayer* newLayers) +SelectionSubtreeRoot& RenderObject::selectionRoot() const { - // Optimize the common case - if (oldLayers && !oldLayers->next() && newLayers && !newLayers->next() && (oldLayers->image() == newLayers->image())) - return; - - // Go through the new layers and addClients first, to avoid removing all clients of an image. - for (const FillLayer* currNew = newLayers; currNew; currNew = currNew->next()) { - if (currNew->image()) - currNew->image()->addClient(this); - } + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (!flowThread) + return view(); - for (const FillLayer* currOld = oldLayers; currOld; currOld = currOld->next()) { - if (currOld->image()) - currOld->image()->removeClient(this); - } -} - -void RenderObject::updateImage(StyleImage* oldImage, StyleImage* newImage) -{ - if (oldImage != newImage) { - if (oldImage) - oldImage->removeClient(this); - if (newImage) - newImage->addClient(this); + if (is<RenderNamedFlowThread>(*flowThread)) + return downcast<RenderNamedFlowThread>(*flowThread); + if (is<RenderMultiColumnFlowThread>(*flowThread)) { + if (!flowThread->containingBlock()) + return view(); + return flowThread->containingBlock()->selectionRoot(); } + ASSERT_NOT_REACHED(); + return view(); } -LayoutRect RenderObject::viewRect() const +void RenderObject::selectionStartEnd(int& spos, int& epos) const { - return view()->viewRect(); + selectionRoot().selectionData().selectionStartEndPositions(spos, epos); } -FloatPoint RenderObject::localToAbsolute(const FloatPoint& localPoint, MapCoordinatesFlags mode) const +FloatPoint RenderObject::localToAbsolute(const FloatPoint& localPoint, MapCoordinatesFlags mode, bool* wasFixed) const { TransformState transformState(TransformState::ApplyTransformDirection, localPoint); - mapLocalToContainer(0, transformState, mode | ApplyContainerFlip); + mapLocalToContainer(nullptr, transformState, mode | ApplyContainerFlip, wasFixed); transformState.flatten(); return transformState.lastPlanarPoint(); @@ -2138,63 +1313,55 @@ void RenderObject::mapLocalToContainer(const RenderLayerModelObject* repaintCont if (repaintContainer == this) return; - RenderObject* o = parent(); - if (!o) + auto* parent = this->parent(); + if (!parent) return; // FIXME: this should call offsetFromContainer to share code, but I'm not sure it's ever called. - LayoutPoint centerPoint = roundedLayoutPoint(transformState.mappedPoint()); - if (mode & ApplyContainerFlip && o->isBox()) { - if (o->style()->isFlippedBlocksWritingMode()) - transformState.move(toRenderBox(o)->flipForWritingModeIncludingColumns(roundedLayoutPoint(transformState.mappedPoint())) - centerPoint); + LayoutPoint centerPoint(transformState.mappedPoint()); + if (mode & ApplyContainerFlip && is<RenderBox>(*parent)) { + if (parent->style().isFlippedBlocksWritingMode()) + transformState.move(downcast<RenderBox>(parent)->flipForWritingMode(LayoutPoint(transformState.mappedPoint())) - centerPoint); mode &= ~ApplyContainerFlip; } - LayoutSize columnOffset; - o->adjustForColumns(columnOffset, roundedLayoutPoint(transformState.mappedPoint())); - if (!columnOffset.isZero()) - transformState.move(columnOffset); - - if (o->hasOverflowClip()) - transformState.move(-toRenderBox(o)->scrolledContentOffset()); + if (is<RenderBox>(*parent)) + transformState.move(-downcast<RenderBox>(*parent).scrolledContentOffset()); - o->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed); + parent->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed); } const RenderObject* RenderObject::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const { ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != this); - RenderObject* container = parent(); + auto* container = parent(); if (!container) - return 0; + return nullptr; // FIXME: this should call offsetFromContainer to share code, but I'm not sure it's ever called. LayoutSize offset; - if (container->hasOverflowClip()) - offset = -toRenderBox(container)->scrolledContentOffset(); + if (is<RenderBox>(*container)) + offset = -downcast<RenderBox>(*container).scrolledContentOffset(); - geometryMap.push(this, offset, hasColumns()); + geometryMap.push(this, offset, false); return container; } void RenderObject::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const { - RenderObject* o = parent(); - if (o) { - o->mapAbsoluteToLocalPoint(mode, transformState); - if (o->hasOverflowClip()) - transformState.move(toRenderBox(o)->scrolledContentOffset()); + if (auto* parent = this->parent()) { + parent->mapAbsoluteToLocalPoint(mode, transformState); + if (is<RenderBox>(*parent)) + transformState.move(downcast<RenderBox>(*parent).scrolledContentOffset()); } } bool RenderObject::shouldUseTransformFromContainer(const RenderObject* containerObject) const { -#if ENABLE(3D_RENDERING) - // hasTransform() indicates whether the object has transform, transform-style or perspective. We just care about transform, - // so check the layer's transform directly. - return (hasLayer() && toRenderLayerModelObject(this)->layer()->transform()) || (containerObject && containerObject->style()->hasPerspective()); +#if ENABLE(3D_TRANSFORMS) + return hasTransform() || (containerObject && containerObject->style().hasPerspective()); #else UNUSED_PARAM(containerObject); return hasTransform(); @@ -2206,17 +1373,17 @@ void RenderObject::getTransformFromContainer(const RenderObject* containerObject transform.makeIdentity(); transform.translate(offsetInContainer.width(), offsetInContainer.height()); RenderLayer* layer; - if (hasLayer() && (layer = toRenderLayerModelObject(this)->layer()) && layer->transform()) + if (hasLayer() && (layer = downcast<RenderLayerModelObject>(*this).layer()) && layer->transform()) transform.multiply(layer->currentTransform()); -#if ENABLE(3D_RENDERING) - if (containerObject && containerObject->hasLayer() && containerObject->style()->hasPerspective()) { +#if ENABLE(3D_TRANSFORMS) + if (containerObject && containerObject->hasLayer() && containerObject->style().hasPerspective()) { // Perpsective on the container affects us, so we have to factor it in here. ASSERT(containerObject->hasLayer()); - FloatPoint perspectiveOrigin = toRenderLayerModelObject(containerObject)->layer()->perspectiveOrigin(); + FloatPoint perspectiveOrigin = downcast<RenderLayerModelObject>(*containerObject).layer()->perspectiveOrigin(); TransformationMatrix perspectiveMatrix; - perspectiveMatrix.applyPerspective(containerObject->style()->perspective()); + perspectiveMatrix.applyPerspective(containerObject->style().perspective()); transform.translateRight3d(-perspectiveOrigin.x(), -perspectiveOrigin.y(), 0); transform = perspectiveMatrix * transform; @@ -2232,7 +1399,7 @@ FloatQuad RenderObject::localToContainerQuad(const FloatQuad& localQuad, const R // Track the point at the center of the quad's bounding box. As mapLocalToContainer() calls offsetFromContainer(), // it will use that point as the reference point to decide which column's transform to apply in multiple-column blocks. TransformState transformState(TransformState::ApplyTransformDirection, localQuad.boundingBox().center(), localQuad); - mapLocalToContainer(repaintContainer, transformState, mode | ApplyContainerFlip | UseTransforms, wasFixed); + mapLocalToContainer(repaintContainer, transformState, mode | ApplyContainerFlip, wasFixed); transformState.flatten(); return transformState.lastPlanarQuad(); @@ -2241,45 +1408,42 @@ FloatQuad RenderObject::localToContainerQuad(const FloatQuad& localQuad, const R FloatPoint RenderObject::localToContainerPoint(const FloatPoint& localPoint, const RenderLayerModelObject* repaintContainer, MapCoordinatesFlags mode, bool* wasFixed) const { TransformState transformState(TransformState::ApplyTransformDirection, localPoint); - mapLocalToContainer(repaintContainer, transformState, mode | ApplyContainerFlip | UseTransforms, wasFixed); + mapLocalToContainer(repaintContainer, transformState, mode | ApplyContainerFlip, wasFixed); transformState.flatten(); return transformState.lastPlanarPoint(); } -LayoutSize RenderObject::offsetFromContainer(RenderObject* o, const LayoutPoint& point, bool* offsetDependsOnPoint) const +LayoutSize RenderObject::offsetFromContainer(RenderElement& container, const LayoutPoint&, bool* offsetDependsOnPoint) const { - ASSERT(o == container()); + ASSERT(&container == this->container()); LayoutSize offset; - - o->adjustForColumns(offset, point); - - if (o->hasOverflowClip()) - offset -= toRenderBox(o)->scrolledContentOffset(); + if (is<RenderBox>(container)) + offset -= downcast<RenderBox>(container).scrolledContentOffset(); if (offsetDependsOnPoint) - *offsetDependsOnPoint = hasColumns() || o->isRenderFlowThread(); + *offsetDependsOnPoint = is<RenderFlowThread>(container); return offset; } -LayoutSize RenderObject::offsetFromAncestorContainer(RenderObject* container) const +LayoutSize RenderObject::offsetFromAncestorContainer(RenderElement& container) const { LayoutSize offset; LayoutPoint referencePoint; const RenderObject* currContainer = this; do { - RenderObject* nextContainer = currContainer->container(); + RenderElement* nextContainer = currContainer->container(); ASSERT(nextContainer); // This means we reached the top without finding container. if (!nextContainer) break; ASSERT(!currContainer->hasTransform()); - LayoutSize currentOffset = currContainer->offsetFromContainer(nextContainer, referencePoint); + LayoutSize currentOffset = currContainer->offsetFromContainer(*nextContainer, referencePoint); offset += currentOffset; referencePoint.move(currentOffset); currContainer = nextContainer; - } while (currContainer != container); + } while (currContainer != &container); return offset; } @@ -2292,61 +1456,34 @@ LayoutRect RenderObject::localCaretRect(InlineBox*, int, LayoutUnit* extraWidthT return LayoutRect(); } -bool RenderObject::isRooted(RenderView** view) const -{ - const RenderObject* o = this; - while (o->parent()) - o = o->parent(); - - if (!o->isRenderView()) - return false; - - if (view) - *view = const_cast<RenderView*>(toRenderView(o)); - - return true; -} - -RenderObject* RenderObject::rendererForRootBackground() +bool RenderObject::isRooted() const { - ASSERT(isRoot()); - if (!hasBackground() && node() && node()->hasTagName(HTMLNames::htmlTag)) { - // Locate the <body> element using the DOM. This is easier than trying - // to crawl around a render tree with potential :before/:after content and - // anonymous blocks created by inline <body> tags etc. We can locate the <body> - // render object very easily via the DOM. - HTMLElement* body = document()->body(); - RenderObject* bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0; - if (bodyObject) - return bodyObject; - } - - return this; + return isDescendantOf(&view()); } RespectImageOrientationEnum RenderObject::shouldRespectImageOrientation() const { +#if USE(CG) || USE(CAIRO) + // This can only be enabled for ports which honor the orientation flag in their drawing code. + if (document().isImageDocument()) + return RespectImageOrientation; +#endif // Respect the image's orientation if it's being used as a full-page image or it's // an <img> and the setting to respect it everywhere is set. - return -#if USE(CG) || USE(CAIRO) || PLATFORM(BLACKBERRY) - // This can only be enabled for ports which honor the orientation flag in their drawing code. - document()->isImageDocument() || -#endif - (document()->settings() && document()->settings()->shouldRespectImageOrientation() && node() && isHTMLImageElement(node())) ? RespectImageOrientation : DoNotRespectImageOrientation; + return (frame().settings().shouldRespectImageOrientation() && is<HTMLImageElement>(node())) ? RespectImageOrientation : DoNotRespectImageOrientation; } bool RenderObject::hasOutlineAnnotation() const { - return node() && node()->isLink() && document()->printing(); + return node() && node()->isLink() && document().printing(); } bool RenderObject::hasEntirelyFixedBackground() const { - return m_style->hasEntirelyFixedBackground(); + return style().hasEntirelyFixedBackground(); } -RenderObject* RenderObject::container(const RenderLayerModelObject* repaintContainer, bool* repaintContainerSkipped) const +RenderElement* RenderObject::container(const RenderLayerModelObject* repaintContainer, bool* repaintContainerSkipped) const { if (repaintContainerSkipped) *repaintContainerSkipped = false; @@ -2360,27 +1497,27 @@ RenderObject* RenderObject::container(const RenderLayerModelObject* repaintConta // containingBlock() simply skips relpositioned inlines and lets an enclosing block handle // the layout of the positioned object. This does mean that computePositionedLogicalWidth and // computePositionedLogicalHeight have to use container(). - RenderObject* o = parent(); + auto o = parent(); if (isText()) return o; - EPosition pos = m_style->position(); + EPosition pos = style().position(); if (pos == FixedPosition) { // container() can be called on an object that is not in the // tree yet. We don't call view() since it will assert if it // can't get back to the canvas. Instead we just walk as high up // as we can. If we're in the tree, we'll get the root. If we // aren't we'll get the root of our little subtree (most likely - // we'll just return 0). + // we'll just return nullptr). // 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()) @@ -2395,11 +1532,12 @@ RenderObject* RenderObject::container(const RenderLayerModelObject* repaintConta // Same goes here. We technically just want our containing block, but // we may not have one if we're part of an uninstalled subtree. We'll // climb as high as we can though. - while (o && o->style()->position() == StaticPosition && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) { -#if ENABLE(SVG) + // 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())) { if (o->isSVGForeignObject()) // foreignObject is the containing block for contents inside it break; -#endif + if (repaintContainerSkipped && o == repaintContainer) *repaintContainerSkipped = true; @@ -2413,181 +1551,159 @@ RenderObject* RenderObject::container(const RenderLayerModelObject* repaintConta bool RenderObject::isSelectionBorder() const { SelectionState st = selectionState(); - return st == SelectionStart || st == SelectionEnd || st == SelectionBoth; -} - -inline void RenderObject::clearLayoutRootIfNeeded() const -{ - if (!documentBeingDestroyed() && frame()) { - if (FrameView* view = frame()->view()) { - if (view->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->clearLayoutRoot(); - } - } - } + return st == SelectionStart + || st == SelectionEnd + || st == SelectionBoth + || view().selectionUnsplitStart() == this + || view().selectionUnsplitEnd() == this; } void RenderObject::willBeDestroyed() { - // Destroy any leftover anonymous children. - RenderObjectChildList* children = virtualChildren(); - if (children) - children->destroyLeftoverChildren(); - - // If this renderer is being autoscrolled, stop the autoscroll timer - - // FIXME: RenderObject::destroy should not get called with a renderer whose document - // has a null frame, so we assert this. However, we don't want release builds to crash which is why we - // check that the frame is not null. - ASSERT(frame()); - if (frame() && frame()->eventHandler()->autoscrollRenderer() == this) - frame()->eventHandler()->stopAutoscrollTimer(true); - - animation()->cancelAnimations(this); - // For accessibility management, notify the parent of the imminent change to its child set. // We do it now, before remove(), while the parent pointer is still available. - if (AXObjectCache* cache = document()->existingAXObjectCache()) + if (AXObjectCache* cache = document().existingAXObjectCache()) cache->childrenChanged(this->parent()); - remove(); + removeFromParent(); - ASSERT(documentBeingDestroyed() || !frame()->view()->hasSlowRepaintObject(this)); + ASSERT(documentBeingDestroyed() || !is<RenderElement>(*this) || !view().frameView().hasSlowRepaintObject(downcast<RenderElement>(*this))); // The remove() call above may invoke axObjectCache()->childrenChanged() on the parent, which may require the AX render // object for this renderer. So we remove the AX render object now, after the renderer is removed. - if (AXObjectCache* cache = document()->existingAXObjectCache()) + if (AXObjectCache* cache = document().existingAXObjectCache()) cache->remove(this); -#ifndef NDEBUG - if (!documentBeingDestroyed() && view() && 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); - toRenderLayerModelObject(this)->destroyLayer(); + downcast<RenderLayerModelObject>(*this).destroyLayer(); } - setAncestorLineBoxDirty(false); - - clearLayoutRootIfNeeded(); + removeRareData(); } void RenderObject::insertedIntoTree() { // FIXME: We should ASSERT(isRooted()) here but generated content makes some out-of-order insertion. - // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children - // and don't have a layer attached to ourselves. - RenderLayer* layer = 0; - if (firstChild() || hasLayer()) { - layer = parent()->enclosingLayer(); - addLayers(layer); - } - - // If |this| is visible but this object was not, tell the layer it has some visible content - // that needs to be drawn and layer visibility optimization can't be used - if (parent()->style()->visibility() != VISIBLE && style()->visibility() == VISIBLE && !hasLayer()) { - if (!layer) - layer = parent()->enclosingLayer(); - if (layer) - layer->setHasVisibleContent(); - } - if (!isFloating() && parent()->childrenInline()) - parent()->dirtyLinesFromChangedChild(this); + parent()->dirtyLinesFromChangedChild(*this); - if (RenderNamedFlowThread* containerFlowThread = parent()->renderNamedFlowThreadWrapper()) - containerFlowThread->addFlowChild(this); + if (RenderFlowThread* flowThread = flowThreadContainingBlock()) + flowThread->flowThreadDescendantInserted(this); } void RenderObject::willBeRemovedFromTree() { // FIXME: We should ASSERT(isRooted()) but we have some out-of-order removals which would need to be fixed first. - if (!isText()) { - if (FrameView* frameView = view()->frameView()) { - bool repaintFixedBackgroundsOnScroll = shouldRepaintFixedBackgroundsOnScroll(frameView); - if (repaintFixedBackgroundsOnScroll && m_style && m_style->hasFixedBackgroundImage()) - frameView->removeSlowRepaintObject(this); - } - } - - // If we remove a visible child from an invisible parent, we don't know the layer visibility any more. - RenderLayer* layer = 0; - if (parent()->style()->visibility() != VISIBLE && style()->visibility() == VISIBLE && !hasLayer()) { - if ((layer = parent()->enclosingLayer())) - layer->dirtyVisibleContentStatus(); - } - - // Keep our layer hierarchy updated. - if (firstChild() || hasLayer()) { - if (!layer) - layer = parent()->enclosingLayer(); - removeLayers(layer); - } - - if (isOutOfFlowPositioned() && parent()->childrenInline()) - parent()->dirtyLinesFromChangedChild(this); - 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. - removeFromRenderFlowThreadRecursive(flowThreadContainingBlock()); + removeFromRenderFlowThreadIncludingDescendants(true); } -void RenderObject::removeFromRenderFlowThreadRecursive(RenderFlowThread* renderFlowThread) +void RenderObject::removeFromRenderFlowThreadIncludingDescendants(bool shouldUpdateState) { - if (const RenderObjectChildList* children = virtualChildren()) { - for (RenderObject* child = children->firstChild(); child; child = child->nextSibling()) - child->removeFromRenderFlowThreadRecursive(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); + + // 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(); } - - 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); + 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(); } void RenderObject::destroyAndCleanupAnonymousWrappers() @@ -2598,62 +1714,41 @@ void RenderObject::destroyAndCleanupAnonymousWrappers() return; } - RenderObject* destroyRoot = this; - for (RenderObject* destroyRootParent = destroyRoot->parent(); destroyRootParent && destroyRootParent->isAnonymous(); destroyRoot = destroyRootParent, destroyRootParent = destroyRootParent->parent()) { - // Currently we only remove anonymous cells' and table sections' wrappers but we should remove all unneeded - // wrappers. See http://webkit.org/b/52123 as an example where this is needed. - if (!destroyRootParent->isTableCell() && !destroyRootParent->isTableSection()) + auto* destroyRoot = this; + auto* destroyRootParent = destroyRoot->parent(); + while (destroyRootParent && destroyRootParent->isAnonymous()) { + if (!destroyRootParent->isTableCell() && !destroyRootParent->isTableRow() + && !destroyRootParent->isTableCaption() && !destroyRootParent->isTableSection() && !destroyRootParent->isTable()) break; - - if (destroyRootParent->firstChild() != this || destroyRootParent->lastChild() != this) + // single child? + if (!(destroyRootParent->firstChild() == destroyRoot && destroyRootParent->lastChild() == destroyRoot)) break; + destroyRoot = destroyRootParent; + destroyRootParent = destroyRootParent->parent(); } - + collapseAnonymousTableRowsIfNeeded(*destroyRoot); destroyRoot->destroy(); - // WARNING: |this| is deleted here. } void RenderObject::destroy() { - willBeDestroyed(); - arenaDelete(renderArena(), this); -} + m_bitfields.setBeingDestroyed(true); -void RenderObject::arenaDelete(RenderArena* arena, void* base) -{ - if (m_style) { - for (const FillLayer* bgLayer = m_style->backgroundLayers(); bgLayer; bgLayer = bgLayer->next()) { - if (StyleImage* backgroundImage = bgLayer->image()) - backgroundImage->removeClient(this); - } - - for (const FillLayer* maskLayer = m_style->maskLayers(); maskLayer; maskLayer = maskLayer->next()) { - if (StyleImage* maskImage = maskLayer->image()) - maskImage->removeClient(this); - } - - if (StyleImage* borderImage = m_style->borderImage().image()) - borderImage->removeClient(this); +#if PLATFORM(IOS) + if (hasLayer()) + downcast<RenderBoxModelObject>(*this).layer()->willBeDestroyed(); +#endif - if (StyleImage* maskBoxImage = m_style->maskBoxImage().image()) - maskBoxImage->removeClient(this); + willBeDestroyed(); + if (is<RenderWidget>(*this)) { + downcast<RenderWidget>(*this).deref(); + return; } - -#ifndef NDEBUG - void* savedBase = baseOfRenderObjectBeingDeleted; - baseOfRenderObjectBeingDeleted = base; -#endif delete this; -#ifndef NDEBUG - baseOfRenderObjectBeingDeleted = savedBase; -#endif - - // Recover the size left there for us by operator delete and free the memory. - arena->free(*(size_t*)base, base); } -VisiblePosition RenderObject::positionForPoint(const LayoutPoint&) +VisiblePosition RenderObject::positionForPoint(const LayoutPoint&, const RenderRegion*) { return createVisiblePosition(caretMinOffset(), DOWNSTREAM); } @@ -2662,15 +1757,20 @@ void RenderObject::updateDragState(bool dragOn) { bool valueChanged = (dragOn != isDragging()); setIsDragging(dragOn); - if (valueChanged && node() && (style()->affectedByDrag() || (node()->isElementNode() && toElement(node())->childrenAffectedByDrag()))) + if (valueChanged && node() && (style().affectedByDrag() || (is<Element>(*node()) && downcast<Element>(*node()).childrenAffectedByDrag()))) node()->setNeedsStyleRecalc(); - for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) + for (RenderObject* curr = firstChildSlow(); curr; curr = curr->nextSibling()) curr->updateDragState(dragOn); } bool RenderObject::isComposited() const { - return hasLayer() && toRenderLayerModelObject(this)->layer()->isComposited(); + return hasLayer() && downcast<RenderLayerModelObject>(*this).layer()->isComposited(); +} + +bool RenderObject::isAnonymousInlineBlock() const +{ + return isAnonymous() && style().display() == INLINE_BLOCK && style().styleType() == NOPSEUDO && isRenderBlockFlow() && !isRubyRun() && !isRubyBase() && !isRuby(parent()); } bool RenderObject::hitTest(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestFilter hitTestFilter) @@ -2706,8 +1806,8 @@ 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 (RenderObject* renderer = parent(); renderer && !node; renderer = renderer->parent()) - node = renderer->node(); + for (auto* renderer = parent(); renderer && !node; renderer = renderer->parent()) + node = renderer->element(); } if (node) { @@ -2723,137 +1823,18 @@ bool RenderObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitT return false; } -void RenderObject::scheduleRelayout() +int RenderObject::innerLineHeight() const { - if (isRenderView()) { - FrameView* view = toRenderView(this)->frameView(); - if (view) - view->scheduleRelayout(); - } else { - if (isRooted()) { - if (RenderView* renderView = view()) { - if (FrameView* frameView = renderView->frameView()) - frameView->scheduleRelayoutOfSubtree(this); - } - } - } -} - -void RenderObject::layout() -{ - StackStats::LayoutCheckPoint layoutCheckPoint; - ASSERT(needsLayout()); - RenderObject* child = firstChild(); - while (child) { - child->layoutIfNeeded(); - ASSERT(!child->needsLayout()); - child = child->nextSibling(); - } - setNeedsLayout(false); -} - -enum StyleCacheState { - Cached, - Uncached -}; - -static PassRefPtr<RenderStyle> firstLineStyleForCachedUncachedType(StyleCacheState type, const RenderObject* renderer, RenderStyle* style) -{ - const RenderObject* rendererForFirstLineStyle = renderer; - if (renderer->isBeforeOrAfterContent()) - rendererForFirstLineStyle = renderer->parent(); - - if (rendererForFirstLineStyle->isBlockFlow()) { - if (RenderBlock* firstLineBlock = rendererForFirstLineStyle->firstLineBlock()) { - if (type == Cached) - return firstLineBlock->getCachedPseudoStyle(FIRST_LINE, style); - return firstLineBlock->getUncachedPseudoStyle(PseudoStyleRequest(FIRST_LINE), style, firstLineBlock == renderer ? style : 0); - } - } else if (!rendererForFirstLineStyle->isAnonymous() && rendererForFirstLineStyle->isRenderInline()) { - RenderStyle* parentStyle = rendererForFirstLineStyle->parent()->firstLineStyle(); - if (parentStyle != rendererForFirstLineStyle->parent()->style()) { - if (type == Cached) { - // A first-line style is in effect. Cache a first-line style for ourselves. - rendererForFirstLineStyle->style()->setHasPseudoStyle(FIRST_LINE_INHERITED); - return rendererForFirstLineStyle->getCachedPseudoStyle(FIRST_LINE_INHERITED, parentStyle); - } - return rendererForFirstLineStyle->getUncachedPseudoStyle(PseudoStyleRequest(FIRST_LINE_INHERITED), parentStyle, style); - } - } - return 0; -} - -PassRefPtr<RenderStyle> RenderObject::uncachedFirstLineStyle(RenderStyle* style) const -{ - if (!document()->styleSheetCollection()->usesFirstLineRules()) - return 0; - - ASSERT(!isText()); - - return firstLineStyleForCachedUncachedType(Uncached, this, style); -} - -RenderStyle* RenderObject::cachedFirstLineStyle() const -{ - ASSERT(document()->styleSheetCollection()->usesFirstLineRules()); - - if (RefPtr<RenderStyle> style = firstLineStyleForCachedUncachedType(Cached, isText() ? parent() : this, m_style.get())) - return style.get(); - - return m_style.get(); -} - -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); + return style().computedLineHeight(); } static Color decorationColor(RenderStyle* style) { Color result; -#if ENABLE(CSS3_TEXT) // Check for text decoration color first. result = style->visitedDependentColor(CSSPropertyWebkitTextDecorationColor); if (result.isValid()) return result; -#endif // CSS3_TEXT if (style->textStrokeWidth() > 0) { // Prefer stroke color if possible but not if it's fully transparent. result = style->visitedDependentColor(CSSPropertyWebkitTextStrokeColor); @@ -2865,70 +1846,75 @@ static Color decorationColor(RenderStyle* style) return result; } -void RenderObject::getTextDecorationColors(int decorations, Color& underline, Color& overline, - Color& linethrough, bool quirksMode, bool firstlineStyle) +void RenderObject::getTextDecorationColorsAndStyles(int decorations, Color& underlineColor, Color& overlineColor, Color& linethroughColor, + TextDecorationStyle& underlineStyle, TextDecorationStyle& overlineStyle, TextDecorationStyle& linethroughStyle, bool firstlineStyle) const { - RenderObject* curr = this; - RenderStyle* styleToUse = 0; + const RenderObject* current = this; + RenderStyle* styleToUse = nullptr; TextDecoration currDecs = TextDecorationNone; Color resultColor; do { - styleToUse = curr->style(firstlineStyle); + styleToUse = firstlineStyle ? ¤t->firstLineStyle() : ¤t->style(); currDecs = styleToUse->textDecoration(); resultColor = decorationColor(styleToUse); // Parameter 'decorations' is cast as an int to enable the bitwise operations below. if (currDecs) { if (currDecs & TextDecorationUnderline) { decorations &= ~TextDecorationUnderline; - underline = resultColor; + underlineColor = resultColor; + underlineStyle = styleToUse->textDecorationStyle(); } if (currDecs & TextDecorationOverline) { decorations &= ~TextDecorationOverline; - overline = resultColor; + overlineColor = resultColor; + overlineStyle = styleToUse->textDecorationStyle(); } if (currDecs & TextDecorationLineThrough) { decorations &= ~TextDecorationLineThrough; - linethrough = resultColor; + linethroughColor = resultColor; + linethroughStyle = styleToUse->textDecorationStyle(); } } - if (curr->isRubyText()) + if (current->isRubyText()) return; - curr = curr->parent(); - if (curr && curr->isAnonymousBlock() && toRenderBlock(curr)->continuation()) - curr = toRenderBlock(curr)->continuation(); - } while (curr && decorations && (!quirksMode || !curr->node() || (!isHTMLAnchorElement(curr->node()) && !curr->node()->hasTagName(fontTag)))); + 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)))); // If we bailed out, use the element we bailed out at (typically a <font> or <a> element). - if (decorations && curr) { - styleToUse = curr->style(firstlineStyle); + if (decorations && current) { + styleToUse = firstlineStyle ? ¤t->firstLineStyle() : ¤t->style(); resultColor = decorationColor(styleToUse); - if (decorations & TextDecorationUnderline) - underline = resultColor; - if (decorations & TextDecorationOverline) - overline = resultColor; - if (decorations & TextDecorationLineThrough) - linethrough = resultColor; + 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 ENABLE(DASHBOARD_SUPPORT) || ENABLE(DRAGGABLE_REGION) +#if ENABLE(DASHBOARD_SUPPORT) void RenderObject::addAnnotatedRegions(Vector<AnnotatedRegionValue>& regions) { // Convert the style regions to absolute coordinates. - if (style()->visibility() != VISIBLE || !isBox()) + if (style().visibility() != VISIBLE || !is<RenderBox>(*this)) return; - RenderBox* box = toRenderBox(this); + auto& box = downcast<RenderBox>(*this); FloatPoint absPos = localToAbsolute(); -#if ENABLE(DASHBOARD_SUPPORT) - const Vector<StyleDashboardRegion>& styleRegions = style()->dashboardRegions(); - unsigned i, count = styleRegions.size(); - for (i = 0; i < count; i++) { - StyleDashboardRegion styleRegion = styleRegions[i]; - - LayoutUnit w = box->width(); - LayoutUnit h = box->height(); + const Vector<StyleDashboardRegion>& styleRegions = style().dashboardRegions(); + for (const auto& styleRegion : styleRegions) { + LayoutUnit w = box.width(); + LayoutUnit h = box.height(); AnnotatedRegionValue region; region.label = styleRegion.label; @@ -2938,8 +1924,7 @@ void RenderObject::addAnnotatedRegions(Vector<AnnotatedRegionValue>& regions) h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value()); region.type = styleRegion.type; - region.clip = region.bounds; - computeAbsoluteRepaintRect(region.clip); + region.clip = computeAbsoluteRepaintRect(region.bounds); if (region.clip.height() < 0) { region.clip.setHeight(0); region.clip.setWidth(0); @@ -2950,51 +1935,21 @@ void RenderObject::addAnnotatedRegions(Vector<AnnotatedRegionValue>& regions) regions.append(region); } -#else // ENABLE(DRAGGABLE_REGION) - if (style()->getDraggableRegionMode() == DraggableRegionNone) - return; - AnnotatedRegionValue region; - region.draggable = style()->getDraggableRegionMode() == DraggableRegionDrag; - region.bounds = LayoutRect(absPos.x(), absPos.y(), box->width(), box->height()); - regions.append(region); -#endif } void RenderObject::collectAnnotatedRegions(Vector<AnnotatedRegionValue>& regions) { // RenderTexts don't have their own style, they just use their parent's style, // so we don't want to include them. - if (isText()) + if (is<RenderText>(*this)) return; addAnnotatedRegions(regions); - for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) - curr->collectAnnotatedRegions(regions); + for (RenderObject* current = downcast<RenderElement>(*this).firstChild(); current; current = current->nextSibling()) + current->collectAnnotatedRegions(regions); } #endif -bool RenderObject::willRenderImage(CachedImage*) -{ - // Without visibility we won't render (and therefore don't care about animation). - if (style()->visibility() != VISIBLE) - return false; - - // We will not render a new image when Active DOM is suspended - if (document()->activeDOMObjectsAreSuspended()) - return false; - - // 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; @@ -3003,7 +1958,7 @@ int RenderObject::caretMinOffset() const int RenderObject::caretMaxOffset() const { if (isReplaced()) - return node() ? max(1U, node()->childNodeCount()) : 1; + return node() ? std::max(1U, node()->countChildNodes()) : 1; if (isHR()) return 1; return 0; @@ -3026,51 +1981,18 @@ int RenderObject::nextOffset(int current) const void RenderObject::adjustRectForOutlineAndShadow(LayoutRect& rect) const { - int outlineSize = outlineStyleForRepaint()->outlineSize(); - if (const ShadowData* boxShadow = style()->boxShadow()) { + LayoutUnit outlineSize = outlineStyleForRepaint().outlineSize(); + if (const ShadowData* boxShadow = style().boxShadow()) { boxShadow->adjustRectForShadow(rect, outlineSize); return; } - rect.inflate(outlineSize); } -AnimationController* RenderObject::animation() const -{ - return frame()->animation(); -} - void RenderObject::imageChanged(CachedImage* image, const IntRect* rect) { imageChanged(static_cast<WrappedImagePtr>(image), rect); } - -RenderObject* RenderObject::hoverAncestor() const -{ - // When searching for the hover ancestor and encountering a named flow thread, - // the search will continue with the DOM ancestor of the top-most element - // in the named flow thread. - // See https://bugs.webkit.org/show_bug.cgi?id=111749 - RenderObject* hoverAncestor = parent(); - - // Skip anonymous blocks directly flowed into flow threads as it would - // prevent us from continuing the search on the DOM tree when reaching the named flow thread. - if (hoverAncestor && hoverAncestor->isAnonymousBlock() && hoverAncestor->parent() && hoverAncestor->parent()->isRenderNamedFlowThread()) - hoverAncestor = hoverAncestor->parent(); - - if (hoverAncestor && hoverAncestor->isRenderNamedFlowThread()) { - hoverAncestor = 0; - - Node* node = this->node(); - if (node) { - Node* domAncestorNode = node->parentNode(); - if (domAncestorNode) - hoverAncestor = domAncestorNode->renderer(); - } - } - - return hoverAncestor; -} RenderBoxModelObject* RenderObject::offsetParent() const { @@ -3078,8 +2000,8 @@ RenderBoxModelObject* RenderObject::offsetParent() const // A is the root element. // A is the HTML body element. // The computed value of the position property for element A is fixed. - if (isRoot() || isBody() || (isOutOfFlowPositioned() && style()->position() == FixedPosition)) - return 0; + if (isDocumentElementRenderer() || isBody() || (isOutOfFlowPositioned() && style().position() == FixedPosition)) + return nullptr; // If A is an area HTML element which has a map HTML element somewhere in the ancestor // chain return the nearest ancestor map HTML element and stop this algorithm. @@ -3094,39 +2016,41 @@ RenderBoxModelObject* RenderObject::offsetParent() const // * Our own extension: if there is a difference in the effective zoom bool skipTables = isPositioned(); - float currZoom = style()->effectiveZoom(); - RenderObject* curr = parent(); - while (curr && (!curr->node() || (!curr->isPositioned() && !curr->isBody())) && !curr->isRenderNamedFlowThread()) { - Node* element = curr->node(); - if (!skipTables && element && (isHTMLTableElement(element) || element->hasTagName(tdTag) || element->hasTagName(thTag))) + 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))) break; - float newZoom = curr->style()->effectiveZoom(); + float newZoom = current->style().effectiveZoom(); if (currZoom != newZoom) break; currZoom = newZoom; - curr = curr->parent(); + current = current->parent(); } // CSS regions specification says that region flows should return the body element as their offsetParent. - if (curr && curr->isRenderNamedFlowThread()) - curr = document()->body() ? document()->body()->renderer() : 0; + if (is<RenderNamedFlowThread>(current)) { + auto* body = document().bodyOrFrameset(); + current = body ? body->renderer() : nullptr; + } - return curr && curr->isBoxModelObject() ? toRenderBoxModelObject(curr) : 0; + return is<RenderBoxModelObject>(current) ? downcast<RenderBoxModelObject>(current) : nullptr; } -VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affinity) +VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affinity) const { // If this is a non-anonymous renderer in an editable area, then it's simple. if (Node* node = nonPseudoNode()) { - if (!node->rendererIsEditable()) { + if (!node->hasEditableStyle()) { // If it can be found, we prefer a visually equivalent position that is editable. Position position = createLegacyEditingPosition(node, offset); Position candidate = position.downstream(CanCrossEditingBoundary); - if (candidate.deprecatedNode()->rendererIsEditable()) + if (candidate.deprecatedNode()->hasEditableStyle()) return VisiblePosition(candidate, affinity); candidate = position.upstream(CanCrossEditingBoundary); - if (candidate.deprecatedNode()->rendererIsEditable()) + if (candidate.deprecatedNode()->hasEditableStyle()) return VisiblePosition(candidate, affinity); } // FIXME: Eliminate legacy editing positions @@ -3139,10 +2063,10 @@ VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affini // find a single non-anonymous renderer. // Find a nearby non-anonymous renderer. - RenderObject* child = this; - while (RenderObject* parent = child->parent()) { + const RenderObject* child = this; + while (const auto parent = child->parent()) { // Find non-anonymous content after. - RenderObject* renderer = child; + const RenderObject* renderer = child; while ((renderer = renderer->nextInPreOrder(parent))) { if (Node* node = renderer->nonPseudoNode()) return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM); @@ -3158,8 +2082,8 @@ VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affini } // Use the parent itself unless it too is anonymous. - if (Node* node = parent->nonPseudoNode()) - return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM); + if (Element* element = parent->nonPseudoElement()) + return VisiblePosition(firstPositionInOrBeforeNode(element), DOWNSTREAM); // Repeat at the next level up. child = parent; @@ -3169,7 +2093,7 @@ VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affini return VisiblePosition(); } -VisiblePosition RenderObject::createVisiblePosition(const Position& position) +VisiblePosition RenderObject::createVisiblePosition(const Position& position) const { if (position.isNotNull()) return VisiblePosition(position); @@ -3201,22 +2125,14 @@ bool RenderObject::canHaveGeneratedChildren() const return canHaveChildren(); } -bool RenderObject::canBeReplacedWithInlineRunIn() const -{ - return true; -} - -#if ENABLE(SVG) - -RenderSVGResourceContainer* RenderObject::toRenderSVGResourceContainer() +Node* RenderObject::generatingPseudoHostElement() const { - ASSERT_NOT_REACHED(); - return 0; + return downcast<PseudoElement>(*node()).hostElement(); } void RenderObject::setNeedsBoundariesUpdate() { - if (RenderObject* renderer = parent()) + if (auto renderer = parent()) renderer->setNeedsBoundariesUpdate(); } @@ -3258,37 +2174,144 @@ bool RenderObject::nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const return false; } -#endif // ENABLE(SVG) +RenderNamedFlowFragment* RenderObject::currentRenderNamedFlowFragment() const +{ + RenderFlowThread* flowThread = flowThreadContainingBlock(); + if (!is<RenderNamedFlowThread>(flowThread)) + return nullptr; -} // namespace WebCore + // FIXME: Once regions are fully integrated with the compositing system we should uncomment this assert. + // This assert needs to be disabled because it's possible to ask for the ancestor clipping rectangle of + // a layer without knowing the containing region in advance. + // ASSERT(flowThread->currentRegion() && flowThread->currentRegion()->isRenderNamedFlowFragment()); -#ifndef NDEBUG + return downcast<RenderNamedFlowFragment>(flowThread->currentRegion()); +} -void showTree(const WebCore::RenderObject* object) +RenderFlowThread* RenderObject::locateFlowThreadContainingBlock() const { - if (object) - object->showTreeForThis(); + RenderBlock* containingBlock = this->containingBlock(); + return containingBlock ? containingBlock->flowThreadContainingBlock() : nullptr; } -void showLineTree(const WebCore::RenderObject* object) +void RenderObject::calculateBorderStyleColor(const EBorderStyle& style, const BoxSide& side, Color& color) +{ + ASSERT(style == INSET || style == OUTSET); + // This values were derived empirically. + const RGBA32 baseDarkColor = 0xFF202020; + const RGBA32 baseLightColor = 0xFFEBEBEB; + enum Operation { Darken, Lighten }; + + Operation operation = (side == BSTop || side == BSLeft) == (style == INSET) ? Darken : Lighten; + + // Here we will darken the border decoration color when needed. This will yield a similar behavior as in FF. + if (operation == Darken) { + if (differenceSquared(color, Color::black) > differenceSquared(baseDarkColor, Color::black)) + color = color.dark(); + } else { + if (differenceSquared(color, Color::white) > differenceSquared(baseLightColor, Color::white)) + color = color.light(); + } +} + +void RenderObject::setIsDragging(bool isDragging) +{ + if (isDragging || hasRareData()) + ensureRareData().setIsDragging(isDragging); +} + +void RenderObject::setHasReflection(bool hasReflection) +{ + if (hasReflection || hasRareData()) + ensureRareData().setHasReflection(hasReflection); +} + +void RenderObject::setIsRenderFlowThread(bool isFlowThread) +{ + if (isFlowThread || hasRareData()) + ensureRareData().setIsRenderFlowThread(isFlowThread); +} + +void RenderObject::setHasOutlineAutoAncestor(bool hasOutlineAutoAncestor) +{ + if (hasOutlineAutoAncestor || hasRareData()) + ensureRareData().setHasOutlineAutoAncestor(hasOutlineAutoAncestor); +} + +void RenderObject::setIsRegisteredForVisibleInViewportCallback(bool registered) +{ + if (registered || hasRareData()) + ensureRareData().setIsRegisteredForVisibleInViewportCallback(registered); +} + +void RenderObject::setVisibleInViewportState(VisibleInViewportState visible) +{ + if (visible != VisibilityUnknown || hasRareData()) + ensureRareData().setVisibleInViewportState(visible); +} + +RenderObject::RareDataHash& RenderObject::rareDataMap() +{ + static NeverDestroyed<RareDataHash> map; + return map; +} + +RenderObject::RenderObjectRareData RenderObject::rareData() const +{ + if (!hasRareData()) + return RenderObjectRareData(); + + return rareDataMap().get(this); +} + +RenderObject::RenderObjectRareData& RenderObject::ensureRareData() { - if (object) - object->showLineTreeForThis(); + setHasRareData(true); + return rareDataMap().add(this, RenderObjectRareData()).iterator->value; } -void showRenderTree(const WebCore::RenderObject* object1) +void RenderObject::removeRareData() { - showRenderTree(object1, 0); + rareDataMap().remove(this); + setHasRareData(false); } -void showRenderTree(const WebCore::RenderObject* object1, const WebCore::RenderObject* object2) +#ifndef NDEBUG +void printRenderTreeForLiveDocuments() { - if (object1) { - const WebCore::RenderObject* root = object1; - while (root->parent()) - root = root->parent(); - root->showRenderTreeAndMark(object1, "*", object2, "-", 0); + 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()); } } +#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 |