diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/rendering/RenderElement.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/rendering/RenderElement.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderElement.cpp | 1816 |
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) + } |