summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/RenderElement.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/rendering/RenderElement.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/rendering/RenderElement.cpp')
-rw-r--r--Source/WebCore/rendering/RenderElement.cpp1816
1 files changed, 1415 insertions, 401 deletions
diff --git a/Source/WebCore/rendering/RenderElement.cpp b/Source/WebCore/rendering/RenderElement.cpp
index 108791db3..f8c02d4da 100644
--- a/Source/WebCore/rendering/RenderElement.cpp
+++ b/Source/WebCore/rendering/RenderElement.cpp
@@ -3,7 +3,7 @@
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
* (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
- * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2013, 2015 Apple Inc. All rights reserved.
* Copyright (C) 2010, 2012 Google Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
@@ -26,265 +26,267 @@
#include "RenderElement.h"
#include "AXObjectCache.h"
-#include "AnimationController.h"
#include "ContentData.h"
#include "CursorList.h"
+#include "ElementChildIterator.h"
#include "EventHandler.h"
+#include "FlowThreadController.h"
+#include "FocusController.h"
#include "Frame.h"
-#include "HTMLElement.h"
+#include "FrameSelection.h"
+#include "HTMLAnchorElement.h"
+#include "HTMLBodyElement.h"
+#include "HTMLHtmlElement.h"
+#include "HTMLImageElement.h"
#include "HTMLNames.h"
+#include "Logging.h"
+#include "Page.h"
+#include "PathUtilities.h"
+#include "RenderBlock.h"
+#include "RenderChildIterator.h"
#include "RenderCounter.h"
#include "RenderDeprecatedFlexibleBox.h"
+#include "RenderDescendantIterator.h"
#include "RenderFlexibleBox.h"
-#include "RenderGrid.h"
#include "RenderImage.h"
#include "RenderImageResourceStyleImage.h"
+#include "RenderInline.h"
#include "RenderIterator.h"
#include "RenderLayer.h"
+#include "RenderLayerCompositor.h"
#include "RenderLineBreak.h"
#include "RenderListItem.h"
+#if !ASSERT_DISABLED
+#include "RenderListMarker.h"
+#endif
+#include "RenderNamedFlowThread.h"
#include "RenderRegion.h"
-#include "RenderRuby.h"
-#include "RenderRubyText.h"
#include "RenderTableCaption.h"
#include "RenderTableCell.h"
#include "RenderTableCol.h"
#include "RenderTableRow.h"
#include "RenderText.h"
+#include "RenderTheme.h"
#include "RenderView.h"
#include "SVGRenderSupport.h"
+#include "Settings.h"
+#include "ShadowRoot.h"
+#include "StylePendingResources.h"
#include "StyleResolver.h"
+#include <wtf/MathExtras.h>
#include <wtf/StackStats.h>
-#if USE(ACCELERATED_COMPOSITING)
-#include "RenderLayerCompositor.h"
-#endif
+#include "RenderGrid.h"
namespace WebCore {
+struct SameSizeAsRenderElement : public RenderObject {
+ uint32_t bitfields;
+ void* firstChild;
+ void* lastChild;
+ RenderStyle style;
+#if !ASSERT_DISABLED
+ bool reparentingChild;
+#endif
+};
+
+static_assert(sizeof(RenderElement) == sizeof(SameSizeAsRenderElement), "RenderElement should stay small");
+
bool RenderElement::s_affectsParentBlock = false;
bool RenderElement::s_noLongerAffectsParentBlock = false;
-
-RenderElement::RenderElement(Element& element, PassRef<RenderStyle> style, unsigned baseTypeFlags)
- : RenderObject(element)
+
+inline RenderElement::RenderElement(ContainerNode& elementOrDocument, RenderStyle&& style, BaseTypeFlags baseTypeFlags)
+ : RenderObject(elementOrDocument)
, m_baseTypeFlags(baseTypeFlags)
, m_ancestorLineBoxDirty(false)
, m_hasInitializedStyle(false)
+ , m_hasInitialAnimatedStyle(false)
, m_renderInlineAlwaysCreatesLineBoxes(false)
+ , m_renderBoxNeedsLazyRepaint(false)
+ , m_hasPausedImageAnimations(false)
+ , m_hasCounterNodeMap(false)
+ , m_isCSSAnimating(false)
+ , m_hasContinuation(false)
+ , m_hasValidCachedFirstLineStyle(false)
+ , m_renderBlockHasMarginBeforeQuirk(false)
+ , m_renderBlockHasMarginAfterQuirk(false)
+ , m_renderBlockShouldForceRelayoutChildren(false)
+ , m_renderBlockFlowHasMarkupTruncation(false)
+ , m_renderBlockFlowLineLayoutPath(RenderBlockFlow::UndeterminedPath)
, m_firstChild(nullptr)
, m_lastChild(nullptr)
- , m_style(std::move(style))
+ , m_style(WTFMove(style))
{
}
-RenderElement::RenderElement(Document& document, PassRef<RenderStyle> style, unsigned baseTypeFlags)
- : RenderObject(document)
- , m_baseTypeFlags(baseTypeFlags)
- , m_ancestorLineBoxDirty(false)
- , m_hasInitializedStyle(false)
- , m_renderInlineAlwaysCreatesLineBoxes(false)
- , m_firstChild(nullptr)
- , m_lastChild(nullptr)
- , m_style(std::move(style))
+RenderElement::RenderElement(Element& element, RenderStyle&& style, BaseTypeFlags baseTypeFlags)
+ : RenderElement(static_cast<ContainerNode&>(element), WTFMove(style), baseTypeFlags)
{
}
-RenderElement::~RenderElement()
+RenderElement::RenderElement(Document& document, RenderStyle&& style, BaseTypeFlags baseTypeFlags)
+ : RenderElement(static_cast<ContainerNode&>(document), WTFMove(style), baseTypeFlags)
{
- if (hasInitializedStyle()) {
- 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 (StyleImage* maskBoxImage = m_style->maskBoxImage().image())
- maskBoxImage->removeClient(this);
+}
-#if ENABLE(CSS_SHAPES)
- if (auto shapeValue = m_style->shapeInside()) {
- if (auto shapeImage = shapeValue->image())
- shapeImage->removeClient(this);
- }
- if (auto shapeValue = m_style->shapeOutside()) {
- if (auto shapeImage = shapeValue->image())
- shapeImage->removeClient(this);
- }
-#endif
- }
+RenderElement::~RenderElement()
+{
+ // Do not add any code here. Add it to willBeDestroyed() instead.
}
-RenderPtr<RenderElement> RenderElement::createFor(Element& element, PassRef<RenderStyle> style)
+RenderPtr<RenderElement> RenderElement::createFor(Element& element, RenderStyle&& style)
{
// 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.get().contentData();
- if (contentData && !contentData->next() && contentData->isImage() && !element.isPseudoElement()) {
- auto styleImage = const_cast<StyleImage*>(static_cast<const ImageContentData*>(contentData)->image());
- auto image = createRenderer<RenderImage>(element, std::move(style), styleImage);
- if (styleImage)
- image->setIsGeneratedContent();
- return std::move(image);
- }
-
- if (element.hasTagName(HTMLNames::rubyTag)) {
- if (style.get().display() == INLINE)
- return createRenderer<RenderRubyAsInline>(element, std::move(style));
- if (style.get().display() == BLOCK)
- return createRenderer<RenderRubyAsBlock>(element, std::move(style));
- }
- // treat <rt> as ruby text ONLY if it still has its default treatment of block
- if (element.hasTagName(HTMLNames::rtTag) && style.get().display() == BLOCK)
- return createRenderer<RenderRubyText>(element, std::move(style));
- switch (style.get().display()) {
+ const ContentData* contentData = style.contentData();
+ if (contentData && !contentData->next() && is<ImageContentData>(*contentData) && !element.isPseudoElement()) {
+ Style::loadPendingResources(style, element.document(), &element);
+ auto& styleImage = downcast<ImageContentData>(*contentData).image();
+ auto image = createRenderer<RenderImage>(element, WTFMove(style), const_cast<StyleImage*>(&styleImage));
+ image->setIsGeneratedContent();
+ return WTFMove(image);
+ }
+
+ switch (style.display()) {
case NONE:
- style.dropRef();
+ case CONTENTS:
return nullptr;
case INLINE:
- return createRenderer<RenderInline>(element, std::move(style));
+ return createRenderer<RenderInline>(element, WTFMove(style));
case BLOCK:
case INLINE_BLOCK:
- case RUN_IN:
case COMPACT:
- return createRenderer<RenderBlockFlow>(element, std::move(style));
+ return createRenderer<RenderBlockFlow>(element, WTFMove(style));
case LIST_ITEM:
- return createRenderer<RenderListItem>(element, std::move(style));
+ return createRenderer<RenderListItem>(element, WTFMove(style));
case TABLE:
case INLINE_TABLE:
- return createRenderer<RenderTable>(element, std::move(style));
+ return createRenderer<RenderTable>(element, WTFMove(style));
case TABLE_ROW_GROUP:
case TABLE_HEADER_GROUP:
case TABLE_FOOTER_GROUP:
- return createRenderer<RenderTableSection>(element, std::move(style));
+ return createRenderer<RenderTableSection>(element, WTFMove(style));
case TABLE_ROW:
- return createRenderer<RenderTableRow>(element, std::move(style));
+ return createRenderer<RenderTableRow>(element, WTFMove(style));
case TABLE_COLUMN_GROUP:
case TABLE_COLUMN:
- return createRenderer<RenderTableCol>(element, std::move(style));
+ return createRenderer<RenderTableCol>(element, WTFMove(style));
case TABLE_CELL:
- return createRenderer<RenderTableCell>(element, std::move(style));
+ return createRenderer<RenderTableCell>(element, WTFMove(style));
case TABLE_CAPTION:
- return createRenderer<RenderTableCaption>(element, std::move(style));
+ return createRenderer<RenderTableCaption>(element, WTFMove(style));
case BOX:
case INLINE_BOX:
- return createRenderer<RenderDeprecatedFlexibleBox>(element, std::move(style));
+ return createRenderer<RenderDeprecatedFlexibleBox>(element, WTFMove(style));
case FLEX:
case INLINE_FLEX:
- return createRenderer<RenderFlexibleBox>(element, std::move(style));
+ case WEBKIT_FLEX:
+ case WEBKIT_INLINE_FLEX:
+ return createRenderer<RenderFlexibleBox>(element, WTFMove(style));
case GRID:
case INLINE_GRID:
- return createRenderer<RenderGrid>(element, std::move(style));
+ return createRenderer<RenderGrid>(element, WTFMove(style));
}
ASSERT_NOT_REACHED();
return nullptr;
}
-enum StyleCacheState {
- Cached,
- Uncached
-};
-
-static PassRefPtr<RenderStyle> firstLineStyleForCachedUncachedType(StyleCacheState type, const RenderElement& renderer, RenderStyle* style)
+std::unique_ptr<RenderStyle> RenderElement::computeFirstLineStyle() const
{
- RenderElement& rendererForFirstLineStyle = renderer.isBeforeOrAfterContent() ? *renderer.parent() : const_cast<RenderElement&>(renderer);
+ ASSERT(view().usesFirstLineRules());
+
+ RenderElement& rendererForFirstLineStyle = isBeforeOrAfterContent() ? *parent() : const_cast<RenderElement&>(*this);
if (rendererForFirstLineStyle.isRenderBlockFlow() || rendererForFirstLineStyle.isRenderButton()) {
- if (RenderBlock* firstLineBlock = rendererForFirstLineStyle.firstLineBlock()) {
- if (type == Cached)
- return firstLineBlock->getCachedPseudoStyle(FIRST_LINE, style);
- return firstLineBlock->getUncachedPseudoStyle(PseudoStyleRequest(FIRST_LINE), style, firstLineBlock == &renderer ? style : nullptr);
- }
- } 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);
- }
+ RenderBlock* firstLineBlock = rendererForFirstLineStyle.firstLineBlock();
+ if (!firstLineBlock)
+ return nullptr;
+ auto* firstLineStyle = firstLineBlock->getCachedPseudoStyle(FIRST_LINE, &style());
+ if (!firstLineStyle)
+ return nullptr;
+ return RenderStyle::clonePtr(*firstLineStyle);
}
- return nullptr;
-}
-PassRefPtr<RenderStyle> RenderElement::uncachedFirstLineStyle(RenderStyle* style) const
-{
- if (!document().styleSheetCollection().usesFirstLineRules())
+ if (rendererForFirstLineStyle.isAnonymous() || !rendererForFirstLineStyle.isRenderInline())
return nullptr;
- return firstLineStyleForCachedUncachedType(Uncached, *this, style);
+ auto& parentStyle = rendererForFirstLineStyle.parent()->firstLineStyle();
+ if (&parentStyle == &rendererForFirstLineStyle.parent()->style())
+ return nullptr;
+ return rendererForFirstLineStyle.element()->styleResolver().styleForElement(*element(), &parentStyle).renderStyle;
}
-RenderStyle* RenderElement::cachedFirstLineStyle() const
+const RenderStyle& RenderElement::firstLineStyle() const
{
- ASSERT(document().styleSheetCollection().usesFirstLineRules());
-
- RenderStyle& style = this->style();
- if (RefPtr<RenderStyle> firstLineStyle = firstLineStyleForCachedUncachedType(Cached, *this, &style))
- return firstLineStyle.get();
+ if (!view().usesFirstLineRules())
+ return style();
+
+ if (!m_hasValidCachedFirstLineStyle) {
+ auto firstLineStyle = computeFirstLineStyle();
+ if (firstLineStyle || hasRareData())
+ const_cast<RenderElement&>(*this).ensureRareData().cachedFirstLineStyle = WTFMove(firstLineStyle);
+ m_hasValidCachedFirstLineStyle = true;
+ }
- return &style;
+ return (hasRareData() && rareData().cachedFirstLineStyle) ? *rareData().cachedFirstLineStyle : style();
}
StyleDifference RenderElement::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 (!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() || !downcast<RenderLayerModelObject>(*this).layer()->isComposited()) {
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;
+ diff = std::max(diff, StyleDifferenceLayout);
+ else {
+ // We need to set at least SimplifiedLayout, but if PositionedMovementOnly is already set
+ // then we actually need SimplifiedLayoutAndPositionedMovement.
+ diff = std::max(diff, (diff == StyleDifferenceLayoutPositionedMovementOnly) ? StyleDifferenceSimplifiedLayoutAndPositionedMovement : StyleDifferenceSimplifiedLayout);
+ }
+
+ } else
+ diff = std::max(diff, StyleDifferenceRecompositeLayer);
}
- // If opacity changed, and we are not composited, need to repaint (also
- // ignoring text nodes)
if (contextSensitiveProperties & ContextSensitivePropertyOpacity) {
- if (!hasLayer() || !toRenderLayerModelObject(this)->layer()->isComposited())
- diff = StyleDifferenceRepaintLayer;
- else if (diff < StyleDifferenceRecompositeLayer)
- diff = StyleDifferenceRecompositeLayer;
+ if (!hasLayer() || !downcast<RenderLayerModelObject>(*this).layer()->isComposited())
+ diff = std::max(diff, StyleDifferenceRepaintLayer);
+ else
+ diff = std::max(diff, StyleDifferenceRecompositeLayer);
+ }
+
+ if (contextSensitiveProperties & ContextSensitivePropertyClipPath) {
+ if (hasLayer()
+ && downcast<RenderLayerModelObject>(*this).layer()->isComposited()
+ && hasClipPath()
+ && RenderLayerCompositor::canCompositeClipPath(*downcast<RenderLayerModelObject>(*this).layer()))
+ diff = std::max(diff, StyleDifferenceRecompositeLayer);
+ else
+ diff = std::max(diff, StyleDifferenceRepaint);
+ }
+
+ if (contextSensitiveProperties & ContextSensitivePropertyWillChange) {
+ if (style().willChange() && style().willChange()->canTriggerCompositing())
+ diff = std::max(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;
+ auto& layer = *downcast<RenderLayerModelObject>(*this).layer();
+ if (!layer.isComposited() || layer.paintsWithFilters())
+ diff = std::max(diff, StyleDifferenceRepaintLayer);
+ else
+ diff = std::max(diff, StyleDifferenceRecompositeLayer);
}
-#endif
// 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 && isRenderLayerModelObject()) {
- if (hasLayer() != toRenderLayerModelObject(this)->requiresLayer())
+ if (diff < StyleDifferenceLayout && isRenderLayerModelObject()) {
+ if (hasLayer() != downcast<RenderLayerModelObject>(*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())
@@ -296,7 +298,7 @@ StyleDifference RenderElement::adjustStyleDifference(StyleDifference diff, unsig
inline bool RenderElement::hasImmediateNonWhitespaceTextChildOrBorderOrOutline() const
{
for (auto& child : childrenOfType<RenderObject>(*this)) {
- if (child.isText() && !toRenderText(child).isAllCollapsibleWhitespace())
+ if (is<RenderText>(child) && !downcast<RenderText>(child).isAllCollapsibleWhitespace())
return true;
if (child.style().hasOutline() || child.style().hasBorder())
return true;
@@ -309,21 +311,20 @@ inline bool RenderElement::shouldRepaintForStyleDifference(StyleDifference diff)
return diff == StyleDifferenceRepaint || (diff == StyleDifferenceRepaintIfTextOrBorderOrOutline && hasImmediateNonWhitespaceTextChildOrBorderOrOutline());
}
-void RenderElement::updateFillImages(const FillLayer* oldLayers, const FillLayer* newLayers)
+void RenderElement::updateFillImages(const FillLayer* oldLayers, const FillLayer& newLayers)
{
- // Optimize the common case
- if (oldLayers && !oldLayers->next() && newLayers && !newLayers->next() && (oldLayers->image() == newLayers->image()))
+ // Optimize the common case.
+ if (FillLayer::imagesIdentical(oldLayers, &newLayers))
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);
+ // Add before removing, to avoid removing all clients of an image that is in both sets.
+ for (auto* layer = &newLayers; layer; layer = layer->next()) {
+ if (layer->image())
+ layer->image()->addClient(this);
}
-
- for (const FillLayer* currOld = oldLayers; currOld; currOld = currOld->next()) {
- if (currOld->image())
- currOld->image()->removeClient(this);
+ for (auto* layer = oldLayers; layer; layer = layer->next()) {
+ if (layer->image())
+ layer->image()->removeClient(this);
}
}
@@ -337,37 +338,19 @@ void RenderElement::updateImage(StyleImage* oldImage, StyleImage* newImage)
newImage->addClient(this);
}
-#if ENABLE(CSS_SHAPES)
void RenderElement::updateShapeImage(const ShapeValue* oldShapeValue, const ShapeValue* newShapeValue)
{
if (oldShapeValue || newShapeValue)
updateImage(oldShapeValue ? oldShapeValue->image() : nullptr, newShapeValue ? newShapeValue->image() : nullptr);
}
-#endif
void RenderElement::initializeStyle()
{
- styleWillChange(StyleDifferenceEqual, style());
+ Style::loadPendingResources(m_style, document(), element());
+ styleWillChange(StyleDifferenceNewStyle, style());
m_hasInitializedStyle = true;
-
- updateFillImages(nullptr, m_style->backgroundLayers());
- updateFillImages(nullptr, m_style->maskLayers());
-
- updateImage(nullptr, m_style->borderImage().image());
- updateImage(nullptr, m_style->maskBoxImage().image());
-
-#if ENABLE(CSS_SHAPES)
- updateShapeImage(nullptr, m_style->shapeInside());
- updateShapeImage(nullptr, m_style->shapeOutside());
-#endif
-
- // 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))
- view().setMaximalOutlineSize(m_style->outlineSize());
-
- styleDidChange(StyleDifferenceEqual, nullptr);
+ styleDidChange(StyleDifferenceNewStyle, nullptr);
// We shouldn't have any text children that would need styleDidChange at this point.
ASSERT(!childrenOfType<RenderText>(*this).first());
@@ -376,66 +359,44 @@ void RenderElement::initializeStyle()
// have their parent set before getting a call to initializeStyle() :|
}
-void RenderElement::setStyle(PassRef<RenderStyle> style)
+void RenderElement::setStyle(RenderStyle&& style, StyleDifference minimalStyleDifference)
{
// FIXME: Should change RenderView so it can use initializeStyle too.
// If we do that, we can assert m_hasInitializedStyle unconditionally,
// and remove the check of m_hasInitializedStyle below too.
ASSERT(m_hasInitializedStyle || isRenderView());
- if (&m_style.get() == &style.get()) {
- // FIXME: Can we change things so we never hit this code path?
-#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());
- ASSERT(!isEmbeddedObject());
- ASSERT(!isCanvas());
-#endif
- style.dropRef();
- return;
- }
-
StyleDifference diff = StyleDifferenceEqual;
unsigned contextSensitiveProperties = ContextSensitivePropertyNone;
if (m_hasInitializedStyle)
- diff = m_style->diff(&style.get(), contextSensitiveProperties);
-
- diff = adjustStyleDifference(diff, contextSensitiveProperties);
-
- styleWillChange(diff, style.get());
+ diff = m_style.diff(style, contextSensitiveProperties);
- Ref<RenderStyle> oldStyle(m_style.replace(std::move(style)));
+ diff = std::max(diff, minimalStyleDifference);
- updateFillImages(oldStyle.get().backgroundLayers(), m_style->backgroundLayers());
- updateFillImages(oldStyle.get().maskLayers(), m_style->maskLayers());
-
- updateImage(oldStyle.get().borderImage().image(), m_style->borderImage().image());
- updateImage(oldStyle.get().maskBoxImage().image(), m_style->maskBoxImage().image());
+ diff = adjustStyleDifference(diff, contextSensitiveProperties);
-#if ENABLE(CSS_SHAPES)
- updateShapeImage(oldStyle.get().shapeInside(), m_style->shapeInside());
- updateShapeImage(oldStyle.get().shapeOutside(), m_style->shapeOutside());
-#endif
+ Style::loadPendingResources(style, document(), element());
- // 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))
- view().setMaximalOutlineSize(m_style->outlineSize());
+ styleWillChange(diff, style);
+ auto oldStyle = m_style.replace(WTFMove(style));
+ bool detachedFromParent = !parent();
- bool doesNotNeedLayout = !parent();
+ // Make sure we invalidate the containing block cache for flows when the contianing block context changes
+ // so that styleDidChange can safely use RenderBlock::locateFlowThreadContainingBlock()
+ if (oldStyle.position() != m_style.position())
+ adjustFlowThreadStateOnContainingBlockChangeIfNeeded();
- styleDidChange(diff, &oldStyle.get());
+ styleDidChange(diff, &oldStyle);
// Text renderers use their parent style. Notify them about the change.
for (auto& child : childrenOfType<RenderText>(*this))
- child.styleDidChange(diff, &oldStyle.get());
+ child.styleDidChange(diff, &oldStyle);
// 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
+ // we will safely bail out with the detachedFromParent 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)
+ if (detachedFromParent)
return;
// Now that the layer (if any) has been updated, we need to adjust the diff again,
@@ -446,9 +407,9 @@ void RenderElement::setStyle(PassRef<RenderStyle> style)
if (updatedDiff == StyleDifferenceLayout)
setNeedsLayoutAndPrefWidthsRecalc();
else if (updatedDiff == StyleDifferenceLayoutPositionedMovementOnly)
- setNeedsPositionedMovementLayout(&oldStyle.get());
+ setNeedsPositionedMovementLayout(&oldStyle);
else if (updatedDiff == StyleDifferenceSimplifiedLayoutAndPositionedMovement) {
- setNeedsPositionedMovementLayout(&oldStyle.get());
+ setNeedsPositionedMovementLayout(&oldStyle);
setNeedsSimplifiedNormalFlowLayout();
} else if (updatedDiff == StyleDifferenceSimplifiedLayout)
setNeedsSimplifiedNormalFlowLayout();
@@ -461,43 +422,45 @@ void RenderElement::setStyle(PassRef<RenderStyle> style)
}
}
-void RenderElement::setAnimatableStyle(PassRef<RenderStyle> style)
+bool RenderElement::childRequiresTable(const RenderObject& child) const
{
- setStyle(animation().updateAnimations(*this, std::move(style)));
+ if (is<RenderTableCol>(child)) {
+ const RenderTableCol& newTableColumn = downcast<RenderTableCol>(child);
+ bool isColumnInColumnGroup = newTableColumn.isTableColumn() && is<RenderTableCol>(*this);
+ return !is<RenderTable>(*this) && !isColumnInColumnGroup;
+ }
+ if (is<RenderTableCaption>(child))
+ return !is<RenderTable>(*this);
+
+ if (is<RenderTableSection>(child))
+ return !is<RenderTable>(*this);
+
+ if (is<RenderTableRow>(child))
+ return !is<RenderTableSection>(*this);
+
+ if (is<RenderTableCell>(child))
+ return !is<RenderTableRow>(*this);
+
+ return false;
}
void RenderElement::addChild(RenderObject* newChild, RenderObject* beforeChild)
{
- 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) {
+ if (childRequiresTable(*newChild)) {
RenderTable* table;
RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : m_lastChild;
- if (afterChild && afterChild->isAnonymous() && afterChild->isTable() && !afterChild->isBeforeContent())
- table = toRenderTable(afterChild);
+ if (afterChild && afterChild->isAnonymous() && is<RenderTable>(*afterChild) && !afterChild->isBeforeContent())
+ table = downcast<RenderTable>(afterChild);
else {
- table = RenderTable::createAnonymousWithParentRenderer(this);
+ table = RenderTable::createAnonymousWithParentRenderer(*this).release();
addChild(table, beforeChild);
}
table->addChild(newChild);
} else
insertChildInternal(newChild, beforeChild, NotifyChildren);
- if (newChild->isText())
- toRenderText(newChild)->styleDidChange(StyleDifferenceEqual, nullptr);
+ if (is<RenderText>(*newChild))
+ downcast<RenderText>(*newChild).styleDidChange(StyleDifferenceEqual, nullptr);
// 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:
@@ -508,11 +471,9 @@ void RenderElement::addChild(RenderObject* newChild, RenderObject* beforeChild)
// 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();
+ downcast<RenderLayerModelObject>(*newChild).layer()->removeOnlyThisLayer();
-#if ENABLE(SVG)
SVGRenderSupport::childAdded(*this, *newChild);
-#endif
}
void RenderElement::removeChild(RenderObject& oldChild)
@@ -523,12 +484,8 @@ void RenderElement::removeChild(RenderObject& oldChild)
void RenderElement::destroyLeftoverChildren()
{
while (m_firstChild) {
- if (m_firstChild->isListMarker() || (m_firstChild->style().styleType() == FIRST_LETTER && !m_firstChild->isText()))
- m_firstChild->removeFromParent(); // List markers are owned by their enclosing list and so don't get destroyed by this container. Similarly, first letters are destroyed by their remaining text fragment.
- else if (m_firstChild->isRunIn() && m_firstChild->node()) {
- m_firstChild->node()->setRenderer(nullptr);
- m_firstChild->node()->setNeedsStyleRecalc();
- m_firstChild->destroy();
+ if (m_firstChild->style().styleType() == FIRST_LETTER && !m_firstChild->isText()) {
+ m_firstChild->removeFromParent(); // :first-letter fragment renderers are destroyed by their remaining text fragment.
} else {
// Destroy any anonymous children remaining in the render tree, as well as implicit (shadow) DOM elements like those used in the engine-based text fields.
if (m_firstChild->node())
@@ -574,10 +531,12 @@ void RenderElement::insertChildInternal(RenderObject* newChild, RenderObject* be
m_lastChild = newChild;
}
- if (!documentBeingDestroyed()) {
+ newChild->initializeFlowThreadStateOnInsertion();
+ if (!renderTreeBeingDestroyed()) {
if (notifyChildren == NotifyChildren)
newChild->insertedIntoTree();
- RenderCounter::rendererSubtreeAttached(newChild);
+ if (is<RenderElement>(*newChild))
+ RenderCounter::rendererSubtreeAttached(downcast<RenderElement>(*newChild));
}
newChild->setNeedsLayoutAndPrefWidthsRecalc();
@@ -587,6 +546,10 @@ void RenderElement::insertChildInternal(RenderObject* newChild, RenderObject* be
if (AXObjectCache* cache = document().axObjectCache())
cache->childrenChanged(this, newChild);
+ if (is<RenderBlockFlow>(*this))
+ downcast<RenderBlockFlow>(*this).invalidateLineLayoutPath();
+ if (hasOutlineAutoAncestor() || outlineStyleForRepaint().outlineStyleIsAuto())
+ newChild->setHasOutlineAutoAncestor();
}
void RenderElement::removeChildInternal(RenderObject& oldChild, NotifyChildrenType notifyChildren)
@@ -595,12 +558,12 @@ void RenderElement::removeChildInternal(RenderObject& oldChild, NotifyChildrenTy
ASSERT(oldChild.parent() == this);
if (oldChild.isFloatingOrOutOfFlowPositioned())
- toRenderBox(oldChild).removeFloatingOrPositionedChildFromBlockLists();
+ downcast<RenderBox>(oldChild).removeFloatingOrPositionedChildFromBlockLists();
// So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or
// that a positioned child got yanked). We also repaint, so that the area exposed when the child
// disappears gets repainted properly.
- if (!documentBeingDestroyed() && notifyChildren == NotifyChildren && oldChild.everHadLayout()) {
+ if (!renderTreeBeingDestroyed() && notifyChildren == NotifyChildren && oldChild.everHadLayout()) {
oldChild.setNeedsLayoutAndPrefWidthsRecalc();
// We only repaint |oldChild| if we have a RenderLayer as its visual overflow may not be tracked by its parent.
if (oldChild.isBody())
@@ -610,32 +573,34 @@ void RenderElement::removeChildInternal(RenderObject& oldChild, NotifyChildrenTy
}
// If we have a line box wrapper, delete it.
- if (oldChild.isBox())
- toRenderBox(oldChild).deleteLineBoxWrapper();
- else if (oldChild.isLineBreak())
- toRenderLineBreak(oldChild).deleteInlineBoxWrapper();
+ if (is<RenderBox>(oldChild))
+ downcast<RenderBox>(oldChild).deleteLineBoxWrapper();
+ else if (is<RenderLineBreak>(oldChild))
+ downcast<RenderLineBreak>(oldChild).deleteInlineBoxWrapper();
// If oldChild is the start or end of the selection, then clear the selection to
// avoid problems of invalid pointers.
- // FIXME: The FrameSelection should be responsible for this when it
- // is notified of DOM mutations.
- if (!documentBeingDestroyed() && oldChild.isSelectionBorder())
- view().clearSelection();
+ if (!renderTreeBeingDestroyed() && oldChild.isSelectionBorder())
+ frame().selection().setNeedsSelectionUpdate();
- if (!documentBeingDestroyed() && notifyChildren == NotifyChildren)
+ if (!renderTreeBeingDestroyed() && notifyChildren == NotifyChildren)
oldChild.willBeRemovedFromTree();
+ oldChild.resetFlowThreadStateOnRemoval();
+
// WARNING: There should be no code running between willBeRemovedFromTree and the actual removal below.
// This is needed to avoid race conditions where willBeRemovedFromTree would dirty the tree's structure
// and the code running here would force an untimely rebuilding, leaving |oldChild| dangling.
+
+ RenderObject* nextSibling = oldChild.nextSibling();
if (oldChild.previousSibling())
- oldChild.previousSibling()->setNextSibling(oldChild.nextSibling());
- if (oldChild.nextSibling())
- oldChild.nextSibling()->setPreviousSibling(oldChild.previousSibling());
+ oldChild.previousSibling()->setNextSibling(nextSibling);
+ if (nextSibling)
+ nextSibling->setPreviousSibling(oldChild.previousSibling());
if (m_firstChild == &oldChild)
- m_firstChild = oldChild.nextSibling();
+ m_firstChild = nextSibling;
if (m_lastChild == &oldChild)
m_lastChild = oldChild.previousSibling();
@@ -645,11 +610,39 @@ void RenderElement::removeChildInternal(RenderObject& oldChild, NotifyChildrenTy
// rendererRemovedFromTree walks the whole subtree. We can improve performance
// by skipping this step when destroying the entire tree.
- if (!documentBeingDestroyed())
- RenderCounter::rendererRemovedFromTree(oldChild);
+ if (!renderTreeBeingDestroyed() && is<RenderElement>(oldChild))
+ RenderCounter::rendererRemovedFromTree(downcast<RenderElement>(oldChild));
if (AXObjectCache* cache = document().existingAXObjectCache())
cache->childrenChanged(this);
+#if !ASSERT_DISABLED
+ // Check if the marker gets detached while laying out the list item.
+ if (is<RenderListMarker>(oldChild))
+ ASSERT(m_reparentingChild || !downcast<RenderListMarker>(oldChild).listItem().inLayout());
+#endif
+}
+
+RenderBlock* RenderElement::containingBlockForFixedPosition() const
+{
+ auto* renderer = parent();
+ while (renderer && !renderer->canContainFixedPositionObjects())
+ renderer = renderer->parent();
+
+ ASSERT(!renderer || !renderer->isAnonymousBlock());
+ return downcast<RenderBlock>(renderer);
+}
+
+RenderBlock* RenderElement::containingBlockForAbsolutePosition() const
+{
+ // A relatively positioned RenderInline forwards its absolute positioned descendants to
+ // its nearest non-anonymous containing block (to avoid having a positioned objects list in all RenderInlines).
+ auto* renderer = isRenderInline() ? const_cast<RenderElement*>(downcast<RenderElement>(this)) : parent();
+ while (renderer && !renderer->canContainAbsolutelyPositionedObjects())
+ renderer = renderer->parent();
+ // Make sure we only return non-anonymous RenderBlock as containing block.
+ while (renderer && (!is<RenderBlock>(*renderer) || renderer->isAnonymousBlock()))
+ renderer = renderer->containingBlock();
+ return downcast<RenderBlock>(renderer);
}
static void addLayers(RenderElement& renderer, RenderLayer* parentLayer, RenderElement*& newObject, RenderLayer*& beforeChild)
@@ -662,7 +655,7 @@ static void addLayers(RenderElement& renderer, RenderLayer* parentLayer, RenderE
beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject);
newObject = nullptr;
}
- parentLayer->addChild(toRenderLayerModelObject(renderer).layer(), beforeChild);
+ parentLayer->addChild(downcast<RenderLayerModelObject>(renderer).layer(), beforeChild);
return;
}
@@ -686,7 +679,7 @@ void RenderElement::removeLayers(RenderLayer* parentLayer)
return;
if (hasLayer()) {
- parentLayer->removeChild(toRenderLayerModelObject(this)->layer());
+ parentLayer->removeChild(downcast<RenderLayerModelObject>(*this).layer());
return;
}
@@ -700,7 +693,7 @@ void RenderElement::moveLayers(RenderLayer* oldParent, RenderLayer* newParent)
return;
if (hasLayer()) {
- RenderLayer* layer = toRenderLayerModelObject(this)->layer();
+ RenderLayer* layer = downcast<RenderLayerModelObject>(*this).layer();
ASSERT(oldParent == layer->parent());
if (oldParent)
oldParent->removeChild(layer);
@@ -719,7 +712,7 @@ RenderLayer* RenderElement::findNextLayer(RenderLayer* parentLayer, RenderObject
return nullptr;
// Step 1: If our layer is a child of the desired parent, then return our layer.
- RenderLayer* ourLayer = hasLayer() ? toRenderLayerModelObject(this)->layer() : nullptr;
+ RenderLayer* ourLayer = hasLayer() ? downcast<RenderLayerModelObject>(*this).layer() : nullptr;
if (ourLayer && ourLayer->parent() == parentLayer)
return ourLayer;
@@ -727,9 +720,9 @@ RenderLayer* RenderElement::findNextLayer(RenderLayer* parentLayer, RenderObject
// into our siblings trying to find the next layer whose parent is the desired parent.
if (!ourLayer || ourLayer == parentLayer) {
for (RenderObject* child = startPoint ? startPoint->nextSibling() : firstChild(); child; child = child->nextSibling()) {
- if (!child->isRenderElement())
+ if (!is<RenderElement>(*child))
continue;
- RenderLayer* nextLayer = toRenderElement(child)->findNextLayer(parentLayer, nullptr, false);
+ RenderLayer* nextLayer = downcast<RenderElement>(*child).findNextLayer(parentLayer, nullptr, false);
if (nextLayer)
return nextLayer;
}
@@ -750,14 +743,12 @@ RenderLayer* RenderElement::findNextLayer(RenderLayer* parentLayer, RenderObject
bool RenderElement::layerCreationAllowedForSubtree() const
{
-#if ENABLE(SVG)
RenderElement* parentRenderer = parent();
while (parentRenderer) {
if (parentRenderer->isSVGHiddenContainer())
return false;
parentRenderer = parentRenderer->parent();
}
-#endif
return true;
}
@@ -769,7 +760,7 @@ void RenderElement::propagateStyleToAnonymousChildren(StylePropagationType propa
if (!elementChild.isAnonymous() || elementChild.style().styleType() != NOPSEUDO)
continue;
- if (propagationType == PropagateToBlockChildrenOnly && !elementChild.isRenderBlock())
+ if (propagationType == PropagateToBlockChildrenOnly && !is<RenderBlock>(elementChild))
continue;
#if ENABLE(FULLSCREEN_API)
@@ -778,53 +769,50 @@ void RenderElement::propagateStyleToAnonymousChildren(StylePropagationType propa
#endif
// RenderFlowThreads are updated through the RenderView::styleDidChange function.
- if (elementChild.isRenderFlowThread())
+ if (is<RenderFlowThread>(elementChild))
continue;
- auto newStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), elementChild.style().display());
+ auto newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), elementChild.style().display());
if (style().specifiesColumns()) {
if (elementChild.style().specifiesColumns())
- newStyle.get().inheritColumnPropertiesFrom(&style());
+ newStyle.inheritColumnPropertiesFrom(style());
if (elementChild.style().columnSpan())
- newStyle.get().setColumnSpan(ColumnSpanAll);
+ 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 (elementChild.isInFlowPositioned() && toRenderBlock(elementChild).isAnonymousBlockContinuation())
- newStyle.get().setPosition(elementChild.style().position());
+ if (elementChild.isInFlowPositioned() && downcast<RenderBlock>(elementChild).isAnonymousBlockContinuation())
+ newStyle.setPosition(elementChild.style().position());
- elementChild.setStyle(std::move(newStyle));
+ elementChild.setStyle(WTFMove(newStyle));
}
}
-// 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()
+static inline bool rendererHasBackground(const RenderElement* renderer)
{
-#if ENABLE(FAST_MOBILE_SCROLLING)
- return false;
-#else
- return true;
-#endif
+ return renderer && renderer->hasBackground();
}
-static inline bool rendererHasBackground(const RenderElement* renderer)
+void RenderElement::invalidateCachedFirstLineStyle()
{
- return renderer && renderer->hasBackground();
+ if (!m_hasValidCachedFirstLineStyle)
+ return;
+ m_hasValidCachedFirstLineStyle = false;
+ // Invalidate the subtree as descendant's first line style may depend on ancestor's.
+ for (auto& descendant : descendantsOfType<RenderElement>(*this))
+ descendant.m_hasValidCachedFirstLineStyle = false;
}
void RenderElement::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
{
- RenderStyle* oldStyle = hasInitializedStyle() ? &style() : nullptr;
+ auto* oldStyle = hasInitializedStyle() ? &style() : nullptr;
if (oldStyle) {
// If our z-index changes value or our visibility changes,
// we need to dirty our stacking context's z-order list.
- bool visibilityChanged = m_style->visibility() != newStyle.visibility()
- || m_style->zIndex() != newStyle.zIndex()
- || m_style->hasAutoZIndex() != newStyle.hasAutoZIndex();
+ bool visibilityChanged = m_style.visibility() != newStyle.visibility()
+ || m_style.zIndex() != newStyle.zIndex()
+ || m_style.hasAutoZIndex() != newStyle.hasAutoZIndex();
#if ENABLE(DASHBOARD_SUPPORT)
if (visibilityChanged)
document().setAnnotatedRegionsDirty(true);
@@ -839,7 +827,7 @@ void RenderElement::styleWillChange(StyleDifference diff, const RenderStyle& new
}
// Keep layer hierarchy visibility bits up to date if visibility changes.
- if (m_style->visibility() != newStyle.visibility()) {
+ if (m_style.visibility() != newStyle.visibility()) {
if (RenderLayer* layer = enclosingLayer()) {
if (newStyle.visibility() == VISIBLE)
layer->setHasVisibleContent();
@@ -851,16 +839,17 @@ void RenderElement::styleWillChange(StyleDifference diff, const RenderStyle& new
}
}
- if (m_parent && (newStyle.outlineSize() < m_style->outlineSize() || shouldRepaintForStyleDifference(diff)))
+ if (m_parent && (newStyle.outlineSize() < m_style.outlineSize() || shouldRepaintForStyleDifference(diff)))
repaint();
- if (isFloating() && (m_style->floating() != newStyle.floating()))
+ 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()))
+ downcast<RenderBox>(*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();
+ downcast<RenderBox>(*this).removeFloatingOrPositionedChildFromBlockLists();
+ }
s_affectsParentBlock = isFloatingOrOutOfFlowPositioned()
&& (!newStyle.isFloating() && !newStyle.hasOutOfFlowPosition())
@@ -874,39 +863,91 @@ void RenderElement::styleWillChange(StyleDifference diff, const RenderStyle& new
setFloating(false);
clearPositionedState();
}
+ if (newStyle.hasPseudoStyle(FIRST_LINE) || oldStyle->hasPseudoStyle(FIRST_LINE))
+ invalidateCachedFirstLineStyle();
+
setHorizontalWritingMode(true);
- setHasBoxDecorations(false);
+ setHasVisibleBoxDecorations(false);
setHasOverflowClip(false);
- setHasTransform(false);
+ setHasTransformRelatedProperty(false);
setHasReflection(false);
} else {
s_affectsParentBlock = false;
s_noLongerAffectsParentBlock = false;
}
- bool repaintFixedBackgroundsOnScroll = shouldRepaintFixedBackgroundsOnScroll();
+ bool newStyleUsesFixedBackgrounds = newStyle.hasFixedBackgroundImage();
+ bool oldStyleUsesFixedBackgrounds = m_style.hasFixedBackgroundImage();
+ if (newStyleUsesFixedBackgrounds || oldStyleUsesFixedBackgrounds) {
+ bool repaintFixedBackgroundsOnScroll = !settings().fixedBackgroundsPaintRelativeToDocument();
+ bool newStyleSlowScroll = repaintFixedBackgroundsOnScroll && newStyleUsesFixedBackgrounds;
+ bool oldStyleSlowScroll = oldStyle && repaintFixedBackgroundsOnScroll && oldStyleUsesFixedBackgrounds;
+ bool drawsRootBackground = isDocumentElementRenderer() || (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;
+ }
+ }
+
+ if (oldStyleSlowScroll != newStyleSlowScroll) {
+ if (oldStyleSlowScroll)
+ view().frameView().removeSlowRepaintObject(this);
- bool newStyleSlowScroll = repaintFixedBackgroundsOnScroll && newStyle.hasFixedBackgroundImage();
- bool oldStyleSlowScroll = oldStyle && repaintFixedBackgroundsOnScroll && m_style->hasFixedBackgroundImage();
+ if (newStyleSlowScroll)
+ view().frameView().addSlowRepaintObject(this);
+ }
+ }
-#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 (isDocumentElementRenderer() || isBody())
+ view().frameView().updateExtendBackgroundIfNecessary();
+}
- if (oldStyleSlowScroll && m_style->hasEntirelyFixedBackground())
- oldStyleSlowScroll = false;
+void RenderElement::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())
+ downcast<RenderBoxModelObject>(*parent()).childBecameNonInline(*this);
+ else {
+ // An anonymous block must be made to wrap this inline.
+ RenderBlock* block = downcast<RenderBlock>(*parent()).createAnonymousBlock();
+ parent()->insertChildInternal(block, this, RenderElement::NotifyChildren);
+ parent()->removeChildInternal(*this, RenderElement::NotifyChildren);
+ block->insertChildInternal(this, nullptr, RenderElement::NotifyChildren);
}
}
-#endif
- if (oldStyleSlowScroll != newStyleSlowScroll) {
- if (oldStyleSlowScroll)
- view().frameView().removeSlowRepaintObject(this);
+}
+
+void RenderElement::removeAnonymousWrappersForInlinesIfNecessary()
+{
+ RenderBlock& parentBlock = downcast<RenderBlock>(*parent());
+ if (!parentBlock.canDropAnonymousBlockChild())
+ return;
- if (newStyleSlowScroll)
- view().frameView().addSlowRepaintObject(this);
+ // 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* current = parent()->firstChild();
+ while (current && ((current->isAnonymousBlock() && !downcast<RenderBlock>(*current).isAnonymousBlockContinuation()) || current->style().isFloating() || current->style().hasOutOfFlowPosition()))
+ current = current->nextSibling();
+
+ if (current)
+ return;
+
+ RenderObject* next;
+ for (current = parent()->firstChild(); current; current = next) {
+ next = current->nextSibling();
+ if (current->isAnonymousBlock())
+ parentBlock.dropAnonymousBoxChild(parentBlock, downcast<RenderBlock>(*current));
}
}
@@ -925,27 +966,32 @@ static inline bool areCursorsEqual(const RenderStyle* a, const RenderStyle* b)
void RenderElement::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
+ updateFillImages(oldStyle ? &oldStyle->backgroundLayers() : nullptr, m_style.backgroundLayers());
+ updateFillImages(oldStyle ? &oldStyle->maskLayers() : nullptr, m_style.maskLayers());
+ updateImage(oldStyle ? oldStyle->borderImage().image() : nullptr, m_style.borderImage().image());
+ updateImage(oldStyle ? oldStyle->maskBoxImage().image() : nullptr, m_style.maskBoxImage().image());
+ updateShapeImage(oldStyle ? oldStyle->shapeOutside() : nullptr, m_style.shapeOutside());
+
if (s_affectsParentBlock)
handleDynamicFloatPositionChange();
if (s_noLongerAffectsParentBlock)
removeAnonymousWrappersForInlinesIfNecessary();
-#if ENABLE(SVG)
- SVGRenderSupport::styleChanged(*this);
-#endif
+
+ SVGRenderSupport::styleChanged(*this, oldStyle);
if (!m_parent)
return;
if (diff == StyleDifferenceLayout || diff == StyleDifferenceSimplifiedLayout) {
- RenderCounter::rendererStyleChanged(this, oldStyle, &m_style.get());
+ RenderCounter::rendererStyleChanged(*this, oldStyle, &m_style);
// 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())
+ if (needsLayout() && oldStyle->position() != m_style.position())
markContainingBlocksForLayout();
if (diff == StyleDifferenceLayout)
@@ -965,12 +1011,16 @@ void RenderElement::styleDidChange(StyleDifference diff, const RenderStyle* oldS
if (oldStyle && !areCursorsEqual(oldStyle, &style()))
frame().eventHandler().scheduleCursorUpdate();
#endif
+ bool hadOutlineAuto = oldStyle && oldStyle->outlineStyleIsAuto();
+ bool hasOutlineAuto = outlineStyleForRepaint().outlineStyleIsAuto();
+ if (hasOutlineAuto != hadOutlineAuto) {
+ updateOutlineAutoAncestor(hasOutlineAuto);
+ issueRepaintForOutlineAuto(hasOutlineAuto ? outlineStyleForRepaint().outlineSize() : oldStyle->outlineSize());
+ }
}
void RenderElement::insertedIntoTree()
{
- RenderObject::insertedIntoTree();
-
// 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 = nullptr;
@@ -987,6 +1037,8 @@ void RenderElement::insertedIntoTree()
if (layer)
layer->setHasVisibleContent();
}
+
+ RenderObject::insertedIntoTree();
}
void RenderElement::willBeRemovedFromTree()
@@ -1004,23 +1056,81 @@ void RenderElement::willBeRemovedFromTree()
removeLayers(layer);
}
- bool repaintFixedBackgroundsOnScroll = shouldRepaintFixedBackgroundsOnScroll();
- if (repaintFixedBackgroundsOnScroll && m_style->hasFixedBackgroundImage())
- view().frameView().removeSlowRepaintObject(this);
-
if (isOutOfFlowPositioned() && parent()->childrenInline())
- parent()->dirtyLinesFromChangedChild(this);
+ parent()->dirtyLinesFromChangedChild(*this);
RenderObject::willBeRemovedFromTree();
}
+inline void RenderElement::clearLayoutRootIfNeeded() const
+{
+ if (renderTreeBeingDestroyed())
+ return;
+
+ if (view().frameView().layoutRoot() != this)
+ return;
+
+ // Normally when a renderer is detached from the tree, the appropriate dirty bits get set
+ // which ensures that this renderer is no longer the layout root.
+ ASSERT_NOT_REACHED();
+
+ // This indicates a failure to layout the child, which is why
+ // the layout root is still set to |this|. Make sure to clear it
+ // since we are getting destroyed.
+ view().frameView().clearLayoutRoot();
+}
+
void RenderElement::willBeDestroyed()
{
- animation().cancelAnimations(this);
+ if (m_style.hasFixedBackgroundImage() && !settings().fixedBackgroundsPaintRelativeToDocument())
+ view().frameView().removeSlowRepaintObject(this);
+
+ animation().cancelAnimations(*this);
destroyLeftoverChildren();
+ if (isRegisteredForVisibleInViewportCallback())
+ unregisterForVisibleInViewportCallback();
+
+ if (hasCounterNodeMap())
+ RenderCounter::destroyCounterNodes(*this);
+
RenderObject::willBeDestroyed();
+
+#if !ASSERT_DISABLED
+ if (!renderTreeBeingDestroyed() && view().hasRenderNamedFlowThreads()) {
+ // After remove, the object and the associated information should not be in any flow thread.
+ for (auto& flowThread : *view().flowThreadController().renderNamedFlowThreadList()) {
+ ASSERT(!flowThread->hasChildInfo(this));
+ }
+ }
+#endif
+
+ clearLayoutRootIfNeeded();
+
+ if (hasInitializedStyle()) {
+ for (auto* bgLayer = &m_style.backgroundLayers(); bgLayer; bgLayer = bgLayer->next()) {
+ if (auto* backgroundImage = bgLayer->image())
+ backgroundImage->removeClient(this);
+ }
+ for (auto* maskLayer = &m_style.maskLayers(); maskLayer; maskLayer = maskLayer->next()) {
+ if (auto* maskImage = maskLayer->image())
+ maskImage->removeClient(this);
+ }
+ if (auto* borderImage = m_style.borderImage().image())
+ borderImage->removeClient(this);
+ if (auto* maskBoxImage = m_style.maskBoxImage().image())
+ maskBoxImage->removeClient(this);
+ if (auto shapeValue = m_style.shapeOutside()) {
+ if (auto shapeImage = shapeValue->image())
+ shapeImage->removeClient(this);
+ }
+ }
+ if (m_hasPausedImageAnimations)
+ view().removeRendererWithPausedImageAnimations(*this);
+
+ if (isRegisteredForVisibleInViewportCallback())
+ view().unregisterForVisibleInViewportCallback(*this);
}
void RenderElement::setNeedsPositionedMovementLayout(const RenderStyle* oldStyle)
@@ -1031,7 +1141,7 @@ void RenderElement::setNeedsPositionedMovementLayout(const RenderStyle* oldStyle
setNeedsPositionedMovementLayoutBit(true);
markContainingBlocksForLayout();
if (hasLayer()) {
- if (oldStyle && style().diffRequiresRepaint(oldStyle))
+ if (oldStyle && style().diffRequiresLayerRepaint(*oldStyle, downcast<RenderLayerModelObject>(*this).layer()->isComposited()))
setLayerNeedsFullRepaint();
else
setLayerNeedsFullRepaintForPositionedMovementLayout();
@@ -1058,24 +1168,6 @@ void RenderElement::setNeedsSimplifiedNormalFlowLayout()
setLayerNeedsFullRepaint();
}
-RenderElement& RenderElement::rendererForRootBackground()
-{
- ASSERT(isRoot());
- if (!hasBackground() && element() && element()->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.
- if (auto body = document().body()) {
- if (body->hasLocalName(HTMLNames::bodyTag)) {
- if (auto renderer = body->renderer())
- return *renderer;
- }
- }
- }
- return *this;
-}
-
RenderElement* RenderElement::hoverAncestor() const
{
// When searching for the hover ancestor and encountering a named flow thread,
@@ -1100,45 +1192,68 @@ RenderElement* RenderElement::hoverAncestor() const
return hoverAncestor;
}
+static inline void paintPhase(RenderElement& element, PaintPhase phase, PaintInfo& paintInfo, const LayoutPoint& childPoint)
+{
+ paintInfo.phase = phase;
+ element.paint(paintInfo, childPoint);
+}
+
+void RenderElement::paintAsInlineBlock(PaintInfo& paintInfo, const LayoutPoint& childPoint)
+{
+ // Paint all phases atomically, as though the element established its own stacking context.
+ // (See Appendix E.2, section 6.4 on inline block/table/replaced elements in the CSS2.1 specification.)
+ // This is also used by other elements (e.g. flex items and grid items).
+ if (paintInfo.phase == PaintPhaseSelection) {
+ paint(paintInfo, childPoint);
+ } else if (paintInfo.phase == PaintPhaseForeground) {
+ paintPhase(*this, PaintPhaseBlockBackground, paintInfo, childPoint);
+ paintPhase(*this, PaintPhaseChildBlockBackgrounds, paintInfo, childPoint);
+ paintPhase(*this, PaintPhaseFloat, paintInfo, childPoint);
+ paintPhase(*this, PaintPhaseForeground, paintInfo, childPoint);
+ paintPhase(*this, PaintPhaseOutline, paintInfo, childPoint);
+
+ // Reset |paintInfo| to the original phase.
+ paintInfo.phase = PaintPhaseForeground;
+ }
+}
+
void RenderElement::layout()
{
StackStats::LayoutCheckPoint layoutCheckPoint;
ASSERT(needsLayout());
- RenderObject* child = firstChild();
- while (child) {
+ for (auto* child = firstChild(); child; child = child->nextSibling()) {
if (child->needsLayout())
- toRenderElement(child)->layout();
+ downcast<RenderElement>(*child).layout();
ASSERT(!child->needsLayout());
- child = child->nextSibling();
}
clearNeedsLayout();
}
-static bool mustRepaintFillLayers(const RenderElement& renderer, const FillLayer* layer)
+static bool mustRepaintFillLayers(const RenderElement& renderer, const FillLayer& layer)
{
// Nobody will use multiple layers without wanting fancy positioning.
- if (layer->next())
+ if (layer.next())
return true;
// Make sure we have a valid image.
- StyleImage* image = layer->image();
+ auto* image = layer.image();
if (!image || !image->canRender(&renderer, renderer.style().effectiveZoom()))
return false;
- if (!layer->xPosition().isZero() || !layer->yPosition().isZero())
+ if (!layer.xPosition().isZero() || !layer.yPosition().isZero())
return true;
- EFillSizeType sizeType = layer->sizeType();
+ auto sizeType = layer.sizeType();
if (sizeType == Contain || sizeType == Cover)
return true;
if (sizeType == SizeLength) {
- LengthSize size = layer->sizeLength();
- if (size.width().isPercent() || size.height().isPercent())
+ auto size = layer.sizeLength();
+ if (size.width.isPercentOrCalculated() || size.height.isPercentOrCalculated())
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()) && image->isGeneratedImage())
+ if ((size.width.isAuto() || size.height.isAuto()) && image->isGeneratedImage())
return true;
} else if (image->usesImageContainerSize())
return true;
@@ -1152,7 +1267,7 @@ static bool mustRepaintBackgroundOrBorder(const RenderElement& renderer)
return true;
// If we don't have a background/border/mask, then nothing to do.
- if (!renderer.hasBoxDecorations())
+ if (!renderer.hasVisibleBoxDecorations())
return false;
if (mustRepaintFillLayers(renderer, renderer.style().backgroundLayers()))
@@ -1183,17 +1298,16 @@ bool RenderElement::repaintAfterLayoutIfNeeded(const RenderLayerModelObject* rep
// 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(*this) && (newBounds != oldBounds || newOutlineBox != oldOutlineBox)))
- fullRepaint = true;
+ fullRepaint = (newOutlineBox.location() != oldOutlineBox.location() || (mustRepaintBackgroundOrBorder(*this) && (newBounds != oldBounds || newOutlineBox != oldOutlineBox)));
}
if (!repaintContainer)
repaintContainer = &view();
if (fullRepaint) {
- repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds));
+ repaintUsingContainer(repaintContainer, oldBounds);
if (newBounds != oldBounds)
- repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds));
+ repaintUsingContainer(repaintContainer, newBounds);
return true;
}
@@ -1202,27 +1316,27 @@ bool RenderElement::repaintAfterLayoutIfNeeded(const RenderLayerModelObject* rep
LayoutUnit deltaLeft = newBounds.x() - oldBounds.x();
if (deltaLeft > 0)
- repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds.x(), oldBounds.y(), deltaLeft, oldBounds.height()));
+ repaintUsingContainer(repaintContainer, LayoutRect(oldBounds.x(), oldBounds.y(), deltaLeft, oldBounds.height()));
else if (deltaLeft < 0)
- repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds.x(), newBounds.y(), -deltaLeft, newBounds.height()));
+ repaintUsingContainer(repaintContainer, LayoutRect(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()));
+ repaintUsingContainer(repaintContainer, LayoutRect(oldBounds.maxX(), newBounds.y(), deltaRight, newBounds.height()));
else if (deltaRight < 0)
- repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds.maxX(), oldBounds.y(), -deltaRight, oldBounds.height()));
+ repaintUsingContainer(repaintContainer, LayoutRect(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));
+ repaintUsingContainer(repaintContainer, LayoutRect(oldBounds.x(), oldBounds.y(), oldBounds.width(), deltaTop));
else if (deltaTop < 0)
- repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds.x(), newBounds.y(), newBounds.width(), -deltaTop));
+ repaintUsingContainer(repaintContainer, LayoutRect(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));
+ repaintUsingContainer(repaintContainer, LayoutRect(newBounds.x(), oldBounds.maxY(), newBounds.width(), deltaBottom));
else if (deltaBottom < 0)
- repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds.x(), newBounds.maxY(), oldBounds.width(), -deltaBottom));
+ repaintUsingContainer(repaintContainer, LayoutRect(oldBounds.x(), newBounds.maxY(), oldBounds.width(), -deltaBottom));
if (newOutlineBox == oldOutlineBox)
return false;
@@ -1237,19 +1351,19 @@ bool RenderElement::repaintAfterLayoutIfNeeded(const RenderLayerModelObject* rep
LayoutUnit shadowLeft;
LayoutUnit shadowRight;
style().getBoxShadowHorizontalExtent(shadowLeft, shadowRight);
- int borderRight = isBox() ? toRenderBox(this)->borderRight() : 0;
- LayoutUnit boxWidth = isBox() ? toRenderBox(this)->width() : LayoutUnit();
- LayoutUnit minInsetRightShadowExtent = std::min<LayoutUnit>(-insetShadowExtent.right(), std::min<LayoutUnit>(newBounds.width(), oldBounds.width()));
- LayoutUnit borderWidth = std::max<LayoutUnit>(borderRight, std::max<LayoutUnit>(valueForLength(style().borderTopRightRadius().width(), boxWidth, &view()), valueForLength(style().borderBottomRightRadius().width(), boxWidth)));
- LayoutUnit decorationsWidth = std::max<LayoutUnit>(-outlineStyle.outlineOffset(), borderWidth + minInsetRightShadowExtent) + std::max<LayoutUnit>(outlineWidth, shadowRight);
+ LayoutUnit borderRight = is<RenderBox>(*this) ? downcast<RenderBox>(*this).borderRight() : LayoutUnit::fromPixel(0);
+ LayoutUnit boxWidth = is<RenderBox>(*this) ? downcast<RenderBox>(*this).width() : LayoutUnit();
+ LayoutUnit minInsetRightShadowExtent = std::min<LayoutUnit>(-insetShadowExtent.right(), std::min(newBounds.width(), oldBounds.width()));
+ LayoutUnit borderWidth = std::max(borderRight, std::max(valueForLength(style().borderTopRightRadius().width, boxWidth), valueForLength(style().borderBottomRightRadius().width, boxWidth)));
+ LayoutUnit decorationsWidth = std::max<LayoutUnit>(-outlineStyle.outlineOffset(), borderWidth + minInsetRightShadowExtent) + std::max(outlineWidth, shadowRight);
LayoutRect rightRect(newOutlineBox.x() + std::min(newOutlineBox.width(), oldOutlineBox.width()) - decorationsWidth,
newOutlineBox.y(),
width + decorationsWidth,
std::max(newOutlineBox.height(), oldOutlineBox.height()));
- LayoutUnit right = std::min<LayoutUnit>(newBounds.maxX(), oldBounds.maxX());
+ LayoutUnit right = std::min(newBounds.maxX(), oldBounds.maxX());
if (rightRect.x() < right) {
rightRect.setWidth(std::min(rightRect.width(), right - rightRect.x()));
- repaintUsingContainer(repaintContainer, pixelSnappedIntRect(rightRect));
+ repaintUsingContainer(repaintContainer, rightRect);
}
}
LayoutUnit height = absoluteValue(newOutlineBox.height() - oldOutlineBox.height());
@@ -1257,11 +1371,12 @@ bool RenderElement::repaintAfterLayoutIfNeeded(const RenderLayerModelObject* rep
LayoutUnit shadowTop;
LayoutUnit shadowBottom;
style().getBoxShadowVerticalExtent(shadowTop, shadowBottom);
- int borderBottom = isBox() ? toRenderBox(this)->borderBottom() : 0;
- LayoutUnit boxHeight = isBox() ? toRenderBox(this)->height() : LayoutUnit();
- LayoutUnit minInsetBottomShadowExtent = std::min<LayoutUnit>(-insetShadowExtent.bottom(), std::min<LayoutUnit>(newBounds.height(), oldBounds.height()));
- LayoutUnit borderHeight = std::max<LayoutUnit>(borderBottom, std::max<LayoutUnit>(valueForLength(style().borderBottomLeftRadius().height(), boxHeight), valueForLength(style().borderBottomRightRadius().height(), boxHeight, &view())));
- LayoutUnit decorationsHeight = std::max<LayoutUnit>(-outlineStyle.outlineOffset(), borderHeight + minInsetBottomShadowExtent) + std::max<LayoutUnit>(outlineWidth, shadowBottom);
+ LayoutUnit borderBottom = is<RenderBox>(*this) ? downcast<RenderBox>(*this).borderBottom() : LayoutUnit::fromPixel(0);
+ LayoutUnit boxHeight = is<RenderBox>(*this) ? downcast<RenderBox>(*this).height() : LayoutUnit();
+ LayoutUnit minInsetBottomShadowExtent = std::min<LayoutUnit>(-insetShadowExtent.bottom(), std::min(newBounds.height(), oldBounds.height()));
+ LayoutUnit borderHeight = std::max(borderBottom, std::max(valueForLength(style().borderBottomLeftRadius().height, boxHeight),
+ valueForLength(style().borderBottomRightRadius().height, boxHeight)));
+ LayoutUnit decorationsHeight = std::max<LayoutUnit>(-outlineStyle.outlineOffset(), borderHeight + minInsetBottomShadowExtent) + std::max(outlineWidth, shadowBottom);
LayoutRect bottomRect(newOutlineBox.x(),
std::min(newOutlineBox.maxY(), oldOutlineBox.maxY()) - decorationsHeight,
std::max(newOutlineBox.width(), oldOutlineBox.width()),
@@ -1269,7 +1384,7 @@ bool RenderElement::repaintAfterLayoutIfNeeded(const RenderLayerModelObject* rep
LayoutUnit bottom = std::min(newBounds.maxY(), oldBounds.maxY());
if (bottomRect.y() < bottom) {
bottomRect.setHeight(std::min(bottomRect.height(), bottom - bottomRect.y()));
- repaintUsingContainer(repaintContainer, pixelSnappedIntRect(bottomRect));
+ repaintUsingContainer(repaintContainer, bottomRect);
}
}
return false;
@@ -1283,4 +1398,903 @@ bool RenderElement::borderImageIsLoadedAndCanBeRendered() const
return borderImage && borderImage->canRender(this, style().effectiveZoom()) && borderImage->isLoaded();
}
+bool RenderElement::mayCauseRepaintInsideViewport(const IntRect* optionalViewportRect) const
+{
+ auto& frameView = view().frameView();
+ if (frameView.isOffscreen())
+ return false;
+
+ if (!hasOverflowClip()) {
+ // FIXME: Computing the overflow rect is expensive if any descendant has
+ // its own self-painting layer. As a result, we prefer to abort early in
+ // this case and assume it may cause us to repaint inside the viewport.
+ if (!hasLayer() || downcast<RenderLayerModelObject>(*this).layer()->firstChild())
+ return true;
+ }
+
+ // Compute viewport rect if it was not provided.
+ const IntRect& visibleRect = optionalViewportRect ? *optionalViewportRect : frameView.windowToContents(frameView.windowClipRect());
+ return visibleRect.intersects(enclosingIntRect(absoluteClippedOverflowRect()));
+}
+
+static bool shouldRepaintForImageAnimation(const RenderElement& renderer, const IntRect& visibleRect)
+{
+ const Document& document = renderer.document();
+ if (document.activeDOMObjectsAreSuspended())
+ return false;
+ if (renderer.style().visibility() != VISIBLE)
+ return false;
+ if (renderer.view().frameView().isOffscreen())
+ return false;
+
+ // Use background rect if we are the root or if we are the body and the background is propagated to the root.
+ // FIXME: This is overly conservative as the image may not be a background-image, in which case it will not
+ // be propagated to the root. At this point, we unfortunately don't have access to the image anymore so we
+ // can no longer check if it is a background image.
+ bool backgroundIsPaintedByRoot = renderer.isDocumentElementRenderer();
+ if (renderer.isBody()) {
+ auto& rootRenderer = *renderer.parent(); // If <body> has a renderer then <html> does too.
+ ASSERT(rootRenderer.isDocumentElementRenderer());
+ ASSERT(is<HTMLHtmlElement>(rootRenderer.element()));
+ // FIXME: Should share body background propagation code.
+ backgroundIsPaintedByRoot = !rootRenderer.hasBackground();
+
+ }
+ LayoutRect backgroundPaintingRect = backgroundIsPaintedByRoot ? renderer.view().backgroundRect() : renderer.absoluteClippedOverflowRect();
+ if (!visibleRect.intersects(enclosingIntRect(backgroundPaintingRect)))
+ return false;
+
+ return true;
+}
+
+void RenderElement::registerForVisibleInViewportCallback()
+{
+ if (isRegisteredForVisibleInViewportCallback())
+ return;
+ setIsRegisteredForVisibleInViewportCallback(true);
+
+ view().registerForVisibleInViewportCallback(*this);
+}
+
+void RenderElement::unregisterForVisibleInViewportCallback()
+{
+ if (!isRegisteredForVisibleInViewportCallback())
+ return;
+ setIsRegisteredForVisibleInViewportCallback(false);
+
+ view().unregisterForVisibleInViewportCallback(*this);
+}
+
+void RenderElement::visibleInViewportStateChanged(VisibleInViewportState state)
+{
+ if (state == visibleInViewportState())
+ return;
+ setVisibleInViewportState(state);
+
+ if (element())
+ element()->isVisibleInViewportChanged();
+}
+
+void RenderElement::newImageAnimationFrameAvailable(CachedImage& image)
+{
+ auto& frameView = view().frameView();
+ auto visibleRect = frameView.windowToContents(frameView.windowClipRect());
+ if (!shouldRepaintForImageAnimation(*this, visibleRect)) {
+ // FIXME: It would be better to pass the image along with the renderer
+ // so that we can be smarter about detecting if the image is inside the
+ // viewport in repaintForPausedImageAnimationsIfNeeded().
+ view().addRendererWithPausedImageAnimations(*this);
+ return;
+ }
+ imageChanged(&image);
+}
+
+bool RenderElement::repaintForPausedImageAnimationsIfNeeded(const IntRect& visibleRect)
+{
+ ASSERT(m_hasPausedImageAnimations);
+ if (!shouldRepaintForImageAnimation(*this, visibleRect))
+ return false;
+
+ repaint();
+
+ // For directly-composited animated GIFs it does not suffice to call repaint() to resume animation. We need to mark the image as changed.
+ if (is<RenderBoxModelObject>(*this))
+ downcast<RenderBoxModelObject>(*this).contentChanged(ImageChanged);
+
+ return true;
+}
+
+const RenderStyle* RenderElement::getCachedPseudoStyle(PseudoId pseudo, const RenderStyle* parentStyle) const
+{
+ if (pseudo < FIRST_INTERNAL_PSEUDOID && !style().hasPseudoStyle(pseudo))
+ return nullptr;
+
+ RenderStyle* cachedStyle = style().getCachedPseudoStyle(pseudo);
+ if (cachedStyle)
+ return cachedStyle;
+
+ std::unique_ptr<RenderStyle> result = getUncachedPseudoStyle(PseudoStyleRequest(pseudo), parentStyle);
+ if (result)
+ return const_cast<RenderStyle&>(m_style).addCachedPseudoStyle(WTFMove(result));
+ return nullptr;
+}
+
+std::unique_ptr<RenderStyle> RenderElement::getUncachedPseudoStyle(const PseudoStyleRequest& pseudoStyleRequest, const RenderStyle* parentStyle, const RenderStyle* ownStyle) const
+{
+ if (pseudoStyleRequest.pseudoId < FIRST_INTERNAL_PSEUDOID && !ownStyle && !style().hasPseudoStyle(pseudoStyleRequest.pseudoId))
+ return nullptr;
+
+ if (!parentStyle) {
+ ASSERT(!ownStyle);
+ parentStyle = &style();
+ }
+
+ if (isAnonymous())
+ return nullptr;
+
+ auto& styleResolver = element()->styleResolver();
+
+ std::unique_ptr<RenderStyle> style = styleResolver.pseudoStyleForElement(*element(), pseudoStyleRequest, *parentStyle);
+
+ if (style)
+ Style::loadPendingResources(*style, document(), element());
+
+ return style;
+}
+
+Color RenderElement::selectionColor(int colorProperty) const
+{
+ // If the element is unselectable, or we are only painting the selection,
+ // don't override the foreground color with the selection foreground color.
+ if (style().userSelect() == SELECT_NONE
+ || (view().frameView().paintBehavior() & (PaintBehaviorSelectionOnly | PaintBehaviorSelectionAndBackgroundsOnly)))
+ return Color();
+
+ if (std::unique_ptr<RenderStyle> pseudoStyle = selectionPseudoStyle()) {
+ Color color = pseudoStyle->visitedDependentColor(colorProperty);
+ if (!color.isValid())
+ color = pseudoStyle->visitedDependentColor(CSSPropertyColor);
+ return color;
+ }
+
+ if (frame().selection().isFocusedAndActive())
+ return theme().activeSelectionForegroundColor();
+ return theme().inactiveSelectionForegroundColor();
+}
+
+std::unique_ptr<RenderStyle> RenderElement::selectionPseudoStyle() const
+{
+ if (isAnonymous())
+ return nullptr;
+
+ if (ShadowRoot* root = element()->containingShadowRoot()) {
+ if (root->mode() == ShadowRootMode::UserAgent) {
+ if (Element* shadowHost = element()->shadowHost())
+ return shadowHost->renderer()->getUncachedPseudoStyle(PseudoStyleRequest(SELECTION));
+ }
+ }
+
+ return getUncachedPseudoStyle(PseudoStyleRequest(SELECTION));
+}
+
+Color RenderElement::selectionForegroundColor() const
+{
+ return selectionColor(CSSPropertyWebkitTextFillColor);
+}
+
+Color RenderElement::selectionEmphasisMarkColor() const
+{
+ return selectionColor(CSSPropertyWebkitTextEmphasisColor);
+}
+
+Color RenderElement::selectionBackgroundColor() const
+{
+ if (style().userSelect() == SELECT_NONE)
+ return Color();
+
+ if (frame().selection().shouldShowBlockCursor() && frame().selection().isCaret())
+ return style().visitedDependentColor(CSSPropertyColor).blendWithWhite();
+
+ std::unique_ptr<RenderStyle> pseudoStyle = selectionPseudoStyle();
+ if (pseudoStyle && pseudoStyle->visitedDependentColor(CSSPropertyBackgroundColor).isValid())
+ return pseudoStyle->visitedDependentColor(CSSPropertyBackgroundColor).blendWithWhite();
+
+ if (frame().selection().isFocusedAndActive())
+ return theme().activeSelectionBackgroundColor();
+ return theme().inactiveSelectionBackgroundColor();
+}
+
+bool RenderElement::getLeadingCorner(FloatPoint& point, bool& insideFixed) const
+{
+ if (!isInline() || isReplaced()) {
+ point = localToAbsolute(FloatPoint(), UseTransforms, &insideFixed);
+ return true;
+ }
+
+ // find the next text/image child, to get a position
+ const RenderObject* o = this;
+ while (o) {
+ const RenderObject* p = o;
+ if (RenderObject* child = o->firstChildSlow())
+ o = child;
+ else if (o->nextSibling())
+ o = o->nextSibling();
+ else {
+ RenderObject* next = 0;
+ while (!next && o->parent()) {
+ o = o->parent();
+ next = o->nextSibling();
+ }
+ o = next;
+
+ if (!o)
+ break;
+ }
+ ASSERT(o);
+
+ if (!o->isInline() || o->isReplaced()) {
+ point = o->localToAbsolute(FloatPoint(), UseTransforms, &insideFixed);
+ return true;
+ }
+
+ if (p->node() && p->node() == element() && is<RenderText>(*o) && !downcast<RenderText>(*o).firstTextBox()) {
+ // do nothing - skip unrendered whitespace that is a child or next sibling of the anchor
+ } else if (is<RenderText>(*o) || o->isReplaced()) {
+ point = FloatPoint();
+ if (is<RenderText>(*o) && downcast<RenderText>(*o).firstTextBox())
+ point.move(downcast<RenderText>(*o).linesBoundingBox().x(), downcast<RenderText>(*o).topOfFirstText());
+ else if (is<RenderBox>(*o))
+ point.moveBy(downcast<RenderBox>(*o).location());
+ point = o->container()->localToAbsolute(point, UseTransforms, &insideFixed);
+ return true;
+ }
+ }
+
+ // If the target doesn't have any children or siblings that could be used to calculate the scroll position, we must be
+ // at the end of the document. Scroll to the bottom. FIXME: who said anything about scrolling?
+ if (!o && document().view()) {
+ point = FloatPoint(0, document().view()->contentsHeight());
+ return true;
+ }
+ return false;
+}
+
+bool RenderElement::getTrailingCorner(FloatPoint& point, bool& insideFixed) const
+{
+ if (!isInline() || isReplaced()) {
+ point = localToAbsolute(LayoutPoint(downcast<RenderBox>(*this).size()), UseTransforms, &insideFixed);
+ return true;
+ }
+
+ // find the last text/image child, to get a position
+ const RenderObject* o = this;
+ while (o) {
+ if (RenderObject* child = o->lastChildSlow())
+ o = child;
+ else if (o->previousSibling())
+ o = o->previousSibling();
+ else {
+ RenderObject* prev = 0;
+ while (!prev) {
+ o = o->parent();
+ if (!o)
+ return false;
+ prev = o->previousSibling();
+ }
+ o = prev;
+ }
+ ASSERT(o);
+ if (is<RenderText>(*o) || o->isReplaced()) {
+ point = FloatPoint();
+ if (is<RenderText>(*o)) {
+ LayoutRect linesBox = downcast<RenderText>(*o).linesBoundingBox();
+ if (!linesBox.maxX() && !linesBox.maxY())
+ continue;
+ point.moveBy(linesBox.maxXMaxYCorner());
+ } else
+ point.moveBy(downcast<RenderBox>(*o).frameRect().maxXMaxYCorner());
+ point = o->container()->localToAbsolute(point, UseTransforms, &insideFixed);
+ return true;
+ }
+ }
+ return true;
+}
+
+LayoutRect RenderElement::absoluteAnchorRect(bool* insideFixed) const
+{
+ FloatPoint leading, trailing;
+ bool leadingInFixed = false;
+ bool trailingInFixed = false;
+ getLeadingCorner(leading, leadingInFixed);
+ getTrailingCorner(trailing, trailingInFixed);
+
+ FloatPoint upperLeft = leading;
+ FloatPoint lowerRight = trailing;
+
+ // Vertical writing modes might mean the leading point is not in the top left
+ if (!isInline() || isReplaced()) {
+ upperLeft = FloatPoint(std::min(leading.x(), trailing.x()), std::min(leading.y(), trailing.y()));
+ lowerRight = FloatPoint(std::max(leading.x(), trailing.x()), std::max(leading.y(), trailing.y()));
+ } // Otherwise, it's not obvious what to do.
+
+ if (insideFixed) {
+ // For now, just look at the leading corner. Handling one inside fixed and one not would be tricky.
+ *insideFixed = leadingInFixed;
+ }
+
+ return enclosingLayoutRect(FloatRect(upperLeft, lowerRight.expandedTo(upperLeft) - upperLeft));
+}
+
+const RenderElement* RenderElement::enclosingRendererWithTextDecoration(TextDecoration textDecoration, bool firstLine) const
+{
+ const RenderElement* current = this;
+ do {
+ if (current->isRenderBlock())
+ return current;
+ if (!current->isRenderInline() || current->isRubyText())
+ return nullptr;
+
+ const RenderStyle& styleToUse = firstLine ? current->firstLineStyle() : current->style();
+ if (styleToUse.textDecoration() & textDecoration)
+ return current;
+ current = current->parent();
+ } while (current && (!current->element() || (!is<HTMLAnchorElement>(*current->element()) && !current->element()->hasTagName(HTMLNames::fontTag))));
+
+ return current;
+}
+
+void RenderElement::drawLineForBoxSide(GraphicsContext& graphicsContext, const FloatRect& rect, BoxSide side, Color color, EBorderStyle borderStyle, float adjacentWidth1, float adjacentWidth2, bool antialias) const
+{
+ auto drawBorderRect = [&graphicsContext] (const FloatRect& rect)
+ {
+ if (rect.isEmpty())
+ return;
+ graphicsContext.drawRect(rect);
+ };
+
+ auto drawLineFor = [this, &graphicsContext, color, antialias] (const FloatRect& rect, BoxSide side, EBorderStyle borderStyle, const FloatSize& adjacent)
+ {
+ if (rect.isEmpty())
+ return;
+ drawLineForBoxSide(graphicsContext, rect, side, color, borderStyle, adjacent.width(), adjacent.height(), antialias);
+ };
+
+ float x1 = rect.x();
+ float x2 = rect.maxX();
+ float y1 = rect.y();
+ float y2 = rect.maxY();
+ float thickness;
+ float 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;
+
+ float deviceScaleFactor = document().deviceScaleFactor();
+ if (borderStyle == DOUBLE && (thickness * deviceScaleFactor) < 3)
+ borderStyle = SOLID;
+
+ switch (borderStyle) {
+ case BNONE:
+ case BHIDDEN:
+ return;
+ case DOTTED:
+ case DASHED: {
+ bool wasAntialiased = graphicsContext.shouldAntialias();
+ StrokeStyle oldStrokeStyle = graphicsContext.strokeStyle();
+ graphicsContext.setShouldAntialias(antialias);
+ graphicsContext.setStrokeColor(color);
+ graphicsContext.setStrokeThickness(thickness);
+ graphicsContext.setStrokeStyle(borderStyle == DASHED ? DashedStroke : DottedStroke);
+ graphicsContext.drawLine(roundPointToDevicePixels(LayoutPoint(x1, y1), deviceScaleFactor), roundPointToDevicePixels(LayoutPoint(x2, y2), deviceScaleFactor));
+ graphicsContext.setShouldAntialias(wasAntialiased);
+ graphicsContext.setStrokeStyle(oldStrokeStyle);
+ break;
+ }
+ case DOUBLE: {
+ float thirdOfThickness = ceilToDevicePixel(thickness / 3, deviceScaleFactor);
+ ASSERT(thirdOfThickness);
+
+ if (!adjacentWidth1 && !adjacentWidth2) {
+ StrokeStyle oldStrokeStyle = graphicsContext.strokeStyle();
+ graphicsContext.setStrokeStyle(NoStroke);
+ graphicsContext.setFillColor(color);
+
+ bool wasAntialiased = graphicsContext.shouldAntialias();
+ graphicsContext.setShouldAntialias(antialias);
+
+ switch (side) {
+ case BSTop:
+ case BSBottom:
+ drawBorderRect(snapRectToDevicePixels(x1, y1, length, thirdOfThickness, deviceScaleFactor));
+ drawBorderRect(snapRectToDevicePixels(x1, y2 - thirdOfThickness, length, thirdOfThickness, deviceScaleFactor));
+ break;
+ case BSLeft:
+ case BSRight:
+ drawBorderRect(snapRectToDevicePixels(x1, y1, thirdOfThickness, length, deviceScaleFactor));
+ drawBorderRect(snapRectToDevicePixels(x2 - thirdOfThickness, y1, thirdOfThickness, length, deviceScaleFactor));
+ break;
+ }
+
+ graphicsContext.setShouldAntialias(wasAntialiased);
+ graphicsContext.setStrokeStyle(oldStrokeStyle);
+ } else {
+ float adjacent1BigThird = ceilToDevicePixel(adjacentWidth1 / 3, deviceScaleFactor);
+ float adjacent2BigThird = ceilToDevicePixel(adjacentWidth2 / 3, deviceScaleFactor);
+
+ float offset1 = floorToDevicePixel(fabs(adjacentWidth1) * 2 / 3, deviceScaleFactor);
+ float offset2 = floorToDevicePixel(fabs(adjacentWidth2) * 2 / 3, deviceScaleFactor);
+
+ float mitreOffset1 = adjacentWidth1 < 0 ? offset1 : 0;
+ float mitreOffset2 = adjacentWidth1 > 0 ? offset1 : 0;
+ float mitreOffset3 = adjacentWidth2 < 0 ? offset2 : 0;
+ float mitreOffset4 = adjacentWidth2 > 0 ? offset2 : 0;
+
+ FloatRect paintBorderRect;
+ switch (side) {
+ case BSTop:
+ paintBorderRect = snapRectToDevicePixels(LayoutRect(x1 + mitreOffset1, y1, (x2 - mitreOffset3) - (x1 + mitreOffset1), thirdOfThickness), deviceScaleFactor);
+ drawLineFor(paintBorderRect, side, SOLID, FloatSize(adjacent1BigThird, adjacent2BigThird));
+
+ paintBorderRect = snapRectToDevicePixels(LayoutRect(x1 + mitreOffset2, y2 - thirdOfThickness, (x2 - mitreOffset4) - (x1 + mitreOffset2), thirdOfThickness), deviceScaleFactor);
+ drawLineFor(paintBorderRect, side, SOLID, FloatSize(adjacent1BigThird, adjacent2BigThird));
+ break;
+ case BSLeft:
+ paintBorderRect = snapRectToDevicePixels(LayoutRect(x1, y1 + mitreOffset1, thirdOfThickness, (y2 - mitreOffset3) - (y1 + mitreOffset1)), deviceScaleFactor);
+ drawLineFor(paintBorderRect, side, SOLID, FloatSize(adjacent1BigThird, adjacent2BigThird));
+
+ paintBorderRect = snapRectToDevicePixels(LayoutRect(x2 - thirdOfThickness, y1 + mitreOffset2, thirdOfThickness, (y2 - mitreOffset4) - (y1 + mitreOffset2)), deviceScaleFactor);
+ drawLineFor(paintBorderRect, side, SOLID, FloatSize(adjacent1BigThird, adjacent2BigThird));
+ break;
+ case BSBottom:
+ paintBorderRect = snapRectToDevicePixels(LayoutRect(x1 + mitreOffset2, y1, (x2 - mitreOffset4) - (x1 + mitreOffset2), thirdOfThickness), deviceScaleFactor);
+ drawLineFor(paintBorderRect, side, SOLID, FloatSize(adjacent1BigThird, adjacent2BigThird));
+
+ paintBorderRect = snapRectToDevicePixels(LayoutRect(x1 + mitreOffset1, y2 - thirdOfThickness, (x2 - mitreOffset3) - (x1 + mitreOffset1), thirdOfThickness), deviceScaleFactor);
+ drawLineFor(paintBorderRect, side, SOLID, FloatSize(adjacent1BigThird, adjacent2BigThird));
+ break;
+ case BSRight:
+ paintBorderRect = snapRectToDevicePixels(LayoutRect(x1, y1 + mitreOffset2, thirdOfThickness, (y2 - mitreOffset4) - (y1 + mitreOffset2)), deviceScaleFactor);
+ drawLineFor(paintBorderRect, side, SOLID, FloatSize(adjacent1BigThird, adjacent2BigThird));
+
+ paintBorderRect = snapRectToDevicePixels(LayoutRect(x2 - thirdOfThickness, y1 + mitreOffset1, thirdOfThickness, (y2 - mitreOffset3) - (y1 + mitreOffset1)), deviceScaleFactor);
+ drawLineFor(paintBorderRect, side, SOLID, FloatSize(adjacent1BigThird, adjacent2BigThird));
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case RIDGE:
+ case GROOVE: {
+ EBorderStyle s1;
+ EBorderStyle s2;
+ if (borderStyle == GROOVE) {
+ s1 = INSET;
+ s2 = OUTSET;
+ } else {
+ s1 = OUTSET;
+ s2 = INSET;
+ }
+
+ float adjacent1BigHalf = ceilToDevicePixel(adjacentWidth1 / 2, deviceScaleFactor);
+ float adjacent2BigHalf = ceilToDevicePixel(adjacentWidth2 / 2, deviceScaleFactor);
+
+ float adjacent1SmallHalf = floorToDevicePixel(adjacentWidth1 / 2, deviceScaleFactor);
+ float adjacent2SmallHalf = floorToDevicePixel(adjacentWidth2 / 2, deviceScaleFactor);
+
+ float offset1 = 0;
+ float offset2 = 0;
+ float offset3 = 0;
+ float offset4 = 0;
+
+ if (((side == BSTop || side == BSLeft) && adjacentWidth1 < 0) || ((side == BSBottom || side == BSRight) && adjacentWidth1 > 0))
+ offset1 = floorToDevicePixel(adjacentWidth1 / 2, deviceScaleFactor);
+
+ if (((side == BSTop || side == BSLeft) && adjacentWidth2 < 0) || ((side == BSBottom || side == BSRight) && adjacentWidth2 > 0))
+ offset2 = ceilToDevicePixel(adjacentWidth2 / 2, deviceScaleFactor);
+
+ if (((side == BSTop || side == BSLeft) && adjacentWidth1 > 0) || ((side == BSBottom || side == BSRight) && adjacentWidth1 < 0))
+ offset3 = floorToDevicePixel(fabs(adjacentWidth1) / 2, deviceScaleFactor);
+
+ if (((side == BSTop || side == BSLeft) && adjacentWidth2 > 0) || ((side == BSBottom || side == BSRight) && adjacentWidth2 < 0))
+ offset4 = ceilToDevicePixel(adjacentWidth2 / 2, deviceScaleFactor);
+
+ float adjustedX = ceilToDevicePixel((x1 + x2) / 2, deviceScaleFactor);
+ float adjustedY = ceilToDevicePixel((y1 + y2) / 2, deviceScaleFactor);
+ // Quads can't use the default snapping rect functions.
+ x1 = roundToDevicePixel(x1, deviceScaleFactor);
+ x2 = roundToDevicePixel(x2, deviceScaleFactor);
+ y1 = roundToDevicePixel(y1, deviceScaleFactor);
+ y2 = roundToDevicePixel(y2, deviceScaleFactor);
+
+ switch (side) {
+ case BSTop:
+ drawLineFor(FloatRect(FloatPoint(x1 + offset1, y1), FloatPoint(x2 - offset2, adjustedY)), side, s1, FloatSize(adjacent1BigHalf, adjacent2BigHalf));
+ drawLineFor(FloatRect(FloatPoint(x1 + offset3, adjustedY), FloatPoint(x2 - offset4, y2)), side, s2, FloatSize(adjacent1SmallHalf, adjacent2SmallHalf));
+ break;
+ case BSLeft:
+ drawLineFor(FloatRect(FloatPoint(x1, y1 + offset1), FloatPoint(adjustedX, y2 - offset2)), side, s1, FloatSize(adjacent1BigHalf, adjacent2BigHalf));
+ drawLineFor(FloatRect(FloatPoint(adjustedX, y1 + offset3), FloatPoint(x2, y2 - offset4)), side, s2, FloatSize(adjacent1SmallHalf, adjacent2SmallHalf));
+ break;
+ case BSBottom:
+ drawLineFor(FloatRect(FloatPoint(x1 + offset1, y1), FloatPoint(x2 - offset2, adjustedY)), side, s2, FloatSize(adjacent1BigHalf, adjacent2BigHalf));
+ drawLineFor(FloatRect(FloatPoint(x1 + offset3, adjustedY), FloatPoint(x2 - offset4, y2)), side, s1, FloatSize(adjacent1SmallHalf, adjacent2SmallHalf));
+ break;
+ case BSRight:
+ drawLineFor(FloatRect(FloatPoint(x1, y1 + offset1), FloatPoint(adjustedX, y2 - offset2)), side, s2, FloatSize(adjacent1BigHalf, adjacent2BigHalf));
+ drawLineFor(FloatRect(FloatPoint(adjustedX, y1 + offset3), FloatPoint(x2, y2 - offset4)), side, s1, FloatSize(adjacent1SmallHalf, adjacent2SmallHalf));
+ break;
+ }
+ break;
+ }
+ case INSET:
+ case OUTSET:
+ calculateBorderStyleColor(borderStyle, side, color);
+ FALLTHROUGH;
+ case SOLID: {
+ StrokeStyle oldStrokeStyle = graphicsContext.strokeStyle();
+ ASSERT(x2 >= x1);
+ ASSERT(y2 >= y1);
+ if (!adjacentWidth1 && !adjacentWidth2) {
+ graphicsContext.setStrokeStyle(NoStroke);
+ graphicsContext.setFillColor(color);
+ bool wasAntialiased = graphicsContext.shouldAntialias();
+ graphicsContext.setShouldAntialias(antialias);
+ drawBorderRect(snapRectToDevicePixels(x1, y1, x2 - x1, y2 - y1, deviceScaleFactor));
+ graphicsContext.setShouldAntialias(wasAntialiased);
+ graphicsContext.setStrokeStyle(oldStrokeStyle);
+ return;
+ }
+
+ // FIXME: These roundings should be replaced by ASSERT(device pixel positioned) when all the callers have transitioned to device pixels.
+ x1 = roundToDevicePixel(x1, deviceScaleFactor);
+ y1 = roundToDevicePixel(y1, deviceScaleFactor);
+ x2 = roundToDevicePixel(x2, deviceScaleFactor);
+ y2 = roundToDevicePixel(y2, deviceScaleFactor);
+
+ Vector<FloatPoint> quad;
+ quad.reserveInitialCapacity(4);
+ switch (side) {
+ case BSTop:
+ quad.uncheckedAppend({ x1 + std::max<float>(-adjacentWidth1, 0), y1 });
+ quad.uncheckedAppend({ x1 + std::max<float>( adjacentWidth1, 0), y2 });
+ quad.uncheckedAppend({ x2 - std::max<float>( adjacentWidth2, 0), y2 });
+ quad.uncheckedAppend({ x2 - std::max<float>(-adjacentWidth2, 0), y1 });
+ break;
+ case BSBottom:
+ quad.uncheckedAppend({ x1 + std::max<float>( adjacentWidth1, 0), y1 });
+ quad.uncheckedAppend({ x1 + std::max<float>(-adjacentWidth1, 0), y2 });
+ quad.uncheckedAppend({ x2 - std::max<float>(-adjacentWidth2, 0), y2 });
+ quad.uncheckedAppend({ x2 - std::max<float>( adjacentWidth2, 0), y1 });
+ break;
+ case BSLeft:
+ quad.uncheckedAppend({ x1, y1 + std::max<float>(-adjacentWidth1, 0) });
+ quad.uncheckedAppend({ x1, y2 - std::max<float>(-adjacentWidth2, 0) });
+ quad.uncheckedAppend({ x2, y2 - std::max<float>( adjacentWidth2, 0) });
+ quad.uncheckedAppend({ x2, y1 + std::max<float>( adjacentWidth1, 0) });
+ break;
+ case BSRight:
+ quad.uncheckedAppend({ x1, y1 + std::max<float>( adjacentWidth1, 0) });
+ quad.uncheckedAppend({ x1, y2 - std::max<float>( adjacentWidth2, 0) });
+ quad.uncheckedAppend({ x2, y2 - std::max<float>(-adjacentWidth2, 0) });
+ quad.uncheckedAppend({ x2, y1 + std::max<float>(-adjacentWidth1, 0) });
+ break;
+ }
+
+ graphicsContext.setStrokeStyle(NoStroke);
+ graphicsContext.setFillColor(color);
+ bool wasAntialiased = graphicsContext.shouldAntialias();
+ graphicsContext.setShouldAntialias(antialias);
+ graphicsContext.fillPath(Path::polygonPathFromPoints(quad));
+ graphicsContext.setShouldAntialias(wasAntialiased);
+
+ graphicsContext.setStrokeStyle(oldStrokeStyle);
+ break;
+ }
+ }
+}
+
+void RenderElement::paintFocusRing(PaintInfo& paintInfo, const RenderStyle& style, const Vector<LayoutRect>& focusRingRects)
+{
+ ASSERT(style.outlineStyleIsAuto());
+ float outlineOffset = style.outlineOffset();
+ Vector<FloatRect> pixelSnappedFocusRingRects;
+ float deviceScaleFactor = document().deviceScaleFactor();
+ for (auto rect : focusRingRects) {
+ rect.inflate(outlineOffset);
+ pixelSnappedFocusRingRects.append(snapRectToDevicePixels(rect, deviceScaleFactor));
+ }
+#if PLATFORM(MAC)
+ bool needsRepaint;
+ if (style.hasBorderRadius()) {
+ Path path = PathUtilities::pathWithShrinkWrappedRectsForOutline(pixelSnappedFocusRingRects, style.border(), outlineOffset, style.direction(), style.writingMode(),
+ document().deviceScaleFactor());
+ if (path.isEmpty()) {
+ for (auto rect : pixelSnappedFocusRingRects)
+ path.addRect(rect);
+ }
+ paintInfo.context().drawFocusRing(path, page().focusController().timeSinceFocusWasSet(), needsRepaint);
+ } else
+ paintInfo.context().drawFocusRing(pixelSnappedFocusRingRects, page().focusController().timeSinceFocusWasSet(), needsRepaint);
+ if (needsRepaint)
+ page().focusController().setFocusedElementNeedsRepaint();
+#else
+ paintInfo.context().drawFocusRing(pixelSnappedFocusRingRects, style.outlineWidth(), style.outlineOffset(), style.visitedDependentColor(CSSPropertyOutlineColor));
+#endif
+}
+
+void RenderElement::paintOutline(PaintInfo& paintInfo, const LayoutRect& paintRect)
+{
+ GraphicsContext& graphicsContext = paintInfo.context();
+ if (graphicsContext.paintingDisabled())
+ return;
+
+ if (!hasOutline())
+ return;
+
+ auto& styleToUse = style();
+ float outlineWidth = floorToDevicePixel(styleToUse.outlineWidth(), document().deviceScaleFactor());
+ float outlineOffset = floorToDevicePixel(styleToUse.outlineOffset(), document().deviceScaleFactor());
+
+ // Only paint the focus ring by hand if the theme isn't able to draw it.
+ if (styleToUse.outlineStyleIsAuto() && !theme().supportsFocusRing(styleToUse)) {
+ Vector<LayoutRect> focusRingRects;
+ addFocusRingRects(focusRingRects, paintRect.location(), paintInfo.paintContainer);
+ paintFocusRing(paintInfo, styleToUse, focusRingRects);
+ }
+
+ if (hasOutlineAnnotation() && !styleToUse.outlineStyleIsAuto() && !theme().supportsFocusRing(styleToUse))
+ addPDFURLRect(paintInfo, paintRect.location());
+
+ if (styleToUse.outlineStyleIsAuto() || styleToUse.outlineStyle() == BNONE)
+ return;
+
+ FloatRect outer = paintRect;
+ outer.inflate(outlineOffset + outlineWidth);
+ FloatRect inner = outer;
+ inner.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);
+
+ bool useTransparencyLayer = !outlineColor.isOpaque();
+ if (useTransparencyLayer) {
+ if (outlineStyle == SOLID) {
+ Path path;
+ path.addRect(outer);
+ path.addRect(inner);
+ graphicsContext.setFillRule(RULE_EVENODD);
+ graphicsContext.setFillColor(outlineColor);
+ graphicsContext.fillPath(path);
+ return;
+ }
+ graphicsContext.beginTransparencyLayer(outlineColor.alphaAsFloat());
+ outlineColor = outlineColor.opaqueColor();
+ }
+
+ float leftOuter = outer.x();
+ float leftInner = inner.x();
+ float rightOuter = outer.maxX();
+ float rightInner = std::min(inner.maxX(), rightOuter);
+ float topOuter = outer.y();
+ float topInner = inner.y();
+ float bottomOuter = outer.maxY();
+ float bottomInner = std::min(inner.maxY(), bottomOuter);
+
+ drawLineForBoxSide(graphicsContext, FloatRect(FloatPoint(leftOuter, topOuter), FloatPoint(leftInner, bottomOuter)), BSLeft, outlineColor, outlineStyle, outlineWidth, outlineWidth);
+ drawLineForBoxSide(graphicsContext, FloatRect(FloatPoint(leftOuter, topOuter), FloatPoint(rightOuter, topInner)), BSTop, outlineColor, outlineStyle, outlineWidth, outlineWidth);
+ drawLineForBoxSide(graphicsContext, FloatRect(FloatPoint(rightInner, topOuter), FloatPoint(rightOuter, bottomOuter)), BSRight, outlineColor, outlineStyle, outlineWidth, outlineWidth);
+ drawLineForBoxSide(graphicsContext, FloatRect(FloatPoint(leftOuter, bottomInner), FloatPoint(rightOuter, bottomOuter)), BSBottom, outlineColor, outlineStyle, outlineWidth, outlineWidth);
+
+ if (useTransparencyLayer)
+ graphicsContext.endTransparencyLayer();
+}
+
+void RenderElement::issueRepaintForOutlineAuto(float outlineSize)
+{
+ LayoutRect repaintRect;
+ Vector<LayoutRect> focusRingRects;
+ addFocusRingRects(focusRingRects, LayoutPoint(), containerForRepaint());
+ for (auto rect : focusRingRects) {
+ rect.inflate(outlineSize);
+ repaintRect.unite(rect);
+ }
+ repaintRectangle(repaintRect);
+}
+
+void RenderElement::updateOutlineAutoAncestor(bool hasOutlineAuto)
+{
+ for (auto& child : childrenOfType<RenderObject>(*this)) {
+ if (hasOutlineAuto == child.hasOutlineAutoAncestor())
+ continue;
+ child.setHasOutlineAutoAncestor(hasOutlineAuto);
+ bool childHasOutlineAuto = child.outlineStyleForRepaint().outlineStyleIsAuto();
+ if (childHasOutlineAuto)
+ continue;
+ if (!is<RenderElement>(child))
+ continue;
+ downcast<RenderElement>(child).updateOutlineAutoAncestor(hasOutlineAuto);
+ }
+ if (hasContinuation())
+ downcast<RenderBoxModelObject>(*this).continuation()->updateOutlineAutoAncestor(hasOutlineAuto);
+}
+
+bool RenderElement::hasOutlineAnnotation() const
+{
+ return element() && element()->isLink() && document().printing();
+}
+
+bool RenderElement::hasSelfPaintingLayer() const
+{
+ if (!hasLayer())
+ return false;
+ auto& layerModelObject = downcast<RenderLayerModelObject>(*this);
+ return layerModelObject.hasSelfPaintingLayer();
+}
+
+bool RenderElement::checkForRepaintDuringLayout() const
+{
+ return !document().view()->needsFullRepaint() && everHadLayout() && !hasSelfPaintingLayer();
+}
+
+RespectImageOrientationEnum RenderElement::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 settings().shouldRespectImageOrientation() && is<HTMLImageElement>(element()) ? RespectImageOrientation : DoNotRespectImageOrientation;
+}
+
+void RenderElement::adjustFlowThreadStateOnContainingBlockChangeIfNeeded()
+{
+ if (flowThreadState() == NotInsideFlowThread)
+ return;
+
+ // Invalidate the containing block caches.
+ if (is<RenderBlock>(*this))
+ downcast<RenderBlock>(*this).resetFlowThreadContainingBlockAndChildInfoIncludingDescendants();
+
+ // Adjust the flow tread state on the subtree.
+ setFlowThreadState(RenderObject::computedFlowThreadState(*this));
+ for (auto& descendant : descendantsOfType<RenderObject>(*this))
+ descendant.setFlowThreadState(RenderObject::computedFlowThreadState(descendant));
+}
+
+void RenderElement::removeFromRenderFlowThread()
+{
+ ASSERT(flowThreadState() != NotInsideFlowThread);
+ // Sometimes we remove the element from the flow, but it's not destroyed at that time.
+ // It's only until later when we actually destroy it and remove all the children from it.
+ // Currently, that happens for firstLetter elements and list markers.
+ // Pass in the flow thread so that we don't have to look it up for all the children.
+ removeFromRenderFlowThreadIncludingDescendants(true);
+}
+
+void RenderElement::removeFromRenderFlowThreadIncludingDescendants(bool shouldUpdateState)
+{
+ // 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 (auto& child : childrenOfType<RenderObject>(*this)) {
+ if (is<RenderElement>(child)) {
+ downcast<RenderElement>(child).removeFromRenderFlowThreadIncludingDescendants(shouldUpdateState);
+ continue;
+ }
+ if (shouldUpdateState)
+ child.setFlowThreadState(NotInsideFlowThread);
+ }
+
+ // 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;
+ auto* parent = flowThreadContainingBlock->parent();
+ if (!parent)
+ break;
+ flowThreadContainingBlock = parent->flowThreadContainingBlock();
+ }
+ if (is<RenderBlock>(*this))
+ downcast<RenderBlock>(*this).setCachedFlowThreadContainingBlockNeedsUpdate();
+
+ if (shouldUpdateState)
+ setFlowThreadState(NotInsideFlowThread);
+}
+
+#if ENABLE(TEXT_AUTOSIZING)
+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 RenderElement::adjustComputedFontSizesOnBlocks(float size, float visibleWidth)
+{
+ Document* document = view().frameView().frame().document();
+ if (!document)
+ return;
+
+ Vector<int> depthStack;
+ int currentDepth = 0;
+ int newFixedDepth = 0;
+
+ // We don't apply autosizing to nodes with fixed height normally.
+ // But we apply it to nodes which are located deep enough
+ // (nesting depth is greater than some const) inside of a parent block
+ // which has fixed height but its content overflows intentionally.
+ for (RenderObject* descendent = traverseNext(this, includeNonFixedHeight, currentDepth, newFixedDepth); descendent; descendent = descendent->traverseNext(this, includeNonFixedHeight, currentDepth, newFixedDepth)) {
+ while (depthStack.size() > 0 && currentDepth <= depthStack[depthStack.size() - 1])
+ depthStack.remove(depthStack.size() - 1);
+ if (newFixedDepth)
+ depthStack.append(newFixedDepth);
+
+ int stackSize = depthStack.size();
+ if (is<RenderBlockFlow>(*descendent) && !descendent->isListItem() && (!stackSize || currentDepth - depthStack[stackSize - 1] > TextAutoSizingFixedHeightDepth))
+ downcast<RenderBlockFlow>(*descendent).adjustComputedFontSizes(size, visibleWidth);
+ newFixedDepth = 0;
+ }
+
+ // Remove style from auto-sizing table that are no longer valid.
+ document->updateAutoSizedNodes();
+}
+
+void RenderElement::resetTextAutosizing()
+{
+ Document* document = view().frameView().frame().document();
+ if (!document)
+ return;
+
+ LOG(TextAutosizing, "RenderElement::resetTextAutosizing()");
+
+ document->clearAutoSizedNodes();
+
+ Vector<int> depthStack;
+ int currentDepth = 0;
+ int newFixedDepth = 0;
+
+ for (RenderObject* descendent = traverseNext(this, includeNonFixedHeight, currentDepth, newFixedDepth); descendent; descendent = descendent->traverseNext(this, includeNonFixedHeight, currentDepth, newFixedDepth)) {
+ while (depthStack.size() > 0 && currentDepth <= depthStack[depthStack.size() - 1])
+ depthStack.remove(depthStack.size() - 1);
+ if (newFixedDepth)
+ depthStack.append(newFixedDepth);
+
+ int stackSize = depthStack.size();
+ if (is<RenderBlockFlow>(*descendent) && !descendent->isListItem() && (!stackSize || currentDepth - depthStack[stackSize - 1] > TextAutoSizingFixedHeightDepth))
+ downcast<RenderBlockFlow>(*descendent).resetComputedFontSize();
+ newFixedDepth = 0;
+ }
+}
+#endif // ENABLE(TEXT_AUTOSIZING)
+
}