summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/RenderObject.cpp
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@qt.io>2017-05-30 12:48:17 +0200
committerOswald Buddenhagen <oswald.buddenhagen@qt.io>2017-05-30 12:48:17 +0200
commit881da28418d380042aa95a97f0cbd42560a64f7c (patch)
treea794dff3274695e99c651902dde93d934ea7a5af /Source/WebCore/rendering/RenderObject.cpp
parent7e104c57a70fdf551bb3d22a5d637cdcbc69dbea (diff)
parent0fcedcd17cc00d3dd44c718b3cb36c1033319671 (diff)
downloadqtwebkit-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.cpp3011
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 ? &current->firstLineStyle() : &current->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 ? &current->firstLineStyle() : &current->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