#include "config.h"
#include "RenderLayer.h"
#include "BoxShape.h"
#include "CSSAnimationController.h"
#include "CSSFilter.h"
#include "CSSPropertyNames.h"
#include "Chrome.h"
#include "DebugPageOverlays.h"
#include "DeprecatedGlobalSettings.h"
#include "Document.h"
#include "DocumentEventQueue.h"
#include "DocumentMarkerController.h"
#include "DocumentTimeline.h"
#include "Element.h"
#include "EventHandler.h"
#include "FEColorMatrix.h"
#include "FEMerge.h"
#include "FloatConversion.h"
#include "FloatPoint3D.h"
#include "FloatRect.h"
#include "FloatRoundedRect.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameLoaderClient.h"
#include "FrameSelection.h"
#include "FrameTree.h"
#include "FrameView.h"
#include "Gradient.h"
#include "GraphicsContext.h"
#include "HTMLFormControlElement.h"
#include "HTMLFrameElement.h"
#include "HTMLFrameOwnerElement.h"
#include "HTMLIFrameElement.h"
#include "HTMLNames.h"
#include "HTMLParserIdioms.h"
#include "HitTestRequest.h"
#include "HitTestResult.h"
#include "HitTestingTransformState.h"
#include "Logging.h"
#include "OverflowEvent.h"
#include "OverlapTestRequestClient.h"
#include "Page.h"
#include "PlatformMouseEvent.h"
#include "RenderFlexibleBox.h"
#include "RenderFragmentContainer.h"
#include "RenderFragmentedFlow.h"
#include "RenderGeometryMap.h"
#include "RenderImage.h"
#include "RenderInline.h"
#include "RenderIterator.h"
#include "RenderLayerBacking.h"
#include "RenderLayerCompositor.h"
#include "RenderLayerFilters.h"
#include "RenderMarquee.h"
#include "RenderMultiColumnFlow.h"
#include "RenderReplica.h"
#include "RenderSVGResourceClipper.h"
#include "RenderSVGRoot.h"
#include "RenderScrollbar.h"
#include "RenderScrollbarPart.h"
#include "RenderTableCell.h"
#include "RenderTableRow.h"
#include "RenderText.h"
#include "RenderTheme.h"
#include "RenderTreeAsText.h"
#include "RenderView.h"
#include "RuntimeEnabledFeatures.h"
#include "SVGNames.h"
#include "ScaleTransformOperation.h"
#include "ScriptDisallowedScope.h"
#include "ScrollAnimator.h"
#include "Scrollbar.h"
#include "ScrollbarTheme.h"
#include "ScrollingCoordinator.h"
#include "Settings.h"
#include "ShadowRoot.h"
#include "SourceGraphic.h"
#include "StyleProperties.h"
#include "StyleResolver.h"
#include "TransformationMatrix.h"
#include "TranslateTransformOperation.h"
#include "WheelEventTestTrigger.h"
#include <stdio.h>
#include <wtf/MonotonicTime.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/CString.h>
#include <wtf/text/TextStream.h>
#if ENABLE(CSS_SCROLL_SNAP)
#include "AxisScrollSnapOffsets.h"
#endif
#define MIN_INTERSECT_FOR_REVEAL 32
namespace WebCore {
using namespace HTMLNames;
class ClipRects : public RefCounted<ClipRects> {
WTF_MAKE_FAST_ALLOCATED;
public:
static Ref<ClipRects> create()
{
return adoptRef(*new ClipRects);
}
static Ref<ClipRects> create(const ClipRects& other)
{
return adoptRef(*new ClipRects(other));
}
void reset()
{
m_overflowClipRect.reset();
m_fixedClipRect.reset();
m_posClipRect.reset();
m_fixed = false;
}
const ClipRect& overflowClipRect() const { return m_overflowClipRect; }
void setOverflowClipRect(const ClipRect& clipRect) { m_overflowClipRect = clipRect; }
const ClipRect& fixedClipRect() const { return m_fixedClipRect; }
void setFixedClipRect(const ClipRect& clipRect) { m_fixedClipRect = clipRect; }
const ClipRect& posClipRect() const { return m_posClipRect; }
void setPosClipRect(const ClipRect& clipRect) { m_posClipRect = clipRect; }
bool fixed() const { return m_fixed; }
void setFixed(bool fixed) { m_fixed = fixed; }
bool operator==(const ClipRects& other) const
{
return m_overflowClipRect == other.overflowClipRect()
&& m_fixedClipRect == other.fixedClipRect()
&& m_posClipRect == other.posClipRect()
&& m_fixed == other.fixed();
}
ClipRects& operator=(const ClipRects& other)
{
m_overflowClipRect = other.overflowClipRect();
m_fixedClipRect = other.fixedClipRect();
m_posClipRect = other.posClipRect();
m_fixed = other.fixed();
return *this;
}
private:
ClipRects() = default;
ClipRects(const LayoutRect& clipRect)
: m_overflowClipRect(clipRect)
, m_fixedClipRect(clipRect)
, m_posClipRect(clipRect)
{
}
ClipRects(const ClipRects& other)
: RefCounted()
, m_fixed(other.fixed())
, m_overflowClipRect(other.overflowClipRect())
, m_fixedClipRect(other.fixedClipRect())
, m_posClipRect(other.posClipRect())
{
}
bool m_fixed { false };
ClipRect m_overflowClipRect;
ClipRect m_fixedClipRect;
ClipRect m_posClipRect;
};
class ClipRectsCache {
WTF_MAKE_FAST_ALLOCATED;
public:
ClipRectsCache()
{
#ifndef NDEBUG
for (int i = 0; i < NumCachedClipRectsTypes; ++i) {
m_clipRectsRoot[i] = 0;
m_scrollbarRelevancy[i] = IgnoreOverlayScrollbarSize;
}
#endif
}
ClipRects* getClipRects(ClipRectsType clipRectsType, ShouldRespectOverflowClip respectOverflow) const
{
return m_clipRects[getIndex(clipRectsType, respectOverflow)].get();
}
void setClipRects(ClipRectsType clipRectsType, ShouldRespectOverflowClip respectOverflow, RefPtr<ClipRects>&& clipRects)
{
m_clipRects[getIndex(clipRectsType, respectOverflow)] = WTFMove(clipRects);
}
#ifndef NDEBUG
const RenderLayer* m_clipRectsRoot[NumCachedClipRectsTypes];
OverlayScrollbarSizeRelevancy m_scrollbarRelevancy[NumCachedClipRectsTypes];
#endif
private:
unsigned getIndex(ClipRectsType clipRectsType, ShouldRespectOverflowClip respectOverflow) const
{
unsigned index = static_cast<unsigned>(clipRectsType);
if (respectOverflow == RespectOverflowClip)
index += static_cast<unsigned>(NumCachedClipRectsTypes);
ASSERT_WITH_SECURITY_IMPLICATION(index < NumCachedClipRectsTypes * 2);
return index;
}
RefPtr<ClipRects> m_clipRects[NumCachedClipRectsTypes * 2];
};
void makeMatrixRenderable(TransformationMatrix& matrix, bool has3DRendering)
{
#if !ENABLE(3D_TRANSFORMS)
UNUSED_PARAM(has3DRendering);
matrix.makeAffine();
#else
if (!has3DRendering)
matrix.makeAffine();
#endif
}
RenderLayer::RenderLayer(RenderLayerModelObject& rendererLayerModelObject)
: m_isRenderViewLayer(rendererLayerModelObject.isRenderView())
, m_forcedStackingContext(rendererLayerModelObject.isMedia())
, m_zOrderListsDirty(false)
, m_normalFlowListDirty(true)
, m_hadNegativeZOrderList(false)
, m_inResizeMode(false)
, m_scrollDimensionsDirty(true)
, m_hasSelfPaintingLayerDescendant(false)
, m_hasSelfPaintingLayerDescendantDirty(false)
, m_usedTransparency(false)
, m_paintingInsideReflection(false)
, m_inOverflowRelayout(false)
, m_repaintStatus(NeedsNormalRepaint)
, m_visibleContentStatusDirty(true)
, m_hasVisibleContent(false)
, m_visibleDescendantStatusDirty(false)
, m_hasVisibleDescendant(false)
, m_registeredScrollableArea(false)
, m_isFixedIntersectingViewport(false)
, m_3DTransformedDescendantStatusDirty(true)
, m_has3DTransformedDescendant(false)
, m_hasCompositingDescendant(false)
, m_hasTransformedAncestor(false)
, m_has3DTransformedAncestor(false)
, m_indirectCompositingReason(static_cast<unsigned>(IndirectCompositingReason::None))
, m_viewportConstrainedNotCompositedReason(NoNotCompositedReason)
#if PLATFORM(IOS_FAMILY)
, m_adjustForIOSCaretWhenScrolling(false)
#endif
#if PLATFORM(IOS_FAMILY)
#if ENABLE(IOS_TOUCH_EVENTS)
, m_registeredAsTouchEventListenerForScrolling(false)
#endif
, m_inUserScroll(false)
, m_requiresScrollBoundsOriginUpdate(false)
#endif
, m_containsDirtyOverlayScrollbars(false)
, m_updatingMarqueePosition(false)
#if !ASSERT_DISABLED
, m_layerListMutationAllowed(true)
#endif
#if ENABLE(CSS_COMPOSITING)
, m_blendMode(static_cast<unsigned>(BlendMode::Normal))
, m_hasNotIsolatedCompositedBlendingDescendants(false)
, m_hasNotIsolatedBlendingDescendants(false)
, m_hasNotIsolatedBlendingDescendantsStatusDirty(false)
#endif
, m_renderer(rendererLayerModelObject)
{
setIsNormalFlowOnly(shouldBeNormalFlowOnly());
setIsStackingContext(shouldBeStackingContext());
m_isSelfPaintingLayer = shouldBeSelfPaintingLayer();
if (!renderer().firstChild()) {
m_visibleContentStatusDirty = false;
m_hasVisibleContent = renderer().style().visibility() == Visibility::Visible;
}
if (Element* element = renderer().element()) {
m_scrollPosition = element->savedLayerScrollPosition();
if (!m_scrollPosition.isZero())
scrollAnimator().setCurrentPosition(m_scrollPosition);
element->setSavedLayerScrollPosition(IntPoint());
}
}
RenderLayer::~RenderLayer()
{
if (inResizeMode())
renderer().frame().eventHandler().resizeLayerDestroyed();
ASSERT(m_registeredScrollableArea == renderer().view().frameView().containsScrollableArea(this));
if (m_registeredScrollableArea)
renderer().view().frameView().removeScrollableArea(this);
#if ENABLE(IOS_TOUCH_EVENTS)
unregisterAsTouchEventListenerForScrolling();
#endif
if (Element* element = renderer().element())
element->setSavedLayerScrollPosition(m_scrollPosition);
destroyScrollbar(HorizontalScrollbar);
destroyScrollbar(VerticalScrollbar);
if (auto* scrollingCoordinator = renderer().page().scrollingCoordinator())
scrollingCoordinator->willDestroyScrollableArea(*this);
if (m_reflection)
removeReflection();
clearScrollCorner();
clearResizer();
clearLayerFilters();
clearBacking(true);
RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(renderer().renderTreeBeingDestroyed() || !parent());
RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(renderer().renderTreeBeingDestroyed() || !firstChild());
}
void RenderLayer::addChild(RenderLayer& child, RenderLayer* beforeChild)
{
RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild();
if (prevSibling) {
child.setPreviousSibling(prevSibling);
prevSibling->setNextSibling(&child);
ASSERT(prevSibling != &child);
} else
setFirstChild(&child);
if (beforeChild) {
beforeChild->setPreviousSibling(&child);
child.setNextSibling(beforeChild);
ASSERT(beforeChild != &child);
} else
setLastChild(&child);
child.setParent(this);
dirtyPaintOrderListsOnChildChange(child);
child.updateDescendantDependentFlags();
if (child.m_hasVisibleContent || child.m_hasVisibleDescendant)
setAncestorChainHasVisibleDescendant();
if (child.isSelfPaintingLayer() || child.hasSelfPaintingLayerDescendant())
setAncestorChainHasSelfPaintingLayerDescendant();
if (compositor().hasContentCompositingLayers())
setDescendantsNeedCompositingRequirementsTraversal();
if (child.hasDescendantNeedingCompositingRequirementsTraversal() || child.needsCompositingRequirementsTraversal())
child.setAncestorsHaveCompositingDirtyFlag(Compositing::HasDescendantNeedingRequirementsTraversal);
if (child.hasDescendantNeedingUpdateBackingOrHierarchyTraversal() || child.needsUpdateBackingOrHierarchyTraversal())
child.setAncestorsHaveCompositingDirtyFlag(Compositing::HasDescendantNeedingBackingOrHierarchyTraversal);
#if ENABLE(CSS_COMPOSITING)
if (child.hasBlendMode() || (child.hasNotIsolatedBlendingDescendants() && !child.isolatesBlending()))
updateAncestorChainHasBlendingDescendants(); #endif
compositor().layerWasAdded(*this, child);
}
void RenderLayer::removeChild(RenderLayer& oldChild)
{
if (!renderer().renderTreeBeingDestroyed())
compositor().layerWillBeRemoved(*this, oldChild);
if (oldChild.previousSibling())
oldChild.previousSibling()->setNextSibling(oldChild.nextSibling());
if (oldChild.nextSibling())
oldChild.nextSibling()->setPreviousSibling(oldChild.previousSibling());
if (m_first == &oldChild)
m_first = oldChild.nextSibling();
if (m_last == &oldChild)
m_last = oldChild.previousSibling();
dirtyPaintOrderListsOnChildChange(oldChild);
oldChild.setPreviousSibling(nullptr);
oldChild.setNextSibling(nullptr);
oldChild.setParent(nullptr);
oldChild.updateDescendantDependentFlags();
if (oldChild.m_hasVisibleContent || oldChild.m_hasVisibleDescendant)
dirtyAncestorChainVisibleDescendantStatus();
if (oldChild.isSelfPaintingLayer() || oldChild.hasSelfPaintingLayerDescendant())
dirtyAncestorChainHasSelfPaintingLayerDescendantStatus();
if (compositor().hasContentCompositingLayers())
setDescendantsNeedCompositingRequirementsTraversal();
#if ENABLE(CSS_COMPOSITING)
if (oldChild.hasBlendMode() || (oldChild.hasNotIsolatedBlendingDescendants() && !oldChild.isolatesBlending()))
dirtyAncestorChainHasBlendingDescendants();
#endif
}
void RenderLayer::dirtyPaintOrderListsOnChildChange(RenderLayer& child)
{
if (child.isNormalFlowOnly())
dirtyNormalFlowList();
if (!child.isNormalFlowOnly() || child.firstChild()) {
child.dirtyStackingContextZOrderLists();
}
}
void RenderLayer::insertOnlyThisLayer()
{
if (!m_parent && renderer().parent()) {
RenderLayer* parentLayer = renderer().parent()->enclosingLayer();
ASSERT(parentLayer);
RenderLayer* beforeChild = parentLayer->reflectionLayer() != this ? renderer().parent()->findNextLayer(parentLayer, &renderer()) : nullptr;
parentLayer->addChild(*this, beforeChild);
}
for (auto& child : childrenOfType<RenderElement>(renderer()))
child.moveLayers(m_parent, this);
clearClipRectsIncludingDescendants();
}
void RenderLayer::removeOnlyThisLayer()
{
if (!m_parent)
return;
renderer().setHasLayer(false);
compositor().layerWillBeRemoved(*m_parent, *this);
clearClipRectsIncludingDescendants();
RenderLayer* nextSib = nextSibling();
if (reflection())
removeChild(*reflectionLayer());
RenderLayer* current = m_first;
while (current) {
RenderLayer* next = current->nextSibling();
removeChild(*current);
m_parent->addChild(*current, nextSib);
current->setRepaintStatus(NeedsFullRepaint);
current = next;
}
m_parent->removeChild(*this);
renderer().destroyLayer();
}
static bool canCreateStackingContext(const RenderLayer& layer)
{
auto& renderer = layer.renderer();
return renderer.hasTransformRelatedProperty()
|| renderer.hasClipPath()
|| renderer.hasFilter()
|| renderer.hasMask()
|| renderer.hasBackdropFilter()
#if ENABLE(CSS_COMPOSITING)
|| renderer.hasBlendMode()
#endif
|| renderer.isTransparent()
|| renderer.isPositioned() || renderer.hasReflection()
|| renderer.style().hasIsolation()
|| !renderer.style().hasAutoZIndex()
|| (renderer.style().willChange() && renderer.style().willChange()->canCreateStackingContext());
}
bool RenderLayer::shouldBeNormalFlowOnly() const
{
if (canCreateStackingContext(*this))
return false;
return renderer().hasOverflowClip()
|| renderer().isCanvas()
|| renderer().isVideo()
|| renderer().isEmbeddedObject()
|| renderer().isRenderIFrame()
|| (renderer().isRenderImage() && downcast<RenderImage>(renderer()).isEditableImage())
|| (renderer().style().specifiesColumns() && !isRenderViewLayer())
|| renderer().isInFlowRenderFragmentedFlow();
}
bool RenderLayer::shouldBeStackingContext() const
{
return !renderer().style().hasAutoZIndex() || isRenderViewLayer() || isForcedStackingContext();
}
bool RenderLayer::setIsNormalFlowOnly(bool isNormalFlowOnly)
{
if (isNormalFlowOnly == m_isNormalFlowOnly)
return false;
m_isNormalFlowOnly = isNormalFlowOnly;
if (auto* p = parent())
p->dirtyNormalFlowList();
dirtyStackingContextZOrderLists();
return true;
}
bool RenderLayer::setIsStackingContext(bool isStackingContext)
{
if (isStackingContext == m_isStackingContext)
return false;
m_isStackingContext = isStackingContext;
dirtyStackingContextZOrderLists();
if (isStackingContext)
dirtyZOrderLists();
else
clearZOrderLists();
return true;
}
void RenderLayer::setParent(RenderLayer* parent)
{
if (parent == m_parent)
return;
if (m_parent && !renderer().renderTreeBeingDestroyed())
compositor().layerWillBeRemoved(*m_parent, *this);
m_parent = parent;
if (m_parent && !renderer().renderTreeBeingDestroyed())
compositor().layerWasAdded(*m_parent, *this);
}
RenderLayer* RenderLayer::stackingContext() const
{
auto* layer = parent();
while (layer && !layer->isStackingContext())
layer = layer->parent();
ASSERT(!layer || layer->isStackingContext());
return layer;
}
void RenderLayer::dirtyZOrderLists()
{
ASSERT(layerListMutationAllowed());
ASSERT(isStackingContext());
if (m_posZOrderList)
m_posZOrderList->clear();
if (m_negZOrderList)
m_negZOrderList->clear();
m_zOrderListsDirty = true;
if (hasCompositingDescendant())
setNeedsCompositingPaintOrderChildrenUpdate();
}
void RenderLayer::dirtyStackingContextZOrderLists()
{
if (auto* sc = stackingContext())
sc->dirtyZOrderLists();
}
void RenderLayer::dirtyNormalFlowList()
{
ASSERT(layerListMutationAllowed());
if (m_normalFlowList)
m_normalFlowList->clear();
m_normalFlowListDirty = true;
if (hasCompositingDescendant())
setNeedsCompositingPaintOrderChildrenUpdate();
}
void RenderLayer::updateNormalFlowList()
{
if (!m_normalFlowListDirty)
return;
ASSERT(layerListMutationAllowed());
for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
if (child->isNormalFlowOnly() && !isReflectionLayer(*child)) {
if (!m_normalFlowList)
m_normalFlowList = std::make_unique<Vector<RenderLayer*>>();
m_normalFlowList->append(child);
}
}
m_normalFlowListDirty = false;
}
void RenderLayer::rebuildZOrderLists()
{
ASSERT(layerListMutationAllowed());
ASSERT(isDirtyStackingContext());
rebuildZOrderLists(m_posZOrderList, m_negZOrderList);
m_zOrderListsDirty = false;
bool hasNegativeZOrderList = m_negZOrderList && m_negZOrderList->size();
if (hasNegativeZOrderList != m_hadNegativeZOrderList) {
m_hadNegativeZOrderList = hasNegativeZOrderList;
if (isComposited())
setNeedsCompositingConfigurationUpdate();
}
}
void RenderLayer::rebuildZOrderLists(std::unique_ptr<Vector<RenderLayer*>>& posZOrderList, std::unique_ptr<Vector<RenderLayer*>>& negZOrderList)
{
bool includeHiddenLayers = compositor().usesCompositing();
for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
if (!isReflectionLayer(*child))
child->collectLayers(includeHiddenLayers, posZOrderList, negZOrderList);
}
auto compareZIndex = [] (const RenderLayer* first, const RenderLayer* second) -> bool {
return first->zIndex() < second->zIndex();
};
if (posZOrderList)
std::stable_sort(posZOrderList->begin(), posZOrderList->end(), compareZIndex);
if (negZOrderList)
std::stable_sort(negZOrderList->begin(), negZOrderList->end(), compareZIndex);
}
void RenderLayer::collectLayers(bool includeHiddenLayers, std::unique_ptr<Vector<RenderLayer*>>& positiveZOrderList, std::unique_ptr<Vector<RenderLayer*>>& negativeZOrderList)
{
updateDescendantDependentFlags();
bool isStacking = isStackingContext();
bool includeHiddenLayer = includeHiddenLayers || (m_hasVisibleContent || (m_hasVisibleDescendant && isStacking));
if (includeHiddenLayer && !isNormalFlowOnly()) {
auto& layerList = (zIndex() >= 0) ? positiveZOrderList : negativeZOrderList;
if (!layerList)
layerList = std::make_unique<Vector<RenderLayer*>>();
layerList->append(this);
}
if ((includeHiddenLayers || m_hasVisibleDescendant) && !isStacking) {
for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
if (!isReflectionLayer(*child))
child->collectLayers(includeHiddenLayers, positiveZOrderList, negativeZOrderList);
}
}
}
void RenderLayer::setAncestorsHaveCompositingDirtyFlag(Compositing flag)
{
for (auto* layer = paintOrderParent(); layer; layer = layer->paintOrderParent()) {
if (layer->m_compositingDirtyBits.contains(flag))
break;
layer->m_compositingDirtyBits.add(flag);
}
}
void RenderLayer::updateLayerListsIfNeeded()
{
updateZOrderLists();
updateNormalFlowList();
if (RenderLayer* reflectionLayer = this->reflectionLayer()) {
reflectionLayer->updateZOrderLists();
reflectionLayer->updateNormalFlowList();
}
}
String RenderLayer::name() const
{
StringBuilder name;
if (Element* element = renderer().element()) {
name.append(" <");
name.append(element->tagName().convertToLowercaseWithoutLocale());
name.append('>');
if (element->hasID()) {
name.appendLiteral(" id=\'");
name.append(element->getIdAttribute());
name.append('\'');
}
if (element->hasClass()) {
name.appendLiteral(" class=\'");
size_t classNamesToDump = element->classNames().size();
const size_t maxNumClassNames = 7;
bool addEllipsis = false;
if (classNamesToDump > maxNumClassNames) {
classNamesToDump = maxNumClassNames;
addEllipsis = true;
}
for (size_t i = 0; i < classNamesToDump; ++i) {
if (i > 0)
name.append(' ');
name.append(element->classNames()[i]);
}
if (addEllipsis)
name.append("...");
name.append('\'');
}
} else
name.append(renderer().renderName());
if (isReflection())
name.appendLiteral(" (reflection)");
return name.toString();
}
RenderLayerCompositor& RenderLayer::compositor() const
{
return renderer().view().compositor();
}
void RenderLayer::contentChanged(ContentChangeType changeType)
{
if (changeType == CanvasChanged || changeType == VideoChanged || changeType == FullScreenChanged || (isComposited() && changeType == ImageChanged)) {
setNeedsPostLayoutCompositingUpdate();
setNeedsCompositingConfigurationUpdate();
}
if (auto* backing = this->backing())
backing->contentChanged(changeType);
}
bool RenderLayer::canRender3DTransforms() const
{
return compositor().canRender3DTransforms();
}
bool RenderLayer::paintsWithFilters() const
{
if (!renderer().hasFilter())
return false;
if (!isComposited())
return true;
return !m_backing->canCompositeFilters();
}
bool RenderLayer::requiresFullLayerImageForFilters() const
{
if (!paintsWithFilters())
return false;
return m_filters && m_filters->hasFilterThatMovesPixels();
}
void RenderLayer::updateLayerPositionsAfterLayout(const RenderLayer* rootLayer, OptionSet<UpdateLayerPositionsFlag> flags)
{
LOG(Compositing, "RenderLayer %p updateLayerPositionsAfterLayout", this);
RenderGeometryMap geometryMap(UseTransforms);
if (this != rootLayer)
geometryMap.pushMappingsToAncestor(parent(), nullptr);
updateLayerPositions(&geometryMap, flags);
}
void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, OptionSet<UpdateLayerPositionsFlag> flags)
{
updateLayerPosition(&flags);
applyPostLayoutScrollPositionIfNeeded();
if (geometryMap)
geometryMap->pushMappingsToAncestor(this, parent());
clearClipRects();
if (hasOverflowControls()) {
LayoutSize offsetFromRoot;
if (geometryMap)
offsetFromRoot = LayoutSize(toFloatSize(geometryMap->absolutePoint(FloatPoint())));
else {
offsetFromRoot = offsetFromAncestor(root());
}
positionOverflowControls(roundedIntSize(offsetFromRoot));
}
updateDescendantDependentFlags();
if (flags & UpdatePagination)
updatePagination();
else
m_enclosingPaginationLayer = nullptr;
if (m_hasVisibleContent) {
ASSERT(!renderer().view().frameView().layoutContext().isPaintOffsetCacheEnabled());
RenderLayerModelObject* repaintContainer = renderer().containerForRepaint();
auto hadRepaintLayoutRects = renderer().hasRepaintLayoutRects();
RepaintLayoutRects oldRects = hadRepaintLayoutRects ? renderer().repaintLayoutRects() : RepaintLayoutRects();
computeRepaintRects(repaintContainer, geometryMap);
auto hasRepaintLayoutRects = renderer().hasRepaintLayoutRects();
RepaintLayoutRects newRects = hasRepaintLayoutRects ? renderer().repaintLayoutRects() : RepaintLayoutRects();
if ((flags & CheckForRepaint) && hasRepaintLayoutRects) {
if (!renderer().view().printing()) {
if (m_repaintStatus & NeedsFullRepaint) {
if (hadRepaintLayoutRects)
renderer().repaintUsingContainer(repaintContainer, oldRects.m_repaintRect);
if (!hadRepaintLayoutRects || newRects.m_repaintRect != oldRects.m_repaintRect)
renderer().repaintUsingContainer(repaintContainer, newRects.m_repaintRect);
} else if (shouldRepaintAfterLayout()) {
renderer().repaintAfterLayoutIfNeeded(repaintContainer, oldRects.m_repaintRect, oldRects.m_outlineBox, &newRects.m_repaintRect, &newRects.m_outlineBox);
}
}
}
} else
clearRepaintRects();
m_repaintStatus = NeedsNormalRepaint;
m_hasTransformedAncestor = flags.contains(SeenTransformedLayer);
m_has3DTransformedAncestor = flags.contains(Seen3DTransformedLayer);
if (m_reflection)
m_reflection->layout();
if (renderer().isInFlowRenderFragmentedFlow()) {
updatePagination();
flags.add(UpdatePagination);
}
if (transform()) {
flags.add(SeenTransformedLayer);
if (!transform()->isAffine())
flags.add(Seen3DTransformedLayer);
}
for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
child->updateLayerPositions(geometryMap, flags);
if (m_marquee) {
bool oldUpdatingMarqueePosition = m_updatingMarqueePosition;
m_updatingMarqueePosition = true;
m_marquee->updateMarqueePosition();
m_updatingMarqueePosition = oldUpdatingMarqueePosition;
}
if (renderer().isOutOfFlowPositioned() && renderer().style().position() == PositionType::Fixed && renderer().settings().acceleratedCompositingForFixedPositionEnabled()) {
bool intersectsViewport = compositor().fixedLayerIntersectsViewport(*this);
if (intersectsViewport != m_isFixedIntersectingViewport) {
m_isFixedIntersectingViewport = intersectsViewport;
setNeedsPostLayoutCompositingUpdate();
}
}
if (isComposited())
backing()->updateAfterLayout(flags.contains(ContainingClippingLayerChangedSize), flags.contains(NeedsFullRepaintInBacking));
if (geometryMap)
geometryMap->popMappingsToAncestor(parent());
renderer().document().markers().invalidateRectsForAllMarkers();
}
LayoutRect RenderLayer::repaintRectIncludingNonCompositingDescendants() const
{
LayoutRect repaintRect = renderer().repaintLayoutRects().m_repaintRect;
for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
if (child->isComposited())
continue;
repaintRect.uniteIfNonZero(child->repaintRectIncludingNonCompositingDescendants());
}
return repaintRect;
}
void RenderLayer::setAncestorChainHasSelfPaintingLayerDescendant()
{
for (RenderLayer* layer = this; layer; layer = layer->parent()) {
if (!layer->m_hasSelfPaintingLayerDescendantDirty && layer->hasSelfPaintingLayerDescendant())
break;
layer->m_hasSelfPaintingLayerDescendantDirty = false;
layer->m_hasSelfPaintingLayerDescendant = true;
}
}
void RenderLayer::dirtyAncestorChainHasSelfPaintingLayerDescendantStatus()
{
for (RenderLayer* layer = this; layer; layer = layer->parent()) {
layer->m_hasSelfPaintingLayerDescendantDirty = true;
if (layer->isSelfPaintingLayer()) {
ASSERT(!parent() || parent()->m_hasSelfPaintingLayerDescendantDirty || parent()->hasSelfPaintingLayerDescendant());
break;
}
}
}
void RenderLayer::computeRepaintRects(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap)
{
ASSERT(!m_visibleContentStatusDirty);
renderer().computeRepaintLayoutRects(repaintContainer, geometryMap);
}
void RenderLayer::computeRepaintRectsIncludingDescendants()
{
computeRepaintRects(renderer().containerForRepaint());
for (RenderLayer* layer = firstChild(); layer; layer = layer->nextSibling())
layer->computeRepaintRectsIncludingDescendants();
}
void RenderLayer::clearRepaintRects()
{
ASSERT(!m_visibleContentStatusDirty);
renderer().clearRepaintLayoutRects();
}
void RenderLayer::updateLayerPositionsAfterDocumentScroll()
{
ASSERT(this == renderer().view().layer());
LOG(Scrolling, "RenderLayer::updateLayerPositionsAfterDocumentScroll");
RenderGeometryMap geometryMap(UseTransforms);
updateLayerPositionsAfterScroll(&geometryMap);
}
void RenderLayer::updateLayerPositionsAfterOverflowScroll()
{
RenderGeometryMap geometryMap(UseTransforms);
if (this != renderer().view().layer())
geometryMap.pushMappingsToAncestor(parent(), nullptr);
updateLayerPositionsAfterScroll(&geometryMap, IsOverflowScroll);
}
void RenderLayer::updateLayerPositionsAfterScroll(RenderGeometryMap* geometryMap, OptionSet<UpdateLayerPositionsAfterScrollFlag> flags)
{
updateDescendantDependentFlags();
if (!m_hasVisibleDescendant && !m_hasVisibleContent)
return;
bool positionChanged = updateLayerPosition();
if (positionChanged)
flags.add(HasChangedAncestor);
if (flags.containsAny({ HasChangedAncestor, HasSeenViewportConstrainedAncestor, IsOverflowScroll }))
clearClipRects();
if (renderer().style().hasViewportConstrainedPosition())
flags.add(HasSeenViewportConstrainedAncestor);
if (renderer().hasOverflowClip())
flags.add(HasSeenAncestorWithOverflowClip);
bool shouldComputeRepaintRects = (flags.contains(HasSeenViewportConstrainedAncestor) || flags.containsAll({ IsOverflowScroll, HasSeenAncestorWithOverflowClip })) && isSelfPaintingLayer();
bool isVisuallyEmpty = !isVisuallyNonEmpty();
bool shouldPushAndPopMappings = geometryMap && ((shouldComputeRepaintRects && !isVisuallyEmpty) || firstChild());
if (shouldPushAndPopMappings)
geometryMap->pushMappingsToAncestor(this, parent());
if (shouldComputeRepaintRects) {
if (isVisuallyEmpty)
clearRepaintRects();
else computeRepaintRects(renderer().containerForRepaint(), geometryMap);
} else if (!renderer().view().frameView().platformWidget()) {
ASSERT(!renderer().hasRepaintLayoutRects() || renderer().repaintLayoutRects().m_repaintRect == renderer().clippedOverflowRectForRepaint(renderer().containerForRepaint()));
ASSERT(!renderer().hasRepaintLayoutRects() || renderer().repaintLayoutRects().m_outlineBox == renderer().outlineBoundsForRepaint(renderer().containerForRepaint()));
}
for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
child->updateLayerPositionsAfterScroll(geometryMap, flags);
if (m_marquee) {
bool oldUpdatingMarqueePosition = m_updatingMarqueePosition;
m_updatingMarqueePosition = true;
m_marquee->updateMarqueePosition();
m_updatingMarqueePosition = oldUpdatingMarqueePosition;
}
if (shouldPushAndPopMappings)
geometryMap->popMappingsToAncestor(parent());
renderer().document().markers().invalidateRectsForAllMarkers();
}
#if ENABLE(CSS_COMPOSITING)
void RenderLayer::updateBlendMode()
{
bool hadBlendMode = static_cast<BlendMode>(m_blendMode) != BlendMode::Normal;
if (parent() && hadBlendMode != hasBlendMode()) {
if (hasBlendMode())
parent()->updateAncestorChainHasBlendingDescendants();
else
parent()->dirtyAncestorChainHasBlendingDescendants();
}
BlendMode newBlendMode = renderer().style().blendMode();
if (newBlendMode != static_cast<BlendMode>(m_blendMode))
m_blendMode = static_cast<unsigned>(newBlendMode);
}
void RenderLayer::updateAncestorChainHasBlendingDescendants()
{
for (auto* layer = this; layer; layer = layer->parent()) {
if (!layer->hasNotIsolatedBlendingDescendantsStatusDirty() && layer->hasNotIsolatedBlendingDescendants())
break;
layer->m_hasNotIsolatedBlendingDescendants = true;
layer->m_hasNotIsolatedBlendingDescendantsStatusDirty = false;
layer->updateSelfPaintingLayer();
if (layer->isStackingContext())
break;
}
}
void RenderLayer::dirtyAncestorChainHasBlendingDescendants()
{
for (auto* layer = this; layer; layer = layer->parent()) {
if (layer->hasNotIsolatedBlendingDescendantsStatusDirty())
break;
layer->m_hasNotIsolatedBlendingDescendantsStatusDirty = true;
if (layer->isStackingContext())
break;
}
}
#endif
void RenderLayer::updateTransform()
{
bool hasTransform = renderer().hasTransform();
bool had3DTransform = has3DTransform();
bool hadTransform = !!m_transform;
if (hasTransform != hadTransform) {
if (hasTransform)
m_transform = std::make_unique<TransformationMatrix>();
else
m_transform = nullptr;
clearClipRectsIncludingDescendants();
}
if (hasTransform) {
RenderBox* box = renderBox();
ASSERT(box);
m_transform->makeIdentity();
box->style().applyTransform(*m_transform, snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor()), RenderStyle::IncludeTransformOrigin);
makeMatrixRenderable(*m_transform, canRender3DTransforms());
}
if (had3DTransform != has3DTransform()) {
dirty3DTransformedDescendantStatus();
setNeedsPostLayoutCompositingUpdateOnAncestors();
}
}
TransformationMatrix RenderLayer::currentTransform(RenderStyle::ApplyTransformOrigin applyOrigin) const
{
if (!m_transform)
return TransformationMatrix();
RenderBox* box = renderBox();
if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
if (auto* timeline = renderer().documentTimeline()) {
if (timeline->isRunningAcceleratedAnimationOnRenderer(renderer(), CSSPropertyTransform)) {
TransformationMatrix currTransform;
FloatRect pixelSnappedBorderRect = snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor());
std::unique_ptr<RenderStyle> style = timeline->animatedStyleForRenderer(renderer());
style->applyTransform(currTransform, pixelSnappedBorderRect, applyOrigin);
makeMatrixRenderable(currTransform, canRender3DTransforms());
return currTransform;
}
}
} else {
if (renderer().animation().isRunningAcceleratedAnimationOnRenderer(renderer(), CSSPropertyTransform, AnimationBase::Running | AnimationBase::Paused)) {
TransformationMatrix currTransform;
FloatRect pixelSnappedBorderRect = snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor());
std::unique_ptr<RenderStyle> style = renderer().animation().animatedStyleForRenderer(renderer());
style->applyTransform(currTransform, pixelSnappedBorderRect, applyOrigin);
makeMatrixRenderable(currTransform, canRender3DTransforms());
return currTransform;
}
}
if (applyOrigin == RenderStyle::ExcludeTransformOrigin) {
TransformationMatrix currTransform;
FloatRect pixelSnappedBorderRect = snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor());
box->style().applyTransform(currTransform, pixelSnappedBorderRect, RenderStyle::ExcludeTransformOrigin);
makeMatrixRenderable(currTransform, canRender3DTransforms());
return currTransform;
}
return *m_transform;
}
TransformationMatrix RenderLayer::renderableTransform(OptionSet<PaintBehavior> paintBehavior) const
{
if (!m_transform)
return TransformationMatrix();
if (paintBehavior & PaintBehavior::FlattenCompositingLayers) {
TransformationMatrix matrix = *m_transform;
makeMatrixRenderable(matrix, false );
return matrix;
}
return *m_transform;
}
RenderLayer* RenderLayer::enclosingOverflowClipLayer(IncludeSelfOrNot includeSelf) const
{
const RenderLayer* layer = (includeSelf == IncludeSelf) ? this : parent();
while (layer) {
if (layer->renderer().hasOverflowClip())
return const_cast<RenderLayer*>(layer);
layer = layer->parent();
}
return nullptr;
}
bool RenderLayer::hasCompositedLayerInEnclosingPaginationChain() const
{
if (!m_enclosingPaginationLayer)
return false;
if (m_enclosingPaginationLayer->isComposited())
return true;
if (m_enclosingPaginationLayer == this)
return false;
if (isComposited())
return true;
if (isNormalFlowOnly())
return parent()->hasCompositedLayerInEnclosingPaginationChain();
for (const auto* containingBlock = renderer().containingBlock(); containingBlock && !is<RenderView>(*containingBlock); containingBlock = containingBlock->containingBlock()) {
if (containingBlock->hasLayer())
return containingBlock->layer()->hasCompositedLayerInEnclosingPaginationChain();
}
return false;
}
void RenderLayer::updatePagination()
{
m_enclosingPaginationLayer = nullptr;
if (!parent())
return;
if (renderer().isInFlowRenderFragmentedFlow()) {
m_enclosingPaginationLayer = makeWeakPtr(*this);
return;
}
if (isNormalFlowOnly()) {
if (parent()->hasTransform())
m_enclosingPaginationLayer = nullptr;
else
m_enclosingPaginationLayer = makeWeakPtr(parent()->enclosingPaginationLayer(IncludeCompositedPaginatedLayers));
return;
}
for (const auto* containingBlock = renderer().containingBlock(); containingBlock && !is<RenderView>(*containingBlock); containingBlock = containingBlock->containingBlock()) {
if (containingBlock->hasLayer()) {
if (containingBlock->layer()->hasTransform())
m_enclosingPaginationLayer = nullptr;
else
m_enclosingPaginationLayer = makeWeakPtr(containingBlock->layer()->enclosingPaginationLayer(IncludeCompositedPaginatedLayers));
return;
}
}
}
void RenderLayer::setHasVisibleContent()
{
if (m_hasVisibleContent && !m_visibleContentStatusDirty) {
ASSERT(!parent() || parent()->hasVisibleDescendant());
return;
}
m_visibleContentStatusDirty = false;
m_hasVisibleContent = true;
computeRepaintRects(renderer().containerForRepaint());
if (!isNormalFlowOnly()) {
for (RenderLayer* sc = stackingContext(); sc; sc = sc->stackingContext()) {
sc->dirtyZOrderLists();
if (sc->hasVisibleContent())
break;
}
}
if (parent())
parent()->setAncestorChainHasVisibleDescendant();
}
void RenderLayer::dirtyVisibleContentStatus()
{
m_visibleContentStatusDirty = true;
if (parent())
parent()->dirtyAncestorChainVisibleDescendantStatus();
}
void RenderLayer::dirtyAncestorChainVisibleDescendantStatus()
{
for (RenderLayer* layer = this; layer; layer = layer->parent()) {
if (layer->m_visibleDescendantStatusDirty)
break;
layer->m_visibleDescendantStatusDirty = true;
}
}
void RenderLayer::setAncestorChainHasVisibleDescendant()
{
for (RenderLayer* layer = this; layer; layer = layer->parent()) {
if (!layer->m_visibleDescendantStatusDirty && layer->hasVisibleDescendant())
break;
layer->m_hasVisibleDescendant = true;
layer->m_visibleDescendantStatusDirty = false;
}
}
void RenderLayer::updateDescendantDependentFlags()
{
if (m_visibleDescendantStatusDirty || m_hasSelfPaintingLayerDescendantDirty || hasNotIsolatedBlendingDescendantsStatusDirty()) {
bool hasVisibleDescendant = false;
bool hasSelfPaintingLayerDescendant = false;
#if ENABLE(CSS_COMPOSITING)
bool hasNotIsolatedBlendingDescendants = false;
#endif
for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
child->updateDescendantDependentFlags();
hasVisibleDescendant |= child->m_hasVisibleContent || child->m_hasVisibleDescendant;
hasSelfPaintingLayerDescendant |= child->isSelfPaintingLayer() || child->hasSelfPaintingLayerDescendant();
#if ENABLE(CSS_COMPOSITING)
hasNotIsolatedBlendingDescendants |= child->hasBlendMode() || (child->hasNotIsolatedBlendingDescendants() && !child->isolatesBlending());
#endif
bool allFlagsSet = hasVisibleDescendant && hasSelfPaintingLayerDescendant;
#if ENABLE(CSS_COMPOSITING)
allFlagsSet &= hasNotIsolatedBlendingDescendants;
#endif
if (allFlagsSet)
break;
}
m_hasVisibleDescendant = hasVisibleDescendant;
m_visibleDescendantStatusDirty = false;
m_hasSelfPaintingLayerDescendant = hasSelfPaintingLayerDescendant;
m_hasSelfPaintingLayerDescendantDirty = false;
#if ENABLE(CSS_COMPOSITING)
m_hasNotIsolatedBlendingDescendants = hasNotIsolatedBlendingDescendants;
if (m_hasNotIsolatedBlendingDescendantsStatusDirty) {
m_hasNotIsolatedBlendingDescendantsStatusDirty = false;
updateSelfPaintingLayer();
}
#endif
}
if (m_visibleContentStatusDirty) {
if (renderer().style().visibility() == Visibility::Visible)
m_hasVisibleContent = true;
else {
m_hasVisibleContent = false;
RenderObject* r = renderer().firstChild();
while (r) {
if (r->style().visibility() == Visibility::Visible && !r->hasLayer()) {
m_hasVisibleContent = true;
break;
}
RenderObject* child = nullptr;
if (!r->hasLayer() && (child = r->firstChildSlow()))
r = child;
else if (r->nextSibling())
r = r->nextSibling();
else {
do {
r = r->parent();
if (r == &renderer())
r = nullptr;
} while (r && !r->nextSibling());
if (r)
r = r->nextSibling();
}
}
}
m_visibleContentStatusDirty = false;
}
}
void RenderLayer::dirty3DTransformedDescendantStatus()
{
RenderLayer* curr = stackingContext();
if (curr)
curr->m_3DTransformedDescendantStatusDirty = true;
while (curr && curr->preserves3D()) {
curr->m_3DTransformedDescendantStatusDirty = true;
curr = curr->stackingContext();
}
}
bool RenderLayer::update3DTransformedDescendantStatus()
{
if (m_3DTransformedDescendantStatusDirty) {
m_has3DTransformedDescendant = false;
updateZOrderLists();
for (auto* layer : positiveZOrderLayers())
m_has3DTransformedDescendant |= layer->update3DTransformedDescendantStatus();
for (auto* layer : negativeZOrderLayers())
m_has3DTransformedDescendant |= layer->update3DTransformedDescendantStatus();
m_3DTransformedDescendantStatusDirty = false;
}
if (preserves3D())
return has3DTransform() || m_has3DTransformedDescendant;
return has3DTransform();
}
bool RenderLayer::updateLayerPosition(OptionSet<UpdateLayerPositionsFlag>* flags)
{
LayoutPoint localPoint;
LayoutSize inlineBoundingBoxOffset; if (renderer().isInline() && is<RenderInline>(renderer())) {
auto& inlineFlow = downcast<RenderInline>(renderer());
IntRect lineBox = inlineFlow.linesBoundingBox();
setSize(lineBox.size());
inlineBoundingBoxOffset = toLayoutSize(lineBox.location());
localPoint += inlineBoundingBoxOffset;
} else if (RenderBox* box = renderBox()) {
auto newSize = snappedIntRect(box->frameRect()).size();
if (newSize != size()) {
if (is<RenderWidget>(*box) && downcast<RenderWidget>(*box).requiresAcceleratedCompositing()) {
setNeedsPostLayoutCompositingUpdate();
}
if (flags && renderer().hasOverflowClip())
flags->add(ContainingClippingLayerChangedSize);
setSize(newSize);
}
box->applyTopLeftLocationOffset(localPoint);
}
if (!renderer().isOutOfFlowPositioned()) {
auto* ancestor = renderer().parent();
while (ancestor && !ancestor->hasLayer()) {
if (is<RenderBox>(*ancestor) && !is<RenderTableRow>(*ancestor)) {
localPoint += downcast<RenderBox>(*ancestor).topLeftLocationOffset();
}
ancestor = ancestor->parent();
}
if (is<RenderTableRow>(ancestor)) {
localPoint -= downcast<RenderTableRow>(*ancestor).topLeftLocationOffset();
}
}
RenderLayer* positionedParent;
if (renderer().isOutOfFlowPositioned() && (positionedParent = enclosingAncestorForPosition(renderer().style().position()))) {
if (positionedParent->renderer().hasOverflowClip())
localPoint -= toLayoutSize(positionedParent->scrollPosition());
if (renderer().isOutOfFlowPositioned() && positionedParent->renderer().isInFlowPositioned() && is<RenderInline>(positionedParent->renderer())) {
LayoutSize offset = downcast<RenderInline>(positionedParent->renderer()).offsetForInFlowPositionedInline(&downcast<RenderBox>(renderer()));
localPoint += offset;
}
} else if (parent()) {
if (parent()->renderer().hasOverflowClip())
localPoint -= toLayoutSize(parent()->scrollPosition());
}
bool positionOrOffsetChanged = false;
if (renderer().isInFlowPositioned()) {
LayoutSize newOffset = downcast<RenderBoxModelObject>(renderer()).offsetForInFlowPosition();
positionOrOffsetChanged = newOffset != m_offsetForInFlowPosition;
m_offsetForInFlowPosition = newOffset;
localPoint.move(m_offsetForInFlowPosition);
} else {
m_offsetForInFlowPosition = LayoutSize();
}
localPoint -= inlineBoundingBoxOffset;
positionOrOffsetChanged |= location() != localPoint;
setLocation(localPoint);
if (positionOrOffsetChanged && compositor().hasContentCompositingLayers()) {
if (isComposited())
setNeedsCompositingGeometryUpdate();
if (auto* paintParent = paintOrderParent())
paintParent->setDescendantsNeedUpdateBackingAndHierarchyTraversal();
}
return positionOrOffsetChanged;
}
TransformationMatrix RenderLayer::perspectiveTransform() const
{
RenderBox* box = renderBox();
if (!box)
return TransformationMatrix();
if (!box->hasTransformRelatedProperty())
return TransformationMatrix();
const RenderStyle& style = box->style();
if (!style.hasPerspective())
return TransformationMatrix();
const FloatRect borderBox = snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor());
float perspectiveOriginX = floatValueForLength(style.perspectiveOriginX(), borderBox.width());
float perspectiveOriginY = floatValueForLength(style.perspectiveOriginY(), borderBox.height());
perspectiveOriginX -= borderBox.width() / 2.0f;
perspectiveOriginY -= borderBox.height() / 2.0f;
TransformationMatrix t;
t.translate(perspectiveOriginX, perspectiveOriginY);
t.applyPerspective(style.perspective());
t.translate(-perspectiveOriginX, -perspectiveOriginY);
return t;
}
FloatPoint RenderLayer::perspectiveOrigin() const
{
if (!renderer().hasTransformRelatedProperty())
return FloatPoint();
const LayoutRect borderBox = downcast<RenderBox>(renderer()).borderBoxRect();
const RenderStyle& style = renderer().style();
return FloatPoint(floatValueForLength(style.perspectiveOriginX(), borderBox.width()),
floatValueForLength(style.perspectiveOriginY(), borderBox.height()));
}
static inline bool isContainerForPositioned(RenderLayer& layer, PositionType position)
{
switch (position) {
case PositionType::Fixed:
return layer.renderer().canContainFixedPositionObjects();
case PositionType::Absolute:
return layer.renderer().canContainAbsolutelyPositionedObjects();
default:
ASSERT_NOT_REACHED();
return false;
}
}
RenderLayer* RenderLayer::enclosingAncestorForPosition(PositionType position) const
{
RenderLayer* curr = parent();
while (curr && !isContainerForPositioned(*curr, position))
curr = curr->parent();
return curr;
}
static RenderLayer* parentLayerCrossFrame(const RenderLayer& layer)
{
if (layer.parent())
return layer.parent();
HTMLFrameOwnerElement* ownerElement = layer.renderer().document().ownerElement();
if (!ownerElement)
return nullptr;
RenderElement* ownerRenderer = ownerElement->renderer();
if (!ownerRenderer)
return nullptr;
return ownerRenderer->enclosingLayer();
}
RenderLayer* RenderLayer::enclosingScrollableLayer() const
{
for (RenderLayer* nextLayer = parentLayerCrossFrame(*this); nextLayer; nextLayer = parentLayerCrossFrame(*nextLayer)) {
if (is<RenderBox>(nextLayer->renderer()) && downcast<RenderBox>(nextLayer->renderer()).canBeScrolledAndHasScrollableArea())
return nextLayer;
}
return nullptr;
}
IntRect RenderLayer::scrollableAreaBoundingBox(bool* isInsideFixed) const
{
return renderer().absoluteBoundingBoxRect( true, isInsideFixed);
}
bool RenderLayer::isRubberBandInProgress() const
{
#if ENABLE(RUBBER_BANDING)
if (!scrollsOverflow())
return false;
if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
return scrollAnimator->isRubberBandInProgress();
#endif
return false;
}
bool RenderLayer::forceUpdateScrollbarsOnMainThreadForPerformanceTesting() const
{
return renderer().settings().forceUpdateScrollbarsOnMainThreadForPerformanceTesting();
}
RenderLayer* RenderLayer::enclosingTransformedAncestor() const
{
RenderLayer* curr = parent();
while (curr && !curr->isRenderViewLayer() && !curr->transform())
curr = curr->parent();
return curr;
}
inline bool RenderLayer::shouldRepaintAfterLayout() const
{
if (m_repaintStatus == NeedsNormalRepaint)
return true;
ASSERT(m_repaintStatus == NeedsFullRepaintForPositionedMovementLayout);
return !isComposited() || backing()->paintsIntoCompositedAncestor();
}
bool compositedWithOwnBackingStore(const RenderLayer& layer)
{
return layer.isComposited() && !layer.backing()->paintsIntoCompositedAncestor();
}
RenderLayer* RenderLayer::enclosingCompositingLayer(IncludeSelfOrNot includeSelf) const
{
if (includeSelf == IncludeSelf && isComposited())
return const_cast<RenderLayer*>(this);
for (const RenderLayer* curr = paintOrderParent(); curr; curr = curr->paintOrderParent()) {
if (curr->isComposited())
return const_cast<RenderLayer*>(curr);
}
return nullptr;
}
RenderLayer* RenderLayer::enclosingCompositingLayerForRepaint(IncludeSelfOrNot includeSelf) const
{
if (includeSelf == IncludeSelf && compositedWithOwnBackingStore(*this))
return const_cast<RenderLayer*>(this);
for (const RenderLayer* curr = paintOrderParent(); curr; curr = curr->paintOrderParent()) {
if (compositedWithOwnBackingStore(*curr))
return const_cast<RenderLayer*>(curr);
}
return nullptr;
}
RenderLayer* RenderLayer::enclosingFilterLayer(IncludeSelfOrNot includeSelf) const
{
const RenderLayer* curr = (includeSelf == IncludeSelf) ? this : parent();
for (; curr; curr = curr->parent()) {
if (curr->requiresFullLayerImageForFilters())
return const_cast<RenderLayer*>(curr);
}
return nullptr;
}
RenderLayer* RenderLayer::enclosingFilterRepaintLayer() const
{
for (const RenderLayer* curr = this; curr; curr = curr->parent()) {
if ((curr != this && curr->requiresFullLayerImageForFilters()) || compositedWithOwnBackingStore(*curr) || curr->isRenderViewLayer())
return const_cast<RenderLayer*>(curr);
}
return nullptr;
}
void RenderLayer::setFilterBackendNeedsRepaintingInRect(const LayoutRect& rect)
{
ASSERT(requiresFullLayerImageForFilters());
ASSERT(m_filters);
if (rect.isEmpty())
return;
LayoutRect rectForRepaint = rect;
renderer().style().filterOutsets().expandRect(rectForRepaint);
m_filters->expandDirtySourceRect(rectForRepaint);
RenderLayer* parentLayer = enclosingFilterRepaintLayer();
ASSERT(parentLayer);
FloatQuad repaintQuad(rectForRepaint);
LayoutRect parentLayerRect = renderer().localToContainerQuad(repaintQuad, &parentLayer->renderer()).enclosingBoundingBox();
if (parentLayer->isComposited()) {
if (!parentLayer->backing()->paintsIntoWindow()) {
parentLayer->setBackingNeedsRepaintInRect(parentLayerRect);
return;
}
parentLayer = renderer().view().layer();
parentLayerRect = renderer().localToContainerQuad(repaintQuad, &parentLayer->renderer()).enclosingBoundingBox();
}
if (parentLayer->paintsWithFilters()) {
parentLayer->setFilterBackendNeedsRepaintingInRect(parentLayerRect);
return;
}
if (parentLayer->isRenderViewLayer()) {
downcast<RenderView>(parentLayer->renderer()).repaintViewRectangle(parentLayerRect);
return;
}
ASSERT_NOT_REACHED();
}
bool RenderLayer::hasAncestorWithFilterOutsets() const
{
for (const RenderLayer* curr = this; curr; curr = curr->parent()) {
if (curr->renderer().style().hasFilterOutsets())
return true;
}
return false;
}
RenderLayer* RenderLayer::clippingRootForPainting() const
{
if (isComposited())
return const_cast<RenderLayer*>(this);
const RenderLayer* current = this;
while (current) {
if (current->isRenderViewLayer())
return const_cast<RenderLayer*>(current);
current = current->paintOrderParent();
ASSERT(current);
if (current->transform() || compositedWithOwnBackingStore(*current))
return const_cast<RenderLayer*>(current);
}
ASSERT_NOT_REACHED();
return nullptr;
}
LayoutPoint RenderLayer::absoluteToContents(const LayoutPoint& absolutePoint) const
{
return LayoutPoint(renderer().absoluteToLocal(absolutePoint, UseTransforms));
}
bool RenderLayer::cannotBlitToWindow() const
{
if (isTransparent() || hasReflection() || hasTransform())
return true;
if (!parent())
return false;
return parent()->cannotBlitToWindow();
}
RenderLayer* RenderLayer::transparentPaintingAncestor()
{
if (isComposited())
return nullptr;
for (RenderLayer* curr = parent(); curr; curr = curr->parent()) {
if (curr->isComposited())
return nullptr;
if (curr->isTransparent())
return curr;
}
return nullptr;
}
enum TransparencyClipBoxBehavior {
PaintingTransparencyClipBox,
HitTestingTransparencyClipBox
};
enum TransparencyClipBoxMode {
DescendantsOfTransparencyClipBox,
RootOfTransparencyClipBox
};
static LayoutRect transparencyClipBox(const RenderLayer&, const RenderLayer* rootLayer, TransparencyClipBoxBehavior, TransparencyClipBoxMode, OptionSet<PaintBehavior> = { });
static void expandClipRectForDescendantsAndReflection(LayoutRect& clipRect, const RenderLayer& layer, const RenderLayer* rootLayer,
TransparencyClipBoxBehavior transparencyBehavior, OptionSet<PaintBehavior> paintBehavior)
{
if (!layer.renderer().hasMask()) {
for (RenderLayer* curr = layer.firstChild(); curr; curr = curr->nextSibling()) {
if (!layer.isReflectionLayer(*curr))
clipRect.unite(transparencyClipBox(*curr, rootLayer, transparencyBehavior, DescendantsOfTransparencyClipBox, paintBehavior));
}
}
if (layer.renderer().hasReflection()) {
LayoutSize delta = layer.offsetFromAncestor(rootLayer);
clipRect.move(-delta);
clipRect.unite(layer.renderBox()->reflectedRect(clipRect));
clipRect.move(delta);
}
}
static LayoutRect transparencyClipBox(const RenderLayer& layer, const RenderLayer* rootLayer, TransparencyClipBoxBehavior transparencyBehavior,
TransparencyClipBoxMode transparencyMode, OptionSet<PaintBehavior> paintBehavior)
{
if (rootLayer != &layer && ((transparencyBehavior == PaintingTransparencyClipBox && layer.paintsWithTransform(paintBehavior))
|| (transparencyBehavior == HitTestingTransparencyClipBox && layer.hasTransform()))) {
RenderLayer::PaginationInclusionMode mode = transparencyBehavior == HitTestingTransparencyClipBox ? RenderLayer::IncludeCompositedPaginatedLayers : RenderLayer::ExcludeCompositedPaginatedLayers;
const RenderLayer* paginationLayer = transparencyMode == DescendantsOfTransparencyClipBox ? layer.enclosingPaginationLayer(mode) : nullptr;
const RenderLayer* rootLayerForTransform = paginationLayer ? paginationLayer : rootLayer;
LayoutSize delta = layer.offsetFromAncestor(rootLayerForTransform);
TransformationMatrix transform;
transform.translate(delta.width(), delta.height());
transform.multiply(*layer.transform());
LayoutRect clipRect = layer.boundingBox(&layer);
expandClipRectForDescendantsAndReflection(clipRect, layer, &layer, transparencyBehavior, paintBehavior);
layer.renderer().style().filterOutsets().expandRect(clipRect);
LayoutRect result = transform.mapRect(clipRect);
if (!paginationLayer)
return result;
auto& enclosingFragmentedFlow = downcast<RenderFragmentedFlow>(paginationLayer->renderer());
result = enclosingFragmentedFlow.fragmentsBoundingBox(result);
result.move(paginationLayer->offsetFromAncestor(rootLayer));
return result;
}
LayoutRect clipRect = layer.boundingBox(rootLayer, layer.offsetFromAncestor(rootLayer), transparencyBehavior == HitTestingTransparencyClipBox ? RenderLayer::UseFragmentBoxesIncludingCompositing : RenderLayer::UseFragmentBoxesExcludingCompositing);
expandClipRectForDescendantsAndReflection(clipRect, layer, rootLayer, transparencyBehavior, paintBehavior);
layer.renderer().style().filterOutsets().expandRect(clipRect);
return clipRect;
}
static LayoutRect paintingExtent(const RenderLayer& currentLayer, const RenderLayer* rootLayer, const LayoutRect& paintDirtyRect, OptionSet<PaintBehavior> paintBehavior)
{
return intersection(transparencyClipBox(currentLayer, rootLayer, PaintingTransparencyClipBox, RootOfTransparencyClipBox, paintBehavior), paintDirtyRect);
}
void RenderLayer::beginTransparencyLayers(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, const LayoutRect& dirtyRect)
{
if (context.paintingDisabled() || (paintsWithTransparency(paintingInfo.paintBehavior) && m_usedTransparency))
return;
RenderLayer* ancestor = transparentPaintingAncestor();
if (ancestor)
ancestor->beginTransparencyLayers(context, paintingInfo, dirtyRect);
if (paintsWithTransparency(paintingInfo.paintBehavior)) {
ASSERT(isStackingContext());
m_usedTransparency = true;
context.save();
LayoutRect adjustedClipRect = paintingExtent(*this, paintingInfo.rootLayer, dirtyRect, paintingInfo.paintBehavior);
adjustedClipRect.move(paintingInfo.subpixelOffset);
FloatRect pixelSnappedClipRect = snapRectToDevicePixels(adjustedClipRect, renderer().document().deviceScaleFactor());
context.clip(pixelSnappedClipRect);
#if ENABLE(CSS_COMPOSITING)
bool usesCompositeOperation = hasBlendMode() && !(renderer().isSVGRoot() && parent() && parent()->isRenderViewLayer());
if (usesCompositeOperation)
context.setCompositeOperation(context.compositeOperation(), blendMode());
#endif
context.beginTransparencyLayer(renderer().opacity());
#if ENABLE(CSS_COMPOSITING)
if (usesCompositeOperation)
context.setCompositeOperation(context.compositeOperation(), BlendMode::Normal);
#endif
#ifdef REVEAL_TRANSPARENCY_LAYERS
context.setFillColor(Color(0.0f, 0.0f, 0.5f, 0.2f));
context.fillRect(pixelSnappedClipRect);
#endif
}
}
#if PLATFORM(IOS_FAMILY)
void RenderLayer::willBeDestroyed()
{
if (RenderLayerBacking* layerBacking = backing())
layerBacking->layerWillBeDestroyed();
}
#endif
bool RenderLayer::isDescendantOf(const RenderLayer& layer) const
{
for (auto* ancestor = this; ancestor; ancestor = ancestor->parent()) {
if (&layer == ancestor)
return true;
}
return false;
}
void RenderLayer::convertToPixelSnappedLayerCoords(const RenderLayer* ancestorLayer, IntPoint& roundedLocation, ColumnOffsetAdjustment adjustForColumns) const
{
LayoutPoint location = convertToLayerCoords(ancestorLayer, roundedLocation, adjustForColumns);
roundedLocation = roundedIntPoint(location);
}
static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer, LayoutPoint& location, RenderLayer::ColumnOffsetAdjustment adjustForColumns)
{
ASSERT(ancestorLayer != layer);
const RenderLayerModelObject& renderer = layer->renderer();
auto position = renderer.style().position();
RenderFragmentedFlow* fixedFragmentedFlowContainer = position == PositionType::Fixed ? renderer.enclosingFragmentedFlow() : nullptr;
if (fixedFragmentedFlowContainer && !fixedFragmentedFlowContainer->isOutOfFlowPositioned())
fixedFragmentedFlowContainer = nullptr;
if (position == PositionType::Fixed && !fixedFragmentedFlowContainer && (!ancestorLayer || ancestorLayer == renderer.view().layer())) {
FloatPoint absPos = renderer.localToAbsolute(FloatPoint(), IsFixed);
location += LayoutSize(absPos.x(), absPos.y());
return ancestorLayer;
}
if (position == PositionType::Fixed && !fixedFragmentedFlowContainer) {
RenderLayer* fixedPositionContainerLayer = nullptr;
bool foundAncestor = false;
for (RenderLayer* currLayer = layer->parent(); currLayer; currLayer = currLayer->parent()) {
if (currLayer == ancestorLayer)
foundAncestor = true;
if (isContainerForPositioned(*currLayer, PositionType::Fixed)) {
fixedPositionContainerLayer = currLayer;
ASSERT_UNUSED(foundAncestor, foundAncestor);
break;
}
}
ASSERT(fixedPositionContainerLayer);
if (fixedPositionContainerLayer != ancestorLayer) {
LayoutSize fixedContainerCoords = layer->offsetFromAncestor(fixedPositionContainerLayer);
LayoutSize ancestorCoords = ancestorLayer->offsetFromAncestor(fixedPositionContainerLayer);
location += (fixedContainerCoords - ancestorCoords);
return ancestorLayer;
}
}
if (position == PositionType::Fixed && fixedFragmentedFlowContainer) {
ASSERT(ancestorLayer);
if (ancestorLayer->isOutOfFlowRenderFragmentedFlow()) {
location += toLayoutSize(layer->location());
return ancestorLayer;
}
if (ancestorLayer == renderer.view().layer()) {
location += toLayoutSize(layer->location());
FloatPoint absPos = renderer.view().localToAbsolute(FloatPoint(), IsFixed);
location += LayoutSize(absPos.x(), absPos.y());
return ancestorLayer;
}
}
RenderLayer* parentLayer;
if (position == PositionType::Absolute || position == PositionType::Fixed) {
parentLayer = layer->parent();
bool foundAncestorFirst = false;
while (parentLayer) {
if (isContainerForPositioned(*parentLayer, position))
break;
if (parentLayer == ancestorLayer) {
foundAncestorFirst = true;
break;
}
parentLayer = parentLayer->parent();
}
if (renderer.enclosingFragmentedFlow() && !layer->isOutOfFlowRenderFragmentedFlow())
ASSERT(parentLayer != renderer.view().layer());
if (foundAncestorFirst) {
RenderLayer* positionedAncestor = parentLayer->enclosingAncestorForPosition(position);
LayoutSize thisCoords = layer->offsetFromAncestor(positionedAncestor);
LayoutSize ancestorCoords = ancestorLayer->offsetFromAncestor(positionedAncestor);
location += (thisCoords - ancestorCoords);
return ancestorLayer;
}
} else
parentLayer = layer->parent();
if (!parentLayer)
return nullptr;
location += toLayoutSize(layer->location());
if (adjustForColumns == RenderLayer::AdjustForColumns) {
if (RenderLayer* parentLayer = layer->parent()) {
if (is<RenderMultiColumnFlow>(parentLayer->renderer())) {
RenderFragmentContainer* fragment = downcast<RenderMultiColumnFlow>(parentLayer->renderer()).physicalTranslationFromFlowToFragment(location);
if (fragment)
location.moveBy(fragment->topLeftLocation() + -parentLayer->renderBox()->topLeftLocation());
}
}
}
return parentLayer;
}
LayoutPoint RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, const LayoutPoint& location, ColumnOffsetAdjustment adjustForColumns) const
{
if (ancestorLayer == this)
return location;
const RenderLayer* currLayer = this;
LayoutPoint locationInLayerCoords = location;
while (currLayer && currLayer != ancestorLayer)
currLayer = accumulateOffsetTowardsAncestor(currLayer, ancestorLayer, locationInLayerCoords, adjustForColumns);
return locationInLayerCoords;
}
LayoutSize RenderLayer::offsetFromAncestor(const RenderLayer* ancestorLayer, ColumnOffsetAdjustment adjustForColumns) const
{
return toLayoutSize(convertToLayerCoords(ancestorLayer, LayoutPoint(), adjustForColumns));
}
bool RenderLayer::canUseCompositedScrolling() const
{
if (renderer().settings().asyncOverflowScrollingEnabled())
return scrollsOverflow();
#if PLATFORM(IOS_FAMILY) && ENABLE(ACCELERATED_OVERFLOW_SCROLLING)
return scrollsOverflow() && (renderer().style().useTouchOverflowScrolling() || renderer().settings().alwaysUseAcceleratedOverflowScroll());
#else
return false;
#endif
}
bool RenderLayer::hasCompositedScrollableOverflow() const
{
return canUseCompositedScrolling() && (hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
}
#if ENABLE(IOS_TOUCH_EVENTS)
bool RenderLayer::handleTouchEvent(const PlatformTouchEvent& touchEvent)
{
if (hasCompositedScrollableOverflow())
return false;
return ScrollableArea::handleTouchEvent(touchEvent);
}
void RenderLayer::registerAsTouchEventListenerForScrolling()
{
if (!renderer().element() || m_registeredAsTouchEventListenerForScrolling)
return;
renderer().document().addTouchEventHandler(*renderer().element());
m_registeredAsTouchEventListenerForScrolling = true;
}
void RenderLayer::unregisterAsTouchEventListenerForScrolling()
{
if (!renderer().element() || !m_registeredAsTouchEventListenerForScrolling)
return;
renderer().document().removeTouchEventHandler(*renderer().element());
m_registeredAsTouchEventListenerForScrolling = false;
}
#endif // ENABLE(IOS_TOUCH_EVENTS)
bool RenderLayer::usesCompositedScrolling() const
{
return isComposited() && backing()->hasScrollingLayer();
}
bool RenderLayer::usesAsyncScrolling() const
{
return usesCompositedScrolling();
}
static inline int adjustedScrollDelta(int beginningDelta)
{
const int speedReducer = 12;
int adjustedDelta = beginningDelta / speedReducer;
if (adjustedDelta > 1)
adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(adjustedDelta))) - 1;
else if (adjustedDelta < -1)
adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(-adjustedDelta))) + 1;
return adjustedDelta;
}
static inline IntSize adjustedScrollDelta(const IntSize& delta)
{
return IntSize(adjustedScrollDelta(delta.width()), adjustedScrollDelta(delta.height()));
}
void RenderLayer::panScrollFromPoint(const IntPoint& sourcePoint)
{
IntPoint lastKnownMousePosition = renderer().frame().eventHandler().lastKnownMousePosition();
static IntPoint previousMousePosition;
if (lastKnownMousePosition.x() < 0 || lastKnownMousePosition.y() < 0)
lastKnownMousePosition = previousMousePosition;
else
previousMousePosition = lastKnownMousePosition;
IntSize delta = lastKnownMousePosition - sourcePoint;
if (abs(delta.width()) <= ScrollView::noPanScrollRadius) delta.setWidth(0);
if (abs(delta.height()) <= ScrollView::noPanScrollRadius)
delta.setHeight(0);
scrollByRecursively(adjustedScrollDelta(delta));
}
void RenderLayer::scrollByRecursively(const IntSize& delta, ScrollableArea** scrolledArea)
{
if (delta.isZero())
return;
bool restrictedByLineClamp = false;
if (renderer().parent())
restrictedByLineClamp = !renderer().parent()->style().lineClamp().isNone();
if (renderer().hasOverflowClip() && !restrictedByLineClamp) {
ScrollOffset newScrollOffset = scrollOffset() + delta;
scrollToOffset(newScrollOffset);
if (scrolledArea)
*scrolledArea = this;
IntSize remainingScrollOffset = newScrollOffset - scrollOffset();
if (!remainingScrollOffset.isZero() && renderer().parent()) {
if (RenderLayer* scrollableLayer = enclosingScrollableLayer())
scrollableLayer->scrollByRecursively(remainingScrollOffset, scrolledArea);
renderer().frame().eventHandler().updateAutoscrollRenderer();
}
} else {
renderer().view().frameView().scrollBy(delta);
if (scrolledArea)
*scrolledArea = &renderer().view().frameView();
}
}
void RenderLayer::setPostLayoutScrollPosition(Optional<ScrollPosition> position)
{
m_postLayoutScrollPosition = position;
}
void RenderLayer::applyPostLayoutScrollPositionIfNeeded()
{
if (!m_postLayoutScrollPosition)
return;
scrollToOffset(scrollOffsetFromPosition(m_postLayoutScrollPosition.value()));
m_postLayoutScrollPosition = WTF::nullopt;
}
void RenderLayer::scrollToXPosition(int x, ScrollClamping clamping)
{
ScrollPosition position(x, m_scrollPosition.y());
scrollToOffset(scrollOffsetFromPosition(position), clamping);
}
void RenderLayer::scrollToYPosition(int y, ScrollClamping clamping)
{
ScrollPosition position(m_scrollPosition.x(), y);
scrollToOffset(scrollOffsetFromPosition(position), clamping);
}
ScrollOffset RenderLayer::clampScrollOffset(const ScrollOffset& scrollOffset) const
{
return scrollOffset.constrainedBetween(IntPoint(), maximumScrollOffset());
}
void RenderLayer::scrollToOffset(const ScrollOffset& scrollOffset, ScrollClamping clamping)
{
ScrollOffset newScrollOffset = clamping == ScrollClamping::Clamped ? clampScrollOffset(scrollOffset) : scrollOffset;
if (newScrollOffset != this->scrollOffset())
scrollToOffsetWithoutAnimation(newScrollOffset, clamping);
}
void RenderLayer::scrollTo(const ScrollPosition& position)
{
RenderBox* box = renderBox();
if (!box)
return;
LOG_WITH_STREAM(Scrolling, stream << "RenderLayer::scrollTo " << position);
ScrollPosition newPosition = position;
if (!box->isHTMLMarquee()) {
if (m_scrollDimensionsDirty)
computeScrollDimensions();
#if PLATFORM(IOS_FAMILY)
if (adjustForIOSCaretWhenScrolling()) {
int maxOffset = scrollWidth() - roundToInt(box->clientWidth());
ScrollOffset newOffset = scrollOffsetFromPosition(newPosition);
int scrollXOffset = newOffset.x();
if (scrollXOffset > maxOffset - caretWidth) {
scrollXOffset += caretWidth;
if (scrollXOffset <= caretWidth)
scrollXOffset = 0;
} else if (scrollXOffset < m_scrollPosition.x() - caretWidth)
scrollXOffset -= caretWidth;
newOffset.setX(scrollXOffset);
newPosition = scrollPositionFromOffset(newOffset);
}
#endif
}
if (m_scrollPosition == newPosition) {
#if PLATFORM(IOS_FAMILY)
if (m_requiresScrollBoundsOriginUpdate) {
setNeedsCompositingGeometryUpdate();
updateCompositingLayersAfterScroll();
}
#endif
return;
}
m_scrollPosition = newPosition;
RenderView& view = renderer().view();
if (!view.frameView().layoutContext().isInRenderTreeLayout()) {
updateLayerPositionsAfterOverflowScroll();
#if ENABLE(DASHBOARD_SUPPORT)
view.frameView().updateAnnotatedRegions();
#endif
view.frameView().scheduleUpdateWidgetPositions();
if (!m_updatingMarqueePosition) {
if (usesCompositedScrolling()) {
setNeedsCompositingGeometryUpdate();
setDescendantsNeedUpdateBackingAndHierarchyTraversal();
}
updateCompositingLayersAfterScroll();
}
#if PLATFORM(IOS_FAMILY) && ENABLE(TOUCH_EVENTS)
renderer().document().setTouchEventRegionsNeedUpdate();
#endif
DebugPageOverlays::didLayout(renderer().frame());
}
Frame& frame = renderer().frame();
RenderLayerModelObject* repaintContainer = renderer().containerForRepaint();
frame.selection().setCaretRectNeedsUpdate();
LayoutRect rectForRepaint = renderer().hasRepaintLayoutRects() ? renderer().repaintLayoutRects().m_repaintRect : renderer().clippedOverflowRectForRepaint(repaintContainer);
FloatQuad quadForFakeMouseMoveEvent = FloatQuad(rectForRepaint);
if (repaintContainer)
quadForFakeMouseMoveEvent = repaintContainer->localToAbsoluteQuad(quadForFakeMouseMoveEvent);
frame.eventHandler().dispatchFakeMouseMoveEventSoonInQuad(quadForFakeMouseMoveEvent);
bool requiresRepaint = true;
if (usesCompositedScrolling()) {
setNeedsCompositingGeometryUpdate();
setDescendantsNeedUpdateBackingAndHierarchyTraversal();
requiresRepaint = false;
}
if (requiresRepaint)
renderer().repaintUsingContainer(repaintContainer, rectForRepaint);
if (Element* element = renderer().element())
element->document().eventQueue().enqueueOrDispatchScrollEvent(*element);
if (scrollsOverflow())
view.frameView().didChangeScrollOffset();
view.frameView().viewportContentsChanged();
}
static inline bool frameElementAndViewPermitScroll(HTMLFrameElementBase* frameElementBase, FrameView& frameView)
{
if (frameElementBase && frameElementBase->scrollingMode() != ScrollbarAlwaysOff)
return true;
if (frameView.wasScrolledByUser())
return false;
return !frameView.frame().eventHandler().autoscrollInProgress();
}
bool RenderLayer::allowsCurrentScroll() const
{
if (!renderer().hasOverflowClip())
return false;
if (renderer().parent() && !renderer().parent()->style().lineClamp().isNone())
return false;
RenderBox* box = renderBox();
ASSERT(box);
if (renderer().frame().eventHandler().autoscrollInProgress()) {
return box->canBeProgramaticallyScrolled();
}
return box->hasHorizontalOverflow() || box->hasVerticalOverflow();
}
void RenderLayer::scrollRectToVisible(const LayoutRect& absoluteRect, bool insideFixed, const ScrollRectToVisibleOptions& options)
{
LOG_WITH_STREAM(Scrolling, stream << "Layer " << this << " scrollRectToVisible " << absoluteRect);
RenderLayer* parentLayer = nullptr;
LayoutRect newRect = absoluteRect;
FrameView& frameView = renderer().view().frameView();
if (renderer().parent())
parentLayer = renderer().parent()->enclosingLayer();
if (allowsCurrentScroll()) {
RenderBox* box = renderBox();
ASSERT(box);
LayoutRect localExposeRect(box->absoluteToLocalQuad(FloatQuad(FloatRect(absoluteRect))).boundingBox());
LayoutRect layerBounds(0_lu, 0_lu, box->clientWidth(), box->clientHeight());
LayoutRect revealRect = getRectToExpose(layerBounds, localExposeRect, insideFixed, options.alignX, options.alignY);
ScrollOffset clampedScrollOffset = clampScrollOffset(scrollOffset() + toIntSize(roundedIntRect(revealRect).location()));
if (clampedScrollOffset != scrollOffset()) {
ScrollOffset oldScrollOffset = scrollOffset();
scrollToOffset(clampedScrollOffset);
IntSize scrollOffsetDifference = scrollOffset() - oldScrollOffset;
localExposeRect.move(-scrollOffsetDifference);
newRect = LayoutRect(box->localToAbsoluteQuad(FloatQuad(FloatRect(localExposeRect)), UseTransforms).boundingBox());
}
} else if (!parentLayer && renderer().isRenderView()) {
HTMLFrameOwnerElement* ownerElement = renderer().document().ownerElement();
if (ownerElement && ownerElement->renderer()) {
HTMLFrameElementBase* frameElementBase = nullptr;
if (is<HTMLFrameElementBase>(*ownerElement))
frameElementBase = downcast<HTMLFrameElementBase>(ownerElement);
if (frameElementAndViewPermitScroll(frameElementBase, frameView)) {
ScriptDisallowedScope::InMainThread scriptDisallowedScope;
LayoutRect viewRect = frameView.visibleContentRect(LegacyIOSDocumentVisibleRect);
LayoutRect exposeRect = getRectToExpose(viewRect, absoluteRect, insideFixed, options.alignX, options.alignY);
IntPoint scrollOffset(roundedIntPoint(exposeRect.location()));
scrollOffset = scrollOffset.constrainedBetween(IntPoint(), IntPoint(frameView.contentsSize()));
frameView.setScrollPosition(scrollOffset);
if (options.shouldAllowCrossOriginScrolling == ShouldAllowCrossOriginScrolling::Yes || frameView.safeToPropagateScrollToParent()) {
parentLayer = ownerElement->renderer()->enclosingLayer();
newRect = frameView.contentsToContainingViewContents(enclosingIntRect(newRect));
insideFixed = false; } else
parentLayer = nullptr;
}
} else {
if (options.revealMode == SelectionRevealMode::RevealUpToMainFrame && frameView.frame().isMainFrame())
return;
#if !PLATFORM(IOS_FAMILY)
LayoutRect viewRect = frameView.visibleContentRect();
#else
LayoutRect viewRect = frameView.unobscuredContentRect();
#endif
LayoutRect targetRect = absoluteRect;
targetRect.move(0, frameView.headerHeight());
LayoutRect revealRect = getRectToExpose(viewRect, targetRect, insideFixed, options.alignX, options.alignY);
frameView.setScrollPosition(roundedIntPoint(revealRect.location()));
page().chrome().scrollRectIntoView(snappedIntRect(absoluteRect));
}
}
if (parentLayer)
parentLayer->scrollRectToVisible(newRect, insideFixed, options);
}
void RenderLayer::updateCompositingLayersAfterScroll()
{
if (compositor().hasContentCompositingLayers()) {
if (RenderLayer* compositingAncestor = stackingContext()->enclosingCompositingLayer()) {
if (usesCompositedScrolling())
compositor().updateCompositingLayers(CompositingUpdateType::OnCompositedScroll, compositingAncestor);
else {
compositingAncestor->setDescendantsNeedUpdateBackingAndHierarchyTraversal();
compositor().updateCompositingLayers(CompositingUpdateType::OnScroll, compositingAncestor);
}
}
}
}
LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const LayoutRect &exposeRect, bool insideFixed, const ScrollAlignment& alignX, const ScrollAlignment& alignY) const
{
FrameView& frameView = renderer().view().frameView();
if (renderer().isRenderView() && insideFixed) {
if (frameView.frameScaleFactor() == 1)
return visibleRect;
if (renderer().settings().visualViewportEnabled()) {
LayoutRect unscaledExposeRect = exposeRect;
unscaledExposeRect.scale(1 / frameView.frameScaleFactor());
unscaledExposeRect.move(0, -frameView.headerHeight());
LayoutRect layoutViewport = frameView.layoutViewportRect();
LayoutRect visualViewport = frameView.visualViewportRect();
unscaledExposeRect.intersect(layoutViewport);
unscaledExposeRect.setSize(unscaledExposeRect.size().shrunkTo(visualViewport.size()));
LayoutRect requiredVisualViewport = getRectToExpose(visualViewport, unscaledExposeRect, false, alignX, alignY);
requiredVisualViewport.scale(frameView.frameScaleFactor());
requiredVisualViewport.move(0, frameView.headerHeight());
return requiredVisualViewport;
}
}
ScrollAlignment::Behavior scrollX;
LayoutRect exposeRectX(exposeRect.x(), visibleRect.y(), exposeRect.width(), visibleRect.height());
LayoutUnit intersectWidth = intersection(visibleRect, exposeRectX).width();
if (intersectWidth == exposeRect.width() || intersectWidth >= MIN_INTERSECT_FOR_REVEAL)
scrollX = ScrollAlignment::getVisibleBehavior(alignX);
else if (intersectWidth == visibleRect.width()) {
scrollX = ScrollAlignment::getVisibleBehavior(alignX);
if (scrollX == ScrollAlignment::Behavior::AlignCenter)
scrollX = ScrollAlignment::Behavior::NoScroll;
} else if (intersectWidth > 0)
scrollX = ScrollAlignment::getPartialBehavior(alignX);
else
scrollX = ScrollAlignment::getHiddenBehavior(alignX);
if (scrollX == ScrollAlignment::Behavior::AlignToClosestEdge && exposeRect.maxX() > visibleRect.maxX() && exposeRect.width() < visibleRect.width())
scrollX = ScrollAlignment::Behavior::AlignRight;
LayoutUnit x;
if (scrollX == ScrollAlignment::Behavior::NoScroll)
x = visibleRect.x();
else if (scrollX == ScrollAlignment::Behavior::AlignRight)
x = exposeRect.maxX() - visibleRect.width();
else if (scrollX == ScrollAlignment::Behavior::AlignCenter)
x = exposeRect.x() + (exposeRect.width() - visibleRect.width()) / 2;
else
x = exposeRect.x();
ScrollAlignment::Behavior scrollY;
LayoutRect exposeRectY(visibleRect.x(), exposeRect.y(), visibleRect.width(), exposeRect.height());
LayoutUnit intersectHeight = intersection(visibleRect, exposeRectY).height();
if (intersectHeight == exposeRect.height())
scrollY = ScrollAlignment::getVisibleBehavior(alignY);
else if (intersectHeight == visibleRect.height()) {
scrollY = ScrollAlignment::getVisibleBehavior(alignY);
if (scrollY == ScrollAlignment::Behavior::AlignCenter)
scrollY = ScrollAlignment::Behavior::NoScroll;
} else if (intersectHeight > 0)
scrollY = ScrollAlignment::getPartialBehavior(alignY);
else
scrollY = ScrollAlignment::getHiddenBehavior(alignY);
if (scrollY == ScrollAlignment::Behavior::AlignToClosestEdge && exposeRect.maxY() > visibleRect.maxY() && exposeRect.height() < visibleRect.height())
scrollY = ScrollAlignment::Behavior::AlignBottom;
LayoutUnit y;
if (scrollY == ScrollAlignment::Behavior::NoScroll)
y = visibleRect.y();
else if (scrollY == ScrollAlignment::Behavior::AlignBottom)
y = exposeRect.maxY() - visibleRect.height();
else if (scrollY == ScrollAlignment::Behavior::AlignCenter)
y = exposeRect.y() + (exposeRect.height() - visibleRect.height()) / 2;
else
y = exposeRect.y();
return LayoutRect(LayoutPoint(x, y), visibleRect.size());
}
void RenderLayer::autoscroll(const IntPoint& positionInWindow)
{
IntPoint currentDocumentPosition = renderer().view().frameView().windowToContents(positionInWindow);
scrollRectToVisible(LayoutRect(currentDocumentPosition, LayoutSize(1, 1)), false, { SelectionRevealMode::Reveal, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded, ShouldAllowCrossOriginScrolling::Yes });
}
bool RenderLayer::canResize() const
{
return (renderer().hasOverflowClip() || renderer().isRenderIFrame()) && renderer().style().resize() != Resize::None;
}
void RenderLayer::resize(const PlatformMouseEvent& evt, const LayoutSize& oldOffset)
{
if (!inResizeMode() || !canResize() || !renderer().element())
return;
Element* element = renderer().element();
auto* renderer = downcast<RenderBox>(element->renderer());
Document& document = element->document();
if (!document.frame()->eventHandler().mousePressed())
return;
float zoomFactor = renderer->style().effectiveZoom();
LayoutSize newOffset = offsetFromResizeCorner(document.view()->windowToContents(evt.position()));
newOffset.setWidth(newOffset.width() / zoomFactor);
newOffset.setHeight(newOffset.height() / zoomFactor);
LayoutSize currentSize = LayoutSize(renderer->width() / zoomFactor, renderer->height() / zoomFactor);
LayoutSize minimumSize = element->minimumSizeForResizing().shrunkTo(currentSize);
element->setMinimumSizeForResizing(minimumSize);
LayoutSize adjustedOldOffset = LayoutSize(oldOffset.width() / zoomFactor, oldOffset.height() / zoomFactor);
if (shouldPlaceBlockDirectionScrollbarOnLeft()) {
newOffset.setWidth(-newOffset.width());
adjustedOldOffset.setWidth(-adjustedOldOffset.width());
}
LayoutSize difference = (currentSize + newOffset - adjustedOldOffset).expandedTo(minimumSize) - currentSize;
StyledElement* styledElement = downcast<StyledElement>(element);
bool isBoxSizingBorder = renderer->style().boxSizing() == BoxSizing::BorderBox;
Resize resize = renderer->style().resize();
if (resize != Resize::Vertical && difference.width()) {
if (is<HTMLFormControlElement>(*element)) {
styledElement->setInlineStyleProperty(CSSPropertyMarginLeft, renderer->marginLeft() / zoomFactor, CSSPrimitiveValue::CSS_PX);
styledElement->setInlineStyleProperty(CSSPropertyMarginRight, renderer->marginRight() / zoomFactor, CSSPrimitiveValue::CSS_PX);
}
LayoutUnit baseWidth = renderer->width() - (isBoxSizingBorder ? 0_lu : renderer->horizontalBorderAndPaddingExtent());
baseWidth = baseWidth / zoomFactor;
styledElement->setInlineStyleProperty(CSSPropertyWidth, roundToInt(baseWidth + difference.width()), CSSPrimitiveValue::CSS_PX);
}
if (resize != Resize::Horizontal && difference.height()) {
if (is<HTMLFormControlElement>(*element)) {
styledElement->setInlineStyleProperty(CSSPropertyMarginTop, renderer->marginTop() / zoomFactor, CSSPrimitiveValue::CSS_PX);
styledElement->setInlineStyleProperty(CSSPropertyMarginBottom, renderer->marginBottom() / zoomFactor, CSSPrimitiveValue::CSS_PX);
}
LayoutUnit baseHeight = renderer->height() - (isBoxSizingBorder ? 0_lu : renderer->verticalBorderAndPaddingExtent());
baseHeight = baseHeight / zoomFactor;
styledElement->setInlineStyleProperty(CSSPropertyHeight, roundToInt(baseHeight + difference.height()), CSSPrimitiveValue::CSS_PX);
}
document.updateLayout();
}
int RenderLayer::scrollSize(ScrollbarOrientation orientation) const
{
Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_hBar : m_vBar).get();
return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0;
}
void RenderLayer::setScrollOffset(const ScrollOffset& offset)
{
scrollTo(scrollPositionFromOffset(offset));
}
int RenderLayer::scrollOffset(ScrollbarOrientation orientation) const
{
if (orientation == HorizontalScrollbar)
return scrollOffset().x();
if (orientation == VerticalScrollbar)
return scrollOffset().y();
return 0;
}
IntRect RenderLayer::visibleContentRectInternal(VisibleContentRectIncludesScrollbars scrollbarInclusion, VisibleContentRectBehavior) const
{
IntSize scrollbarSpace;
if (showsOverflowControls() && scrollbarInclusion == IncludeScrollbars)
scrollbarSpace = scrollbarIntrusion();
auto visibleSize = this->visibleSize();
return { scrollPosition(), { std::max(0, visibleSize.width() - scrollbarSpace.width()), std::max(0, visibleSize.height() - scrollbarSpace.height()) } };
}
IntSize RenderLayer::overhangAmount() const
{
#if ENABLE(RUBBER_BANDING)
if (!renderer().settings().rubberBandingForSubScrollableRegionsEnabled())
return IntSize();
IntSize stretch;
ScrollOffset scrollOffset = scrollOffsetFromPosition(scrollPosition());
if (scrollOffset.y() < 0)
stretch.setHeight(scrollOffset.y());
else if (scrollableContentsSize().height() && scrollOffset.y() > scrollableContentsSize().height() - visibleHeight())
stretch.setHeight(scrollOffset.y() - (scrollableContentsSize().height() - visibleHeight()));
if (scrollOffset.x() < 0)
stretch.setWidth(scrollOffset.x());
else if (scrollableContentsSize().width() && scrollOffset.x() > scrollableContentsSize().width() - visibleWidth())
stretch.setWidth(scrollOffset.x() - (scrollableContentsSize().width() - visibleWidth()));
return stretch;
#else
return IntSize();
#endif
}
bool RenderLayer::isActive() const
{
return page().focusController().isActive();
}
static int cornerStart(const RenderLayer& layer, int minX, int maxX, int thickness)
{
if (layer.shouldPlaceBlockDirectionScrollbarOnLeft())
return minX + layer.renderer().style().borderLeftWidth();
return maxX - thickness - layer.renderer().style().borderRightWidth();
}
static LayoutRect cornerRect(const RenderLayer& layer, const LayoutRect& bounds)
{
int horizontalThickness;
int verticalThickness;
if (!layer.verticalScrollbar() && !layer.horizontalScrollbar()) {
horizontalThickness = ScrollbarTheme::theme().scrollbarThickness();
verticalThickness = horizontalThickness;
} else if (layer.verticalScrollbar() && !layer.horizontalScrollbar()) {
horizontalThickness = layer.verticalScrollbar()->width();
verticalThickness = horizontalThickness;
} else if (layer.horizontalScrollbar() && !layer.verticalScrollbar()) {
verticalThickness = layer.horizontalScrollbar()->height();
horizontalThickness = verticalThickness;
} else {
horizontalThickness = layer.verticalScrollbar()->width();
verticalThickness = layer.horizontalScrollbar()->height();
}
return LayoutRect(cornerStart(layer, bounds.x(), bounds.maxX(), horizontalThickness),
bounds.maxY() - verticalThickness - layer.renderer().style().borderBottomWidth(),
horizontalThickness, verticalThickness);
}
IntRect RenderLayer::scrollCornerRect() const
{
bool hasHorizontalBar = m_hBar && !m_hBar->isOverlayScrollbar();
bool hasVerticalBar = m_vBar && !m_vBar->isOverlayScrollbar();
bool hasResizer = renderer().style().resize() != Resize::None;
if ((hasHorizontalBar && hasVerticalBar) || (hasResizer && (hasHorizontalBar || hasVerticalBar)))
return snappedIntRect(cornerRect(*this, renderBox()->borderBoxRect()));
return IntRect();
}
static LayoutRect resizerCornerRect(const RenderLayer& layer, const LayoutRect& bounds)
{
ASSERT(layer.renderer().isBox());
if (layer.renderer().style().resize() == Resize::None)
return LayoutRect();
return cornerRect(layer, bounds);
}
LayoutRect RenderLayer::scrollCornerAndResizerRect() const
{
RenderBox* box = renderBox();
if (!box)
return LayoutRect();
LayoutRect scrollCornerAndResizer = scrollCornerRect();
if (scrollCornerAndResizer.isEmpty())
scrollCornerAndResizer = resizerCornerRect(*this, box->borderBoxRect());
return scrollCornerAndResizer;
}
bool RenderLayer::isScrollCornerVisible() const
{
ASSERT(renderer().isBox());
return !scrollCornerRect().isEmpty();
}
IntRect RenderLayer::convertFromScrollbarToContainingView(const Scrollbar& scrollbar, const IntRect& scrollbarRect) const
{
IntRect rect = scrollbarRect;
rect.move(scrollbarOffset(scrollbar));
return renderer().view().frameView().convertFromRendererToContainingView(&renderer(), rect);
}
IntRect RenderLayer::convertFromContainingViewToScrollbar(const Scrollbar& scrollbar, const IntRect& parentRect) const
{
IntRect rect = renderer().view().frameView().convertFromContainingViewToRenderer(&renderer(), parentRect);
rect.move(-scrollbarOffset(scrollbar));
return rect;
}
IntPoint RenderLayer::convertFromScrollbarToContainingView(const Scrollbar& scrollbar, const IntPoint& scrollbarPoint) const
{
IntPoint point = scrollbarPoint;
point.move(scrollbarOffset(scrollbar));
return renderer().view().frameView().convertFromRendererToContainingView(&renderer(), point);
}
IntPoint RenderLayer::convertFromContainingViewToScrollbar(const Scrollbar& scrollbar, const IntPoint& parentPoint) const
{
IntPoint point = renderer().view().frameView().convertFromContainingViewToRenderer(&renderer(), parentPoint);
point.move(-scrollbarOffset(scrollbar));
return point;
}
IntSize RenderLayer::visibleSize() const
{
RenderBox* box = renderBox();
if (!box)
return IntSize();
return IntSize(roundToInt(box->clientWidth()), roundToInt(box->clientHeight()));
}
IntSize RenderLayer::contentsSize() const
{
return IntSize(scrollWidth(), scrollHeight());
}
IntSize RenderLayer::scrollableContentsSize() const
{
IntSize contentsSize = this->contentsSize();
if (!hasScrollableHorizontalOverflow())
contentsSize.setWidth(std::min(contentsSize.width(), visibleSize().width()));
if (!hasScrollableVerticalOverflow())
contentsSize.setHeight(std::min(contentsSize.height(), visibleSize().height()));
return contentsSize;
}
void RenderLayer::availableContentSizeChanged(AvailableSizeChangeReason reason)
{
ScrollableArea::availableContentSizeChanged(reason);
if (reason == AvailableSizeChangeReason::ScrollbarsChanged) {
if (is<RenderBlock>(renderer()))
downcast<RenderBlock>(renderer()).setShouldForceRelayoutChildren(true);
renderer().setNeedsLayout();
}
}
bool RenderLayer::shouldSuspendScrollAnimations() const
{
return renderer().view().frameView().shouldSuspendScrollAnimations();
}
#if PLATFORM(IOS_FAMILY)
void RenderLayer::didStartScroll()
{
page().chrome().client().didStartOverflowScroll();
}
void RenderLayer::didEndScroll()
{
page().chrome().client().didEndOverflowScroll();
}
void RenderLayer::didUpdateScroll()
{
page().chrome().client().didLayout(ChromeClient::Scroll);
}
#endif
IntPoint RenderLayer::lastKnownMousePosition() const
{
return renderer().frame().eventHandler().lastKnownMousePosition();
}
bool RenderLayer::isHandlingWheelEvent() const
{
return renderer().frame().eventHandler().isHandlingWheelEvent();
}
IntRect RenderLayer::rectForHorizontalScrollbar(const IntRect& borderBoxRect) const
{
if (!m_hBar)
return IntRect();
const RenderBox* box = renderBox();
const IntRect& scrollCorner = scrollCornerRect();
return IntRect(horizontalScrollbarStart(borderBoxRect.x()),
borderBoxRect.maxY() - box->borderBottom() - m_hBar->height(),
borderBoxRect.width() - (box->borderLeft() + box->borderRight()) - scrollCorner.width(),
m_hBar->height());
}
IntRect RenderLayer::rectForVerticalScrollbar(const IntRect& borderBoxRect) const
{
if (!m_vBar)
return IntRect();
const RenderBox* box = renderBox();
const IntRect& scrollCorner = scrollCornerRect();
return IntRect(verticalScrollbarStart(borderBoxRect.x(), borderBoxRect.maxX()),
borderBoxRect.y() + box->borderTop(),
m_vBar->width(),
borderBoxRect.height() - (box->borderTop() + box->borderBottom()) - scrollCorner.height());
}
LayoutUnit RenderLayer::verticalScrollbarStart(int minX, int maxX) const
{
const RenderBox* box = renderBox();
if (shouldPlaceBlockDirectionScrollbarOnLeft())
return minX + box->borderLeft();
return maxX - box->borderRight() - m_vBar->width();
}
LayoutUnit RenderLayer::horizontalScrollbarStart(int minX) const
{
const RenderBox* box = renderBox();
int x = minX + box->borderLeft();
if (shouldPlaceBlockDirectionScrollbarOnLeft())
x += m_vBar ? m_vBar->width() : roundToInt(resizerCornerRect(*this, box->borderBoxRect()).width());
return x;
}
IntSize RenderLayer::scrollbarOffset(const Scrollbar& scrollbar) const
{
RenderBox* box = renderBox();
if (&scrollbar == m_vBar.get())
return IntSize(verticalScrollbarStart(0, box->width()), box->borderTop());
if (&scrollbar == m_hBar.get())
return IntSize(horizontalScrollbarStart(0), box->height() - box->borderBottom() - scrollbar.height());
ASSERT_NOT_REACHED();
return IntSize();
}
void RenderLayer::invalidateScrollbarRect(Scrollbar& scrollbar, const IntRect& rect)
{
if (!showsOverflowControls())
return;
if (&scrollbar == m_vBar.get()) {
if (GraphicsLayer* layer = layerForVerticalScrollbar()) {
layer->setNeedsDisplayInRect(rect);
return;
}
} else {
if (GraphicsLayer* layer = layerForHorizontalScrollbar()) {
layer->setNeedsDisplayInRect(rect);
return;
}
}
IntRect scrollRect = rect;
RenderBox* box = renderBox();
ASSERT(box);
if (!box->parent())
return;
if (&scrollbar == m_vBar.get())
scrollRect.move(verticalScrollbarStart(0, box->width()), box->borderTop());
else
scrollRect.move(horizontalScrollbarStart(0), box->height() - box->borderBottom() - scrollbar.height());
LayoutRect repaintRect = scrollRect;
renderBox()->flipForWritingMode(repaintRect);
renderer().repaintRectangle(repaintRect);
}
void RenderLayer::invalidateScrollCornerRect(const IntRect& rect)
{
if (!showsOverflowControls())
return;
if (GraphicsLayer* layer = layerForScrollCorner()) {
layer->setNeedsDisplayInRect(rect);
return;
}
if (m_scrollCorner)
m_scrollCorner->repaintRectangle(rect);
if (m_resizer)
m_resizer->repaintRectangle(rect);
}
static inline RenderElement* rendererForScrollbar(RenderLayerModelObject& renderer)
{
if (Element* element = renderer.element()) {
if (ShadowRoot* shadowRoot = element->containingShadowRoot()) {
if (shadowRoot->mode() == ShadowRootMode::UserAgent)
return shadowRoot->host()->renderer();
}
}
return &renderer;
}
Ref<Scrollbar> RenderLayer::createScrollbar(ScrollbarOrientation orientation)
{
RefPtr<Scrollbar> widget;
ASSERT(rendererForScrollbar(renderer()));
auto& actualRenderer = *rendererForScrollbar(renderer());
bool hasCustomScrollbarStyle = is<RenderBox>(actualRenderer) && downcast<RenderBox>(actualRenderer).style().hasPseudoStyle(PseudoId::Scrollbar);
if (hasCustomScrollbarStyle)
widget = RenderScrollbar::createCustomScrollbar(*this, orientation, downcast<RenderBox>(actualRenderer).element());
else {
widget = Scrollbar::createNativeScrollbar(*this, orientation, RegularScrollbar);
didAddScrollbar(widget.get(), orientation);
if (page().expectsWheelEventTriggers())
scrollAnimator().setWheelEventTestTrigger(page().testTrigger());
}
renderer().view().frameView().addChild(*widget);
return widget.releaseNonNull();
}
void RenderLayer::destroyScrollbar(ScrollbarOrientation orientation)
{
RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_hBar : m_vBar;
if (!scrollbar)
return;
if (!scrollbar->isCustomScrollbar())
willRemoveScrollbar(scrollbar.get(), orientation);
scrollbar->removeFromParent();
scrollbar = nullptr;
}
bool RenderLayer::scrollsOverflow() const
{
if (!is<RenderBox>(renderer()))
return false;
return downcast<RenderBox>(renderer()).scrollsOverflow();
}
void RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar)
{
if (hasScrollbar == hasHorizontalScrollbar())
return;
if (hasScrollbar) {
m_hBar = createScrollbar(HorizontalScrollbar);
#if ENABLE(RUBBER_BANDING)
ScrollElasticity elasticity = scrollsOverflow() && renderer().settings().rubberBandingForSubScrollableRegionsEnabled() ? ScrollElasticityAutomatic : ScrollElasticityNone;
ScrollableArea::setHorizontalScrollElasticity(elasticity);
#endif
} else {
destroyScrollbar(HorizontalScrollbar);
#if ENABLE(RUBBER_BANDING)
ScrollableArea::setHorizontalScrollElasticity(ScrollElasticityNone);
#endif
}
if (m_hBar)
m_hBar->styleChanged();
if (m_vBar)
m_vBar->styleChanged();
#if ENABLE(DASHBOARD_SUPPORT)
if (renderer().document().hasAnnotatedRegions())
renderer().document().setAnnotatedRegionsDirty(true);
#endif
}
void RenderLayer::setHasVerticalScrollbar(bool hasScrollbar)
{
if (hasScrollbar == hasVerticalScrollbar())
return;
if (hasScrollbar) {
m_vBar = createScrollbar(VerticalScrollbar);
#if ENABLE(RUBBER_BANDING)
ScrollElasticity elasticity = scrollsOverflow() && renderer().settings().rubberBandingForSubScrollableRegionsEnabled() ? ScrollElasticityAutomatic : ScrollElasticityNone;
ScrollableArea::setVerticalScrollElasticity(elasticity);
#endif
} else {
destroyScrollbar(VerticalScrollbar);
#if ENABLE(RUBBER_BANDING)
ScrollableArea::setVerticalScrollElasticity(ScrollElasticityNone);
#endif
}
if (m_hBar)
m_hBar->styleChanged();
if (m_vBar)
m_vBar->styleChanged();
#if ENABLE(DASHBOARD_SUPPORT)
if (renderer().document().hasAnnotatedRegions())
renderer().document().setAnnotatedRegionsDirty(true);
#endif
}
ScrollableArea* RenderLayer::enclosingScrollableArea() const
{
if (RenderLayer* scrollableLayer = enclosingScrollableLayer())
return scrollableLayer;
return nullptr;
}
bool RenderLayer::isScrollableOrRubberbandable()
{
return renderer().isScrollableOrRubberbandableBox();
}
bool RenderLayer::hasScrollableOrRubberbandableAncestor()
{
for (RenderLayer* nextLayer = parentLayerCrossFrame(*this); nextLayer; nextLayer = parentLayerCrossFrame(*nextLayer)) {
if (nextLayer->isScrollableOrRubberbandable())
return true;
}
return false;
}
bool RenderLayer::useDarkAppearance() const
{
return renderer().useDarkAppearance();
}
#if ENABLE(CSS_SCROLL_SNAP)
void RenderLayer::updateSnapOffsets()
{
if (!is<HTMLElement>(enclosingElement()) || !enclosingElement()->renderBox())
return;
RenderBox* box = enclosingElement()->renderBox();
updateSnapOffsetsForScrollableArea(*this, *downcast<HTMLElement>(enclosingElement()), *box, box->style());
}
bool RenderLayer::isScrollSnapInProgress() const
{
if (!scrollsOverflow())
return false;
if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
return scrollAnimator->isScrollSnapInProgress();
return false;
}
#endif
bool RenderLayer::usesMockScrollAnimator() const
{
return DeprecatedGlobalSettings::usesMockScrollAnimator();
}
void RenderLayer::logMockScrollAnimatorMessage(const String& message) const
{
renderer().document().addConsoleMessage(MessageSource::Other, MessageLevel::Debug, "RenderLayer: " + message);
}
int RenderLayer::verticalScrollbarWidth(OverlayScrollbarSizeRelevancy relevancy) const
{
if (!m_vBar
|| !showsOverflowControls()
|| (m_vBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_vBar->shouldParticipateInHitTesting())))
return 0;
return m_vBar->width();
}
int RenderLayer::horizontalScrollbarHeight(OverlayScrollbarSizeRelevancy relevancy) const
{
if (!m_hBar
|| !showsOverflowControls()
|| (m_hBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_hBar->shouldParticipateInHitTesting())))
return 0;
return m_hBar->height();
}
IntSize RenderLayer::offsetFromResizeCorner(const IntPoint& absolutePoint) const
{
IntSize elementSize = size();
if (shouldPlaceBlockDirectionScrollbarOnLeft())
elementSize.setWidth(0);
IntPoint resizerPoint = IntPoint(elementSize);
IntPoint localPoint = roundedIntPoint(absoluteToContents(absolutePoint));
return localPoint - resizerPoint;
}
bool RenderLayer::hasOverflowControls() const
{
return m_hBar || m_vBar || m_scrollCorner || renderer().style().resize() != Resize::None;
}
void RenderLayer::positionOverflowControls(const IntSize& offsetFromRoot)
{
if (!m_hBar && !m_vBar && !canResize())
return;
RenderBox* box = renderBox();
if (!box)
return;
const IntRect borderBox = snappedIntRect(box->borderBoxRect());
const IntRect& scrollCorner = scrollCornerRect();
IntRect absBounds(borderBox.location() + offsetFromRoot, borderBox.size());
if (m_vBar) {
IntRect vBarRect = rectForVerticalScrollbar(borderBox);
vBarRect.move(offsetFromRoot);
m_vBar->setFrameRect(vBarRect);
}
if (m_hBar) {
IntRect hBarRect = rectForHorizontalScrollbar(borderBox);
hBarRect.move(offsetFromRoot);
m_hBar->setFrameRect(hBarRect);
}
if (m_scrollCorner)
m_scrollCorner->setFrameRect(scrollCorner);
if (m_resizer)
m_resizer->setFrameRect(resizerCornerRect(*this, borderBox));
if (isComposited())
backing()->positionOverflowControlsLayers();
}
int RenderLayer::scrollWidth() const
{
ASSERT(renderBox());
if (m_scrollDimensionsDirty)
const_cast<RenderLayer*>(this)->computeScrollDimensions();
return m_scrollSize.width();
}
int RenderLayer::scrollHeight() const
{
ASSERT(renderBox());
if (m_scrollDimensionsDirty)
const_cast<RenderLayer*>(this)->computeScrollDimensions();
return m_scrollSize.height();
}
LayoutUnit RenderLayer::overflowTop() const
{
RenderBox* box = renderBox();
LayoutRect overflowRect(box->layoutOverflowRect());
box->flipForWritingMode(overflowRect);
return overflowRect.y();
}
LayoutUnit RenderLayer::overflowBottom() const
{
RenderBox* box = renderBox();
LayoutRect overflowRect(box->layoutOverflowRect());
box->flipForWritingMode(overflowRect);
return overflowRect.maxY();
}
LayoutUnit RenderLayer::overflowLeft() const
{
RenderBox* box = renderBox();
LayoutRect overflowRect(box->layoutOverflowRect());
box->flipForWritingMode(overflowRect);
return overflowRect.x();
}
LayoutUnit RenderLayer::overflowRight() const
{
RenderBox* box = renderBox();
LayoutRect overflowRect(box->layoutOverflowRect());
box->flipForWritingMode(overflowRect);
return overflowRect.maxX();
}
void RenderLayer::computeScrollDimensions()
{
RenderBox* box = renderBox();
ASSERT(box);
m_scrollDimensionsDirty = false;
m_scrollSize.setWidth(roundToInt(overflowRight() - overflowLeft()));
m_scrollSize.setHeight(roundToInt(overflowBottom() - overflowTop()));
int scrollableLeftOverflow = roundToInt(overflowLeft() - box->borderLeft());
if (shouldPlaceBlockDirectionScrollbarOnLeft())
scrollableLeftOverflow -= verticalScrollbarWidth();
int scrollableTopOverflow = roundToInt(overflowTop() - box->borderTop());
setScrollOrigin(IntPoint(-scrollableLeftOverflow, -scrollableTopOverflow));
}
bool RenderLayer::hasScrollableHorizontalOverflow() const
{
return hasHorizontalOverflow() && renderBox()->scrollsOverflowX();
}
bool RenderLayer::hasScrollableVerticalOverflow() const
{
return hasVerticalOverflow() && renderBox()->scrollsOverflowY();
}
bool RenderLayer::hasHorizontalOverflow() const
{
ASSERT(!m_scrollDimensionsDirty);
return scrollWidth() > roundToInt(renderBox()->clientWidth());
}
bool RenderLayer::hasVerticalOverflow() const
{
ASSERT(!m_scrollDimensionsDirty);
return scrollHeight() > roundToInt(renderBox()->clientHeight());
}
static bool styleRequiresScrollbar(const RenderStyle& style, ScrollbarOrientation axis)
{
Overflow overflow = axis == ScrollbarOrientation::HorizontalScrollbar ? style.overflowX() : style.overflowY();
bool overflowScrollActsLikeAuto = overflow == Overflow::Scroll && !style.hasPseudoStyle(PseudoId::Scrollbar) && ScrollbarTheme::theme().usesOverlayScrollbars();
return overflow == Overflow::Scroll && !overflowScrollActsLikeAuto;
}
static bool styleDefinesAutomaticScrollbar(const RenderStyle& style, ScrollbarOrientation axis)
{
Overflow overflow = axis == ScrollbarOrientation::HorizontalScrollbar ? style.overflowX() : style.overflowY();
bool overflowScrollActsLikeAuto = overflow == Overflow::Scroll && !style.hasPseudoStyle(PseudoId::Scrollbar) && ScrollbarTheme::theme().usesOverlayScrollbars();
return overflow == Overflow::Auto || overflowScrollActsLikeAuto;
}
void RenderLayer::updateScrollbarsAfterLayout()
{
RenderBox* box = renderBox();
ASSERT(box);
if (box->style().appearance() == ListboxPart)
return;
bool hasHorizontalOverflow = this->hasHorizontalOverflow();
bool hasVerticalOverflow = this->hasVerticalOverflow();
if (m_hBar && styleRequiresScrollbar(renderer().style(), HorizontalScrollbar))
m_hBar->setEnabled(hasHorizontalOverflow);
if (m_vBar && styleRequiresScrollbar(renderer().style(), VerticalScrollbar))
m_vBar->setEnabled(hasVerticalOverflow);
bool autoHorizontalScrollBarChanged = box->hasHorizontalScrollbarWithAutoBehavior() && (hasHorizontalScrollbar() != hasHorizontalOverflow);
bool autoVerticalScrollBarChanged = box->hasVerticalScrollbarWithAutoBehavior() && (hasVerticalScrollbar() != hasVerticalOverflow);
if (autoHorizontalScrollBarChanged || autoVerticalScrollBarChanged) {
if (box->hasHorizontalScrollbarWithAutoBehavior())
setHasHorizontalScrollbar(hasHorizontalOverflow);
if (box->hasVerticalScrollbarWithAutoBehavior())
setHasVerticalScrollbar(hasVerticalOverflow);
updateSelfPaintingLayer();
#if ENABLE(DASHBOARD_SUPPORT)
if (renderer().document().hasAnnotatedRegions())
renderer().document().setAnnotatedRegionsDirty(true);
#endif
renderer().repaint();
if (renderer().style().overflowX() == Overflow::Auto || renderer().style().overflowY() == Overflow::Auto) {
if (!m_inOverflowRelayout) {
m_inOverflowRelayout = true;
renderer().setNeedsLayout(MarkOnlyThis);
if (is<RenderBlock>(renderer())) {
RenderBlock& block = downcast<RenderBlock>(renderer());
block.scrollbarsChanged(autoHorizontalScrollBarChanged, autoVerticalScrollBarChanged);
block.layoutBlock(true);
} else
renderer().layout();
m_inOverflowRelayout = false;
}
}
RenderObject* parent = renderer().parent();
if (parent && parent->isFlexibleBox() && renderer().isBox())
downcast<RenderFlexibleBox>(parent)->clearCachedMainSizeForChild(*renderBox());
}
if (m_hBar) {
int clientWidth = roundToInt(box->clientWidth());
int pageStep = Scrollbar::pageStep(clientWidth);
m_hBar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
m_hBar->setProportion(clientWidth, m_scrollSize.width());
}
if (m_vBar) {
int clientHeight = roundToInt(box->clientHeight());
int pageStep = Scrollbar::pageStep(clientHeight);
m_vBar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
m_vBar->setProportion(clientHeight, m_scrollSize.height());
}
updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
}
void RenderLayer::updateScrollInfoAfterLayout()
{
RenderBox* box = renderBox();
if (!box)
return;
m_scrollDimensionsDirty = true;
ScrollOffset originalScrollOffset = scrollOffset();
computeScrollDimensions();
#if ENABLE(CSS_SCROLL_SNAP)
updateSnapOffsets();
#endif
if (!box->isHTMLMarquee() && !isRubberBandInProgress()) {
ScrollOffset clampedScrollOffset = clampScrollOffset(scrollOffset());
#if PLATFORM(IOS_FAMILY)
setAdjustForIOSCaretWhenScrolling(true);
#endif
if (clampedScrollOffset != scrollOffset())
scrollToOffset(clampedScrollOffset);
#if PLATFORM(IOS_FAMILY)
setAdjustForIOSCaretWhenScrolling(false);
#endif
}
updateScrollbarsAfterLayout();
if (originalScrollOffset != scrollOffset())
scrollToOffsetWithoutAnimation(IntPoint(scrollOffset()));
if (isComposited()) {
setNeedsCompositingGeometryUpdate();
setNeedsCompositingConfigurationUpdate();
}
#if PLATFORM(IOS_FAMILY)
if (canUseCompositedScrolling())
setNeedsPostLayoutCompositingUpdate();
#endif
updateScrollSnapState();
}
bool RenderLayer::overflowControlsIntersectRect(const IntRect& localRect) const
{
const IntRect borderBox = snappedIntRect(renderBox()->borderBoxRect());
if (rectForHorizontalScrollbar(borderBox).intersects(localRect))
return true;
if (rectForVerticalScrollbar(borderBox).intersects(localRect))
return true;
if (scrollCornerRect().intersects(localRect))
return true;
if (resizerCornerRect(*this, borderBox).intersects(localRect))
return true;
return false;
}
bool RenderLayer::showsOverflowControls() const
{
#if PLATFORM(IOS_FAMILY)
return !canUseCompositedScrolling();
#endif
return true;
}
void RenderLayer::paintOverflowControls(GraphicsContext& context, const IntPoint& paintOffset, const IntRect& damageRect, bool paintingOverlayControls)
{
if (!renderer().hasOverflowClip())
return;
if (!showsOverflowControls())
return;
if (hasOverlayScrollbars() && !paintingOverlayControls) {
m_cachedOverlayScrollbarOffset = paintOffset;
if ((m_hBar && layerForHorizontalScrollbar()) || (m_vBar && layerForVerticalScrollbar()))
return;
IntRect localDamgeRect = damageRect;
localDamgeRect.moveBy(-paintOffset);
if (!overflowControlsIntersectRect(localDamgeRect))
return;
RenderLayer* paintingRoot = enclosingCompositingLayer();
if (!paintingRoot)
paintingRoot = renderer().view().layer();
paintingRoot->setContainsDirtyOverlayScrollbars(true);
return;
}
if (paintingOverlayControls && !hasOverlayScrollbars())
return;
IntPoint adjustedPaintOffset = paintOffset;
if (paintingOverlayControls)
adjustedPaintOffset = m_cachedOverlayScrollbarOffset;
positionOverflowControls(toIntSize(adjustedPaintOffset));
if (m_hBar && !layerForHorizontalScrollbar())
m_hBar->paint(context, damageRect);
if (m_vBar && !layerForVerticalScrollbar())
m_vBar->paint(context, damageRect);
if (layerForScrollCorner())
return;
paintScrollCorner(context, adjustedPaintOffset, damageRect);
paintResizer(context, adjustedPaintOffset, damageRect);
}
void RenderLayer::paintScrollCorner(GraphicsContext& context, const IntPoint& paintOffset, const IntRect& damageRect)
{
IntRect absRect = scrollCornerRect();
absRect.moveBy(paintOffset);
if (!absRect.intersects(damageRect))
return;
if (context.invalidatingControlTints()) {
updateScrollCornerStyle();
return;
}
if (m_scrollCorner) {
m_scrollCorner->paintIntoRect(context, paintOffset, absRect);
return;
}
if (!hasOverlayScrollbars())
ScrollbarTheme::theme().paintScrollCorner(context, absRect);
}
void RenderLayer::drawPlatformResizerImage(GraphicsContext& context, const LayoutRect& resizerCornerRect)
{
RefPtr<Image> resizeCornerImage;
FloatSize cornerResizerSize;
if (renderer().document().deviceScaleFactor() >= 2) {
static NeverDestroyed<Image*> resizeCornerImageHiRes(&Image::loadPlatformResource("textAreaResizeCorner@2x").leakRef());
resizeCornerImage = resizeCornerImageHiRes;
cornerResizerSize = resizeCornerImage->size();
cornerResizerSize.scale(0.5f);
} else {
static NeverDestroyed<Image*> resizeCornerImageLoRes(&Image::loadPlatformResource("textAreaResizeCorner").leakRef());
resizeCornerImage = resizeCornerImageLoRes;
cornerResizerSize = resizeCornerImage->size();
}
if (shouldPlaceBlockDirectionScrollbarOnLeft()) {
context.save();
context.translate(resizerCornerRect.x() + cornerResizerSize.width(), resizerCornerRect.y() + resizerCornerRect.height() - cornerResizerSize.height());
context.scale(FloatSize(-1.0, 1.0));
if (resizeCornerImage)
context.drawImage(*resizeCornerImage, FloatRect(FloatPoint(), cornerResizerSize));
context.restore();
return;
}
if (!resizeCornerImage)
return;
FloatRect imageRect = snapRectToDevicePixels(LayoutRect(resizerCornerRect.maxXMaxYCorner() - cornerResizerSize, cornerResizerSize), renderer().document().deviceScaleFactor());
context.drawImage(*resizeCornerImage, imageRect);
}
void RenderLayer::paintResizer(GraphicsContext& context, const LayoutPoint& paintOffset, const LayoutRect& damageRect)
{
if (renderer().style().resize() == Resize::None)
return;
RenderBox* box = renderBox();
ASSERT(box);
LayoutRect absRect = resizerCornerRect(*this, box->borderBoxRect());
absRect.moveBy(paintOffset);
if (!absRect.intersects(damageRect))
return;
if (context.invalidatingControlTints()) {
updateResizerStyle();
return;
}
if (m_resizer) {
m_resizer->paintIntoRect(context, paintOffset, absRect);
return;
}
drawPlatformResizerImage(context, absRect);
if (!hasOverlayScrollbars() && (m_vBar || m_hBar)) {
GraphicsContextStateSaver stateSaver(context);
context.clip(absRect);
LayoutRect largerCorner = absRect;
largerCorner.setSize(LayoutSize(largerCorner.width() + 1_lu, largerCorner.height() + 1_lu));
context.setStrokeColor(Color(makeRGB(217, 217, 217)));
context.setStrokeThickness(1.0f);
context.setFillColor(Color::transparent);
context.drawRect(snappedIntRect(largerCorner));
}
}
bool RenderLayer::isPointInResizeControl(const IntPoint& absolutePoint) const
{
if (!canResize())
return false;
RenderBox* box = renderBox();
ASSERT(box);
IntPoint localPoint = roundedIntPoint(absoluteToContents(absolutePoint));
IntRect localBounds(IntPoint(), snappedIntRect(box->frameRect()).size());
return resizerCornerRect(*this, localBounds).contains(localPoint);
}
bool RenderLayer::hitTestOverflowControls(HitTestResult& result, const IntPoint& localPoint)
{
if (!m_hBar && !m_vBar && !canResize())
return false;
RenderBox* box = renderBox();
ASSERT(box);
IntRect resizeControlRect;
if (renderer().style().resize() != Resize::None) {
resizeControlRect = snappedIntRect(resizerCornerRect(*this, box->borderBoxRect()));
if (resizeControlRect.contains(localPoint))
return true;
}
int resizeControlSize = std::max(resizeControlRect.height(), 0);
if (m_vBar && m_vBar->shouldParticipateInHitTesting()) {
LayoutRect vBarRect(verticalScrollbarStart(0, box->width()),
box->borderTop(),
m_vBar->width(),
box->height() - (box->borderTop() + box->borderBottom()) - (m_hBar ? m_hBar->height() : resizeControlSize));
if (vBarRect.contains(localPoint)) {
result.setScrollbar(m_vBar.get());
return true;
}
}
resizeControlSize = std::max(resizeControlRect.width(), 0);
if (m_hBar && m_hBar->shouldParticipateInHitTesting()) {
LayoutRect hBarRect(horizontalScrollbarStart(0),
box->height() - box->borderBottom() - m_hBar->height(),
box->width() - (box->borderLeft() + box->borderRight()) - (m_vBar ? m_vBar->width() : resizeControlSize),
m_hBar->height());
if (hBarRect.contains(localPoint)) {
result.setScrollbar(m_hBar.get());
return true;
}
}
return false;
}
bool RenderLayer::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
{
return ScrollableArea::scroll(direction, granularity, multiplier);
}
void RenderLayer::paint(GraphicsContext& context, const LayoutRect& damageRect, const LayoutSize& subpixelOffset, OptionSet<PaintBehavior> paintBehavior, RenderObject* subtreePaintRoot, OptionSet<PaintLayerFlag> paintFlags, SecurityOriginPaintPolicy paintPolicy)
{
OverlapTestRequestMap overlapTestRequests;
LayerPaintingInfo paintingInfo(this, enclosingIntRect(damageRect), paintBehavior, subpixelOffset, subtreePaintRoot, &overlapTestRequests, paintPolicy == SecurityOriginPaintPolicy::AccessibleOriginOnly);
paintLayer(context, paintingInfo, paintFlags);
for (auto& widget : overlapTestRequests.keys())
widget->setOverlapTestResult(false);
}
void RenderLayer::paintOverlayScrollbars(GraphicsContext& context, const LayoutRect& damageRect, OptionSet<PaintBehavior> paintBehavior, RenderObject* subtreePaintRoot)
{
if (!m_containsDirtyOverlayScrollbars)
return;
LayerPaintingInfo paintingInfo(this, enclosingIntRect(damageRect), paintBehavior, LayoutSize(), subtreePaintRoot);
paintLayer(context, paintingInfo, PaintLayerPaintingOverlayScrollbars);
m_containsDirtyOverlayScrollbars = false;
}
static bool inContainingBlockChain(RenderLayer* startLayer, RenderLayer* endLayer)
{
if (startLayer == endLayer)
return true;
for (const auto* currentBlock = startLayer->renderer().containingBlock(); currentBlock && !is<RenderView>(*currentBlock); currentBlock = currentBlock->containingBlock()) {
if (currentBlock->layer() == endLayer)
return true;
}
return false;
}
void RenderLayer::clipToRect(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, const ClipRect& clipRect, BorderRadiusClippingRule rule)
{
float deviceScaleFactor = renderer().document().deviceScaleFactor();
bool needsClipping = !clipRect.isInfinite() && clipRect.rect() != paintingInfo.paintDirtyRect;
if (needsClipping || clipRect.affectedByRadius())
context.save();
if (needsClipping) {
LayoutRect adjustedClipRect = clipRect.rect();
adjustedClipRect.move(paintingInfo.subpixelOffset);
context.clip(snapRectToDevicePixels(adjustedClipRect, deviceScaleFactor));
}
if (clipRect.affectedByRadius()) {
for (RenderLayer* layer = rule == IncludeSelfForBorderRadius ? this : parent(); layer; layer = layer->parent()) {
if (layer->renderer().hasOverflowClip() && layer->renderer().style().hasBorderRadius() && inContainingBlockChain(this, layer)) {
LayoutRect adjustedClipRect = LayoutRect(toLayoutPoint(layer->offsetFromAncestor(paintingInfo.rootLayer, AdjustForColumns)), layer->size());
adjustedClipRect.move(paintingInfo.subpixelOffset);
FloatRoundedRect roundedRect = layer->renderer().style().getRoundedInnerBorderFor(adjustedClipRect).pixelSnappedRoundedRectForPainting(deviceScaleFactor);
if (roundedRect.intersectionIsRectangular(paintingInfo.paintDirtyRect))
context.clip(snapRectToDevicePixels(intersection(paintingInfo.paintDirtyRect, adjustedClipRect), deviceScaleFactor));
else
context.clipRoundedRect(roundedRect);
}
if (layer == paintingInfo.rootLayer)
break;
}
}
}
void RenderLayer::restoreClip(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, const ClipRect& clipRect)
{
if ((!clipRect.isInfinite() && clipRect.rect() != paintingInfo.paintDirtyRect) || clipRect.affectedByRadius())
context.restore();
}
static void performOverlapTests(OverlapTestRequestMap& overlapTestRequests, const RenderLayer* rootLayer, const RenderLayer* layer)
{
Vector<OverlapTestRequestClient*> overlappedRequestClients;
LayoutRect boundingBox = layer->boundingBox(rootLayer, layer->offsetFromAncestor(rootLayer));
for (auto& request : overlapTestRequests) {
if (!boundingBox.intersects(request.value))
continue;
request.key->setOverlapTestResult(true);
overlappedRequestClients.append(request.key);
}
for (auto* client : overlappedRequestClients)
overlapTestRequests.remove(client);
}
static inline bool shouldDoSoftwarePaint(const RenderLayer* layer, bool paintingReflection)
{
return paintingReflection && !layer->has3DTransform();
}
static inline bool shouldSuppressPaintingLayer(RenderLayer* layer)
{
if (layer->renderer().style().isNotFinal() && !layer->isRenderViewLayer() && !layer->renderer().isDocumentElementRenderer())
return true;
if (!layer->renderer().document().visualUpdatesAllowed())
return true;
return false;
}
static inline bool paintForFixedRootBackground(const RenderLayer* layer, OptionSet<RenderLayer::PaintLayerFlag> paintFlags)
{
return layer->renderer().isDocumentElementRenderer() && (paintFlags & RenderLayer::PaintLayerPaintingRootBackgroundOnly);
}
void RenderLayer::paintLayer(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, OptionSet<PaintLayerFlag> paintFlags)
{
if (isComposited()) {
if (context.performingPaintInvalidation() || (paintingInfo.paintBehavior & PaintBehavior::FlattenCompositingLayers))
paintFlags.add(PaintLayerTemporaryClipRects);
else if (!backing()->paintsIntoWindow()
&& !backing()->paintsIntoCompositedAncestor()
&& !shouldDoSoftwarePaint(this, paintFlags.contains(PaintLayerPaintingReflection))
&& !paintForFixedRootBackground(this, paintFlags)) {
return;
}
} else if (viewportConstrainedNotCompositedReason() == NotCompositedForBoundsOutOfView) {
ASSERT(renderer().isFixedPositioned());
return;
}
if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant())
return;
if (shouldSuppressPaintingLayer(this))
return;
if (!renderer().opacity())
return;
if (paintsWithTransparency(paintingInfo.paintBehavior))
paintFlags.add(PaintLayerHaveTransparency);
if (paintsWithTransform(paintingInfo.paintBehavior) && !(paintFlags & PaintLayerAppliedTransform)) {
TransformationMatrix layerTransform = renderableTransform(paintingInfo.paintBehavior);
if (!layerTransform.isInvertible())
return;
if (paintFlags & PaintLayerHaveTransparency) {
if (parent())
parent()->beginTransparencyLayers(context, paintingInfo, paintingInfo.paintDirtyRect);
else
beginTransparencyLayers(context, paintingInfo, paintingInfo.paintDirtyRect);
}
if (enclosingPaginationLayer(ExcludeCompositedPaginatedLayers)) {
paintTransformedLayerIntoFragments(context, paintingInfo, paintFlags);
return;
}
ClipRect clipRect = paintingInfo.paintDirtyRect;
if (parent()) {
ClipRectsContext clipRectsContext(paintingInfo.rootLayer, (paintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects,
IgnoreOverlayScrollbarSize, (paintFlags & PaintLayerPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip);
clipRect = backgroundClipRect(clipRectsContext);
clipRect.intersect(paintingInfo.paintDirtyRect);
parent()->clipToRect(context, paintingInfo, clipRect);
}
paintLayerByApplyingTransform(context, paintingInfo, paintFlags);
if (parent())
parent()->restoreClip(context, paintingInfo, clipRect);
return;
}
paintLayerContentsAndReflection(context, paintingInfo, paintFlags);
}
void RenderLayer::paintLayerContentsAndReflection(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, OptionSet<PaintLayerFlag> paintFlags)
{
ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant());
auto localPaintFlags = paintFlags - PaintLayerAppliedTransform;
if (m_reflection && !m_paintingInsideReflection) {
m_paintingInsideReflection = true;
reflectionLayer()->paintLayer(context, paintingInfo, localPaintFlags | PaintLayerPaintingReflection);
m_paintingInsideReflection = false;
}
localPaintFlags.add(paintLayerPaintingCompositingAllPhasesFlags());
paintLayerContents(context, paintingInfo, localPaintFlags);
}
bool RenderLayer::setupFontSubpixelQuantization(GraphicsContext& context, bool& didQuantizeFonts)
{
if (context.paintingDisabled())
return false;
bool scrollingOnMainThread = true;
#if ENABLE(ASYNC_SCROLLING)
if (ScrollingCoordinator* scrollingCoordinator = page().scrollingCoordinator())
scrollingOnMainThread = scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(renderer().view().frameView());
#endif
bool contentsScrollByPainting = (renderer().hasOverflowClip() && !usesCompositedScrolling()) || (renderer().frame().ownerElement());
bool isZooming = !page().chrome().client().hasStablePageScaleFactor();
if (scrollingOnMainThread || contentsScrollByPainting || isZooming) {
didQuantizeFonts = context.shouldSubpixelQuantizeFonts();
context.setShouldSubpixelQuantizeFonts(false);
return true;
}
return false;
}
static inline LayoutRect computeReferenceBox(const RenderObject& renderer, const CSSBoxType& boxType, const LayoutSize& offsetFromRoot, const LayoutRect& rootRelativeBounds)
{
if (!renderer.isBox())
return rootRelativeBounds;
LayoutRect referenceBox;
const auto& box = downcast<RenderBox>(renderer);
switch (boxType) {
case CSSBoxType::ContentBox:
case CSSBoxType::FillBox:
referenceBox = box.contentBoxRect();
referenceBox.move(offsetFromRoot);
break;
case CSSBoxType::PaddingBox:
referenceBox = box.paddingBoxRect();
referenceBox.move(offsetFromRoot);
break;
case CSSBoxType::MarginBox:
referenceBox = box.marginBoxRect();
referenceBox.move(offsetFromRoot);
break;
case CSSBoxType::StrokeBox:
case CSSBoxType::ViewBox:
case CSSBoxType::BorderBox:
case CSSBoxType::BoxMissing:
referenceBox = box.borderBoxRect();
referenceBox.move(offsetFromRoot);
break;
}
return referenceBox;
}
Path RenderLayer::computeClipPath(const LayoutSize& offsetFromRoot, LayoutRect& rootRelativeBounds, WindRule& windRule) const
{
const RenderStyle& style = renderer().style();
float deviceSaleFactor = renderer().document().deviceScaleFactor();
if (is<ShapeClipPathOperation>(*style.clipPath())) {
auto& clipPath = downcast<ShapeClipPathOperation>(*style.clipPath());
FloatRect referenceBox = snapRectToDevicePixels(computeReferenceBox(renderer(), clipPath.referenceBox(), offsetFromRoot, rootRelativeBounds), deviceSaleFactor);
windRule = clipPath.windRule();
return clipPath.pathForReferenceRect(referenceBox);
}
if (is<BoxClipPathOperation>(*style.clipPath()) && is<RenderBox>(renderer())) {
auto& clipPath = downcast<BoxClipPathOperation>(*style.clipPath());
FloatRoundedRect shapeRect = computeRoundedRectForBoxShape(clipPath.referenceBox(), downcast<RenderBox>(renderer())).pixelSnappedRoundedRectForPainting(deviceSaleFactor);
shapeRect.move(offsetFromRoot);
windRule = WindRule::NonZero;
return clipPath.pathForReferenceRect(shapeRect);
}
return Path();
}
bool RenderLayer::setupClipPath(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, const LayoutSize& offsetFromRoot, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed)
{
if (!renderer().hasClipPath() || context.paintingDisabled())
return false;
if (!rootRelativeBoundsComputed) {
rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, offsetFromRoot, { });
rootRelativeBoundsComputed = true;
}
if (is<RenderSVGRoot>(renderer()))
return false;
auto& style = renderer().style();
LayoutSize paintingOffsetFromRoot = LayoutSize(snapSizeToDevicePixel(offsetFromRoot + paintingInfo.subpixelOffset, LayoutPoint(), renderer().document().deviceScaleFactor()));
ASSERT(style.clipPath());
if (is<ShapeClipPathOperation>(*style.clipPath()) || (is<BoxClipPathOperation>(*style.clipPath()) && is<RenderBox>(renderer()))) {
WindRule windRule;
Path path = computeClipPath(paintingOffsetFromRoot, rootRelativeBounds, windRule);
context.save();
context.clipPath(path, windRule);
return true;
}
if (style.clipPath()->type() == ClipPathOperation::Reference) {
ReferenceClipPathOperation* referenceClipPathOperation = static_cast<ReferenceClipPathOperation*>(style.clipPath());
Element* element = renderer().document().getElementById(referenceClipPathOperation->fragment());
if (element && element->renderer() && is<RenderSVGResourceClipper>(element->renderer())) {
context.save();
float deviceSaleFactor = renderer().document().deviceScaleFactor();
FloatRect referenceBox = snapRectToDevicePixels(computeReferenceBox(renderer(), CSSBoxType::ContentBox, paintingOffsetFromRoot, rootRelativeBounds), deviceSaleFactor);
FloatPoint offset {referenceBox.location()};
context.translate(offset);
FloatRect svgReferenceBox {FloatPoint(), referenceBox.size()};
downcast<RenderSVGResourceClipper>(*element->renderer()).applyClippingToContext(renderer(), svgReferenceBox, paintingInfo.paintDirtyRect, context);
context.translate(FloatPoint(-offset.x(), -offset.y()));
return true;
}
}
return false;
}
RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags) const
{
if (context.paintingDisabled())
return nullptr;
if (paintFlags & PaintLayerPaintingOverlayScrollbars)
return nullptr;
if (!paintsWithFilters())
return nullptr;
if (m_filters && m_filters->filter())
return m_filters.get();
return nullptr;
}
GraphicsContext* RenderLayer::setupFilters(GraphicsContext& destinationContext, LayerPaintingInfo& paintingInfo, OptionSet<PaintLayerFlag> paintFlags, const LayoutSize& offsetFromRoot, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed)
{
auto* paintingFilters = filtersForPainting(destinationContext, paintFlags);
if (!paintingFilters)
return nullptr;
LayoutRect filterRepaintRect = paintingFilters->dirtySourceRect();
filterRepaintRect.move(offsetFromRoot);
if (!rootRelativeBoundsComputed) {
rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, offsetFromRoot, { });
rootRelativeBoundsComputed = true;
}
GraphicsContext* filterContext = paintingFilters->beginFilterEffect(destinationContext, enclosingIntRect(rootRelativeBounds), enclosingIntRect(paintingInfo.paintDirtyRect), enclosingIntRect(filterRepaintRect));
if (!filterContext)
return nullptr;
paintingInfo.paintDirtyRect = paintingFilters->repaintRect();
paintingInfo.clipToDirtyRect = !paintingFilters->hasFilterThatMovesPixels();
paintingInfo.requireSecurityOriginAccessForWidgets = paintingFilters->hasFilterThatShouldBeRestrictedBySecurityOrigin();
return filterContext;
}
void RenderLayer::applyFilters(GraphicsContext& originalContext, const LayerPaintingInfo& paintingInfo, const LayerFragments& layerFragments)
{
ClipRect backgroundRect = layerFragments.isEmpty() ? ClipRect() : layerFragments[0].backgroundRect;
clipToRect(originalContext, paintingInfo, backgroundRect);
m_filters->applyFilterEffect(originalContext);
restoreClip(originalContext, paintingInfo, backgroundRect);
}
void RenderLayer::paintLayerContents(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, OptionSet<PaintLayerFlag> paintFlags)
{
ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant());
auto localPaintFlags = paintFlags - PaintLayerAppliedTransform;
bool haveTransparency = localPaintFlags.contains(PaintLayerHaveTransparency);
bool isSelfPaintingLayer = this->isSelfPaintingLayer();
bool isPaintingOverlayScrollbars = paintFlags.contains(PaintLayerPaintingOverlayScrollbars);
bool isPaintingScrollingContent = paintFlags.contains(PaintLayerPaintingCompositingScrollingPhase);
bool isPaintingCompositedForeground = paintFlags.contains(PaintLayerPaintingCompositingForegroundPhase);
bool isPaintingCompositedBackground = paintFlags.contains(PaintLayerPaintingCompositingBackgroundPhase);
bool isPaintingOverflowContents = paintFlags.contains(PaintLayerPaintingOverflowContents);
bool shouldPaintOutline = isSelfPaintingLayer && !isPaintingOverlayScrollbars
&& ((isPaintingScrollingContent && isPaintingCompositedBackground)
|| (!isPaintingScrollingContent && isPaintingCompositedForeground));
bool shouldPaintContent = m_hasVisibleContent && isSelfPaintingLayer && !isPaintingOverlayScrollbars;
if (localPaintFlags & PaintLayerPaintingRootBackgroundOnly && !renderer().isRenderView() && !renderer().isDocumentElementRenderer())
return;
updateLayerListsIfNeeded();
LayoutSize offsetFromRoot = offsetFromAncestor(paintingInfo.rootLayer);
LayoutRect rootRelativeBounds;
bool rootRelativeBoundsComputed = false;
bool didQuantizeFonts = true;
bool needToAdjustSubpixelQuantization = setupFontSubpixelQuantization(context, didQuantizeFonts);
LayoutSize columnAwareOffsetFromRoot = offsetFromRoot;
if (renderer().enclosingFragmentedFlow() && (renderer().hasClipPath() || filtersForPainting(context, paintFlags)))
columnAwareOffsetFromRoot = toLayoutSize(convertToLayerCoords(paintingInfo.rootLayer, LayoutPoint(), AdjustForColumns));
bool hasClipPath = false;
if (shouldApplyClipPath(paintingInfo.paintBehavior, localPaintFlags))
hasClipPath = setupClipPath(context, paintingInfo, columnAwareOffsetFromRoot, rootRelativeBounds, rootRelativeBoundsComputed);
bool selectionAndBackgroundsOnly = paintingInfo.paintBehavior.contains(PaintBehavior::SelectionAndBackgroundsOnly);
bool selectionOnly = paintingInfo.paintBehavior.contains(PaintBehavior::SelectionOnly);
SinglePaintFrequencyTracking singlePaintFrequencyTracking(m_paintFrequencyTracker, shouldPaintContent);
LayerFragments layerFragments;
RenderObject* subtreePaintRootForRenderer = nullptr;
{ LayerPaintingInfo localPaintingInfo(paintingInfo);
GraphicsContext* filterContext = setupFilters(context, localPaintingInfo, paintFlags, columnAwareOffsetFromRoot, rootRelativeBounds, rootRelativeBoundsComputed);
if (filterContext && haveTransparency) {
beginTransparencyLayers(context, localPaintingInfo, paintingInfo.paintDirtyRect);
}
GraphicsContext& currentContext = filterContext ? *filterContext : context;
if (localPaintingInfo.subtreePaintRoot && !renderer().isDescendantOf(localPaintingInfo.subtreePaintRoot))
subtreePaintRootForRenderer = localPaintingInfo.subtreePaintRoot;
if (localPaintingInfo.overlapTestRequests && isSelfPaintingLayer)
performOverlapTests(*localPaintingInfo.overlapTestRequests, localPaintingInfo.rootLayer, this);
OptionSet<PaintBehavior> paintBehavior = PaintBehavior::Normal;
if (localPaintFlags & PaintLayerPaintingSkipRootBackground)
paintBehavior.add(PaintBehavior::SkipRootBackground);
else if (localPaintFlags & PaintLayerPaintingRootBackgroundOnly)
paintBehavior.add(PaintBehavior::RootBackgroundOnly);
if (paintingInfo.paintBehavior & PaintBehavior::FlattenCompositingLayers)
paintBehavior.add(PaintBehavior::FlattenCompositingLayers);
if (paintingInfo.paintBehavior & PaintBehavior::Snapshotting)
paintBehavior.add(PaintBehavior::Snapshotting);
if ((paintingInfo.paintBehavior & PaintBehavior::TileFirstPaint) && isRenderViewLayer())
paintBehavior.add(PaintBehavior::TileFirstPaint);
if (paintingInfo.paintBehavior & PaintBehavior::ExcludeSelection)
paintBehavior.add(PaintBehavior::ExcludeSelection);
LayoutRect paintDirtyRect = localPaintingInfo.paintDirtyRect;
if (shouldPaintContent || shouldPaintOutline || isPaintingOverlayScrollbars) {
if (!localPaintingInfo.clipToDirtyRect && renderer().hasOverflowClip()) {
localPaintingInfo.clipToDirtyRect = true;
paintDirtyRect = clipRectRelativeToAncestor(localPaintingInfo.rootLayer, offsetFromRoot, LayoutRect::infiniteRect());
}
collectFragments(layerFragments, localPaintingInfo.rootLayer, paintDirtyRect, ExcludeCompositedPaginatedLayers,
(localPaintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize,
(isPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip, offsetFromRoot);
updatePaintingInfoForFragments(layerFragments, localPaintingInfo, localPaintFlags, shouldPaintContent, offsetFromRoot);
}
if (isPaintingCompositedBackground) {
if (shouldPaintContent && !selectionOnly) {
paintBackgroundForFragments(layerFragments, currentContext, context, paintingInfo.paintDirtyRect, haveTransparency,
localPaintingInfo, paintBehavior, subtreePaintRootForRenderer);
}
}
if ((isPaintingScrollingContent && isPaintingOverflowContents) || (!isPaintingScrollingContent && isPaintingCompositedBackground))
paintList(negativeZOrderLayers(), currentContext, localPaintingInfo, localPaintFlags);
if (isPaintingCompositedForeground) {
if (shouldPaintContent) {
paintForegroundForFragments(layerFragments, currentContext, context, paintingInfo.paintDirtyRect, haveTransparency,
localPaintingInfo, paintBehavior, subtreePaintRootForRenderer);
}
}
if (shouldPaintOutline)
paintOutlineForFragments(layerFragments, currentContext, localPaintingInfo, paintBehavior, subtreePaintRootForRenderer);
if (isPaintingCompositedForeground) {
paintList(normalFlowLayers(), currentContext, localPaintingInfo, localPaintFlags);
paintList(positiveZOrderLayers(), currentContext, localPaintingInfo, localPaintFlags);
}
if (isPaintingOverlayScrollbars && hasScrollbars())
paintOverflowControlsForFragments(layerFragments, currentContext, localPaintingInfo);
if (filterContext) {
LayerFragments layerFragments;
collectFragments(layerFragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, ExcludeCompositedPaginatedLayers,
(localPaintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize,
(isPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip, offsetFromRoot);
updatePaintingInfoForFragments(layerFragments, paintingInfo, localPaintFlags, shouldPaintContent, offsetFromRoot);
applyFilters(context, paintingInfo, layerFragments);
}
}
if (shouldPaintContent && !(selectionOnly || selectionAndBackgroundsOnly)) {
OptionSet<PaintBehavior> paintBehavior = PaintBehavior::Normal;
if (paintingInfo.paintBehavior & PaintBehavior::FlattenCompositingLayers)
paintBehavior.add(PaintBehavior::FlattenCompositingLayers);
if (paintingInfo.paintBehavior & PaintBehavior::Snapshotting)
paintBehavior.add(PaintBehavior::Snapshotting);
if (paintingInfo.paintBehavior & PaintBehavior::TileFirstPaint)
paintBehavior.add(PaintBehavior::TileFirstPaint);
if (shouldPaintMask(paintingInfo.paintBehavior, localPaintFlags)) {
paintMaskForFragments(layerFragments, context, paintingInfo, paintBehavior, subtreePaintRootForRenderer);
}
if (!(paintFlags & PaintLayerPaintingCompositingMaskPhase) && (paintFlags & PaintLayerPaintingCompositingClipPathPhase)) {
paintChildClippingMaskForFragments(layerFragments, context, paintingInfo, paintBehavior, subtreePaintRootForRenderer);
}
if (localPaintFlags & PaintLayerPaintingChildClippingMaskPhase) {
paintChildClippingMaskForFragments(layerFragments, context, paintingInfo, paintBehavior, subtreePaintRootForRenderer);
}
}
if (haveTransparency && m_usedTransparency && !m_paintingInsideReflection) {
context.endTransparencyLayer();
context.restore();
m_usedTransparency = false;
}
if (needToAdjustSubpixelQuantization)
context.setShouldSubpixelQuantizeFonts(didQuantizeFonts);
if (hasClipPath)
context.restore();
}
void RenderLayer::paintLayerByApplyingTransform(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, OptionSet<PaintLayerFlag> paintFlags, const LayoutSize& translationOffset)
{
float deviceScaleFactor = renderer().document().deviceScaleFactor();
LayoutSize offsetFromParent = offsetFromAncestor(paintingInfo.rootLayer);
offsetFromParent += translationOffset;
TransformationMatrix transform(renderableTransform(paintingInfo.paintBehavior));
LayoutSize offsetForThisLayer = offsetFromParent + paintingInfo.subpixelOffset;
FloatSize devicePixelSnappedOffsetForThisLayer = toFloatSize(roundPointToDevicePixels(toLayoutPoint(offsetForThisLayer), deviceScaleFactor));
transform.translateRight(devicePixelSnappedOffsetForThisLayer.width(), devicePixelSnappedOffsetForThisLayer.height());
AffineTransform oldTransfrom = context.getCTM();
context.concatCTM(transform.toAffineTransform());
LayoutSize adjustedSubpixelOffset = offsetForThisLayer - LayoutSize(devicePixelSnappedOffsetForThisLayer);
LayerPaintingInfo transformedPaintingInfo(paintingInfo);
transformedPaintingInfo.rootLayer = this;
transformedPaintingInfo.paintDirtyRect = LayoutRect(encloseRectToDevicePixels(transform.inverse().valueOr(AffineTransform()).mapRect(paintingInfo.paintDirtyRect), deviceScaleFactor));
transformedPaintingInfo.subpixelOffset = adjustedSubpixelOffset;
paintLayerContentsAndReflection(context, transformedPaintingInfo, paintFlags);
context.setCTM(oldTransfrom);
}
void RenderLayer::paintList(LayerList layerIterator, GraphicsContext& context, const LayerPaintingInfo& paintingInfo, OptionSet<PaintLayerFlag> paintFlags)
{
if (layerIterator.begin() == layerIterator.end())
return;
if (!hasSelfPaintingLayerDescendant())
return;
#if !ASSERT_DISABLED
LayerListMutationDetector mutationChecker(*this);
#endif
for (auto* childLayer : layerIterator)
childLayer->paintLayer(context, paintingInfo, paintFlags);
}
RenderLayer* RenderLayer::enclosingPaginationLayerInSubtree(const RenderLayer* rootLayer, PaginationInclusionMode mode) const
{
RenderLayer* paginationLayer = enclosingPaginationLayer(mode);
if (!paginationLayer || rootLayer == paginationLayer)
return paginationLayer;
for (const RenderLayer* layer = this; layer; layer = layer->parent()) {
if (layer == rootLayer)
return nullptr;
if (layer == paginationLayer)
return paginationLayer;
}
ASSERT_NOT_REACHED();
return nullptr;
}
void RenderLayer::collectFragments(LayerFragments& fragments, const RenderLayer* rootLayer, const LayoutRect& dirtyRect, PaginationInclusionMode inclusionMode,
ClipRectsType clipRectsType, OverlayScrollbarSizeRelevancy inOverlayScrollbarSizeRelevancy, ShouldRespectOverflowClip respectOverflowClip, const LayoutSize& offsetFromRoot,
const LayoutRect* layerBoundingBox, ShouldApplyRootOffsetToFragments applyRootOffsetToFragments)
{
RenderLayer* paginationLayer = enclosingPaginationLayerInSubtree(rootLayer, inclusionMode);
if (!paginationLayer || hasTransform()) {
LayerFragment fragment;
ClipRectsContext clipRectsContext(rootLayer, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip);
calculateRects(clipRectsContext, dirtyRect, fragment.layerBounds, fragment.backgroundRect, fragment.foregroundRect, offsetFromRoot);
fragments.append(fragment);
return;
}
LayoutSize offsetWithinPaginatedLayer = offsetFromAncestor(paginationLayer);
ClipRectsContext paginationClipRectsContext(paginationLayer, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip);
LayoutRect layerBoundsInFragmentedFlow;
ClipRect backgroundRectInFragmentedFlow;
ClipRect foregroundRectInFragmentedFlow;
calculateRects(paginationClipRectsContext, LayoutRect::infiniteRect(), layerBoundsInFragmentedFlow, backgroundRectInFragmentedFlow, foregroundRectInFragmentedFlow,
offsetWithinPaginatedLayer);
LayoutRect layerBoundingBoxInFragmentedFlow = layerBoundingBox ? *layerBoundingBox : boundingBox(paginationLayer, offsetWithinPaginatedLayer);
layerBoundingBoxInFragmentedFlow.intersect(backgroundRectInFragmentedFlow.rect());
auto& enclosingFragmentedFlow = downcast<RenderFragmentedFlow>(paginationLayer->renderer());
RenderLayer* parentPaginationLayer = paginationLayer->parent()->enclosingPaginationLayerInSubtree(rootLayer, inclusionMode);
LayerFragments ancestorFragments;
if (parentPaginationLayer) {
LayoutRect layerFragmentBoundingBoxInParentPaginationLayer = enclosingFragmentedFlow.fragmentsBoundingBox(layerBoundingBoxInFragmentedFlow);
LayoutSize offsetWithinParentPaginatedLayer = paginationLayer->offsetFromAncestor(parentPaginationLayer);
layerFragmentBoundingBoxInParentPaginationLayer.move(offsetWithinParentPaginatedLayer);
parentPaginationLayer->collectFragments(ancestorFragments, rootLayer, dirtyRect, inclusionMode, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip,
offsetFromAncestor(rootLayer), &layerFragmentBoundingBoxInParentPaginationLayer, ApplyRootOffsetToFragments);
if (ancestorFragments.isEmpty())
return;
for (auto& ancestorFragment : ancestorFragments) {
LayoutRect dirtyRectInFragmentedFlow(dirtyRect);
dirtyRectInFragmentedFlow.move(-offsetWithinParentPaginatedLayer - ancestorFragment.paginationOffset);
size_t oldSize = fragments.size();
enclosingFragmentedFlow.collectLayerFragments(fragments, layerBoundingBoxInFragmentedFlow, dirtyRectInFragmentedFlow);
size_t newSize = fragments.size();
if (oldSize == newSize)
continue;
for (size_t i = oldSize; i < newSize; ++i) {
LayerFragment& fragment = fragments.at(i);
fragment.setRects(layerBoundsInFragmentedFlow, backgroundRectInFragmentedFlow, foregroundRectInFragmentedFlow, &layerBoundingBoxInFragmentedFlow);
fragment.moveBy(toLayoutPoint(ancestorFragment.paginationOffset + fragment.paginationOffset + offsetWithinParentPaginatedLayer));
fragment.intersect(ancestorFragment.paginationClip);
fragment.intersect(fragment.paginationClip);
if (applyRootOffsetToFragments == ApplyRootOffsetToFragments)
fragment.paginationOffset = fragment.paginationOffset + offsetWithinParentPaginatedLayer;
}
}
return;
}
LayoutSize offsetOfPaginationLayerFromRoot = enclosingPaginationLayer(inclusionMode)->offsetFromAncestor(rootLayer);
LayoutRect dirtyRectInFragmentedFlow(dirtyRect);
dirtyRectInFragmentedFlow.move(-offsetOfPaginationLayerFromRoot);
enclosingFragmentedFlow.collectLayerFragments(fragments, layerBoundingBoxInFragmentedFlow, dirtyRectInFragmentedFlow);
if (fragments.isEmpty())
return;
ClipRect ancestorClipRect = dirtyRect;
if (paginationLayer->parent()) {
ClipRectsContext clipRectsContext(rootLayer, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip);
ancestorClipRect = paginationLayer->backgroundClipRect(clipRectsContext);
ancestorClipRect.intersect(dirtyRect);
}
for (auto& fragment : fragments) {
fragment.setRects(layerBoundsInFragmentedFlow, backgroundRectInFragmentedFlow, foregroundRectInFragmentedFlow, &layerBoundingBoxInFragmentedFlow);
fragment.moveBy(toLayoutPoint(fragment.paginationOffset + offsetOfPaginationLayerFromRoot));
fragment.intersect(ancestorClipRect);
fragment.intersect(fragment.paginationClip);
if (applyRootOffsetToFragments == ApplyRootOffsetToFragments)
fragment.paginationOffset = fragment.paginationOffset + offsetOfPaginationLayerFromRoot;
}
}
void RenderLayer::updatePaintingInfoForFragments(LayerFragments& fragments, const LayerPaintingInfo& localPaintingInfo, OptionSet<PaintLayerFlag> localPaintFlags,
bool shouldPaintContent, const LayoutSize& offsetFromRoot)
{
for (auto& fragment : fragments) {
fragment.shouldPaintContent = shouldPaintContent;
if (this != localPaintingInfo.rootLayer || !(localPaintFlags & PaintLayerPaintingOverflowContents)) {
LayoutSize newOffsetFromRoot = offsetFromRoot + fragment.paginationOffset;
fragment.shouldPaintContent &= intersectsDamageRect(fragment.layerBounds, fragment.backgroundRect.rect(), localPaintingInfo.rootLayer, newOffsetFromRoot, fragment.hasBoundingBox ? &fragment.boundingBox : 0);
}
}
}
void RenderLayer::paintTransformedLayerIntoFragments(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, OptionSet<PaintLayerFlag> paintFlags)
{
LayerFragments enclosingPaginationFragments;
LayoutSize offsetOfPaginationLayerFromRoot;
RenderLayer* paginatedLayer = enclosingPaginationLayer(ExcludeCompositedPaginatedLayers);
LayoutRect transformedExtent = transparencyClipBox(*this, paginatedLayer, PaintingTransparencyClipBox, RootOfTransparencyClipBox, paintingInfo.paintBehavior);
paginatedLayer->collectFragments(enclosingPaginationFragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, ExcludeCompositedPaginatedLayers,
(paintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize,
(paintFlags & PaintLayerPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip, offsetOfPaginationLayerFromRoot, &transformedExtent);
for (const auto& fragment : enclosingPaginationFragments) {
LayoutRect clipRect = fragment.backgroundRect.rect();
if (parent() != paginatedLayer) {
offsetOfPaginationLayerFromRoot = toLayoutSize(paginatedLayer->convertToLayerCoords(paintingInfo.rootLayer, toLayoutPoint(offsetOfPaginationLayerFromRoot)));
ClipRectsContext clipRectsContext(paginatedLayer, (paintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects,
IgnoreOverlayScrollbarSize, (paintFlags & PaintLayerPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip);
LayoutRect parentClipRect = backgroundClipRect(clipRectsContext).rect();
parentClipRect.move(fragment.paginationOffset + offsetOfPaginationLayerFromRoot);
clipRect.intersect(parentClipRect);
}
parent()->clipToRect(context, paintingInfo, clipRect);
paintLayerByApplyingTransform(context, paintingInfo, paintFlags, fragment.paginationOffset);
parent()->restoreClip(context, paintingInfo, clipRect);
}
}
void RenderLayer::paintBackgroundForFragments(const LayerFragments& layerFragments, GraphicsContext& context, GraphicsContext& contextForTransparencyLayer,
const LayoutRect& transparencyPaintDirtyRect, bool haveTransparency, const LayerPaintingInfo& localPaintingInfo, OptionSet<PaintBehavior> paintBehavior,
RenderObject* subtreePaintRootForRenderer)
{
for (const auto& fragment : layerFragments) {
if (!fragment.shouldPaintContent)
continue;
if (haveTransparency)
beginTransparencyLayers(contextForTransparencyLayer, localPaintingInfo, transparencyPaintDirtyRect);
if (localPaintingInfo.clipToDirtyRect) {
clipToRect(context, localPaintingInfo, fragment.backgroundRect, DoNotIncludeSelfForBorderRadius); }
PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhase::BlockBackground, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer(), this);
renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset));
if (localPaintingInfo.clipToDirtyRect)
restoreClip(context, localPaintingInfo, fragment.backgroundRect);
}
}
void RenderLayer::paintForegroundForFragments(const LayerFragments& layerFragments, GraphicsContext& context, GraphicsContext& contextForTransparencyLayer,
const LayoutRect& transparencyPaintDirtyRect, bool haveTransparency, const LayerPaintingInfo& localPaintingInfo, OptionSet<PaintBehavior> paintBehavior,
RenderObject* subtreePaintRootForRenderer)
{
if (haveTransparency) {
for (const auto& fragment : layerFragments) {
if (fragment.shouldPaintContent && !fragment.foregroundRect.isEmpty()) {
beginTransparencyLayers(contextForTransparencyLayer, localPaintingInfo, transparencyPaintDirtyRect);
break;
}
}
}
OptionSet<PaintBehavior> localPaintBehavior;
if (localPaintingInfo.paintBehavior & PaintBehavior::ForceBlackText)
localPaintBehavior = PaintBehavior::ForceBlackText;
else if (localPaintingInfo.paintBehavior & PaintBehavior::ForceWhiteText)
localPaintBehavior = PaintBehavior::ForceWhiteText;
else
localPaintBehavior = paintBehavior;
if (localPaintingInfo.paintBehavior & PaintBehavior::ExcludeSelection)
localPaintBehavior.add(PaintBehavior::ExcludeSelection);
if (localPaintingInfo.paintBehavior & PaintBehavior::Snapshotting)
localPaintBehavior.add(PaintBehavior::Snapshotting);
if (localPaintingInfo.paintBehavior & PaintBehavior::TileFirstPaint)
localPaintBehavior.add(PaintBehavior::TileFirstPaint);
bool shouldClip = localPaintingInfo.clipToDirtyRect && layerFragments.size() == 1 && layerFragments[0].shouldPaintContent && !layerFragments[0].foregroundRect.isEmpty();
ClipRect clippedRect;
if (shouldClip) {
clippedRect = layerFragments[0].foregroundRect;
clipToRect(context, localPaintingInfo, clippedRect);
}
bool selectionOnly = localPaintingInfo.paintBehavior.containsAny({ PaintBehavior::SelectionAndBackgroundsOnly, PaintBehavior::SelectionOnly });
paintForegroundForFragmentsWithPhase(selectionOnly ? PaintPhase::Selection : PaintPhase::ChildBlockBackgrounds, layerFragments,
context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer);
if (!selectionOnly) {
paintForegroundForFragmentsWithPhase(PaintPhase::Float, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer);
paintForegroundForFragmentsWithPhase(PaintPhase::Foreground, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer);
paintForegroundForFragmentsWithPhase(PaintPhase::ChildOutlines, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer);
}
if (shouldClip)
restoreClip(context, localPaintingInfo, clippedRect);
}
void RenderLayer::paintForegroundForFragmentsWithPhase(PaintPhase phase, const LayerFragments& layerFragments, GraphicsContext& context,
const LayerPaintingInfo& localPaintingInfo, OptionSet<PaintBehavior> paintBehavior, RenderObject* subtreePaintRootForRenderer)
{
bool shouldClip = localPaintingInfo.clipToDirtyRect && layerFragments.size() > 1;
for (const auto& fragment : layerFragments) {
if (!fragment.shouldPaintContent || fragment.foregroundRect.isEmpty())
continue;
if (shouldClip)
clipToRect(context, localPaintingInfo, fragment.foregroundRect);
PaintInfo paintInfo(context, fragment.foregroundRect.rect(), phase, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer(), this, localPaintingInfo.requireSecurityOriginAccessForWidgets);
if (phase == PaintPhase::Foreground)
paintInfo.overlapTestRequests = localPaintingInfo.overlapTestRequests;
renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset));
if (shouldClip)
restoreClip(context, localPaintingInfo, fragment.foregroundRect);
}
}
void RenderLayer::paintOutlineForFragments(const LayerFragments& layerFragments, GraphicsContext& context, const LayerPaintingInfo& localPaintingInfo,
OptionSet<PaintBehavior> paintBehavior, RenderObject* subtreePaintRootForRenderer)
{
for (const auto& fragment : layerFragments) {
if (fragment.backgroundRect.isEmpty())
continue;
PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhase::SelfOutline, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer(), this);
clipToRect(context, localPaintingInfo, fragment.backgroundRect, DoNotIncludeSelfForBorderRadius);
renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset));
restoreClip(context, localPaintingInfo, fragment.backgroundRect);
}
}
void RenderLayer::paintMaskForFragments(const LayerFragments& layerFragments, GraphicsContext& context, const LayerPaintingInfo& localPaintingInfo,
OptionSet<PaintBehavior> paintBehavior, RenderObject* subtreePaintRootForRenderer)
{
for (const auto& fragment : layerFragments) {
if (!fragment.shouldPaintContent)
continue;
if (localPaintingInfo.clipToDirtyRect)
clipToRect(context, localPaintingInfo, fragment.backgroundRect, DoNotIncludeSelfForBorderRadius);
PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhase::Mask, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer(), this);
renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset));
if (localPaintingInfo.clipToDirtyRect)
restoreClip(context, localPaintingInfo, fragment.backgroundRect);
}
}
void RenderLayer::paintChildClippingMaskForFragments(const LayerFragments& layerFragments, GraphicsContext& context, const LayerPaintingInfo& localPaintingInfo, OptionSet<PaintBehavior> paintBehavior, RenderObject* subtreePaintRootForRenderer)
{
for (const auto& fragment : layerFragments) {
if (!fragment.shouldPaintContent)
continue;
if (localPaintingInfo.clipToDirtyRect)
clipToRect(context, localPaintingInfo, fragment.foregroundRect, IncludeSelfForBorderRadius);
PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhase::ClippingMask, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer(), this);
renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset));
if (localPaintingInfo.clipToDirtyRect)
restoreClip(context, localPaintingInfo, fragment.foregroundRect);
}
}
void RenderLayer::paintOverflowControlsForFragments(const LayerFragments& layerFragments, GraphicsContext& context, const LayerPaintingInfo& localPaintingInfo)
{
for (const auto& fragment : layerFragments) {
if (fragment.backgroundRect.isEmpty())
continue;
clipToRect(context, localPaintingInfo, fragment.backgroundRect);
paintOverflowControls(context, roundedIntPoint(toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset)),
snappedIntRect(fragment.backgroundRect.rect()), true);
restoreClip(context, localPaintingInfo, fragment.backgroundRect);
}
}
bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result)
{
return hitTest(request, result.hitTestLocation(), result);
}
bool RenderLayer::hitTest(const HitTestRequest& request, const HitTestLocation& hitTestLocation, HitTestResult& result)
{
ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant());
ASSERT(!renderer().view().needsLayout());
ASSERT(!isRenderFragmentedFlow());
LayoutRect hitTestArea = renderer().view().documentRect();
if (!request.ignoreClipping()) {
const auto& settings = renderer().settings();
if (settings.visualViewportEnabled() && settings.clientCoordinatesRelativeToLayoutViewport()) {
auto& frameView = renderer().view().frameView();
LayoutRect absoluteLayoutViewportRect = frameView.layoutViewportRect();
auto scaleFactor = frameView.frame().frameScaleFactor();
if (scaleFactor > 1)
absoluteLayoutViewportRect.scale(scaleFactor);
hitTestArea.intersect(absoluteLayoutViewportRect);
} else
hitTestArea.intersect(renderer().view().frameView().visibleContentRect(LegacyIOSDocumentVisibleRect));
}
RenderLayer* insideLayer = hitTestLayer(this, nullptr, request, result, hitTestArea, hitTestLocation, false);
if (!insideLayer) {
if (!request.isChildFrameHitTest() && (request.active() || request.release()) && isRenderViewLayer()) {
renderer().updateHitTestResult(result, downcast<RenderView>(renderer()).flipForWritingMode(hitTestLocation.point()));
insideLayer = this;
}
}
Node* node = result.innerNode();
if (node && !result.URLElement())
result.setURLElement(node->enclosingLinkEventParentOrSelf());
return insideLayer;
}
Element* RenderLayer::enclosingElement() const
{
for (RenderElement* r = &renderer(); r; r = r->parent()) {
if (Element* e = r->element())
return e;
}
return nullptr;
}
RenderLayer* RenderLayer::enclosingFragmentedFlowAncestor() const
{
RenderLayer* curr = parent();
for (; curr && !curr->isRenderFragmentedFlow(); curr = curr->parent()) {
if (curr->isStackingContext() && curr->isComposited()) {
return nullptr;
}
}
return curr;
}
static double computeZOffset(const HitTestingTransformState& transformState)
{
if (transformState.m_accumulatedTransform.isAffine())
return 0;
FloatPoint targetPoint = transformState.mappedPoint();
FloatPoint3D backmappedPoint = transformState.m_accumulatedTransform.mapPoint(FloatPoint3D(targetPoint));
return backmappedPoint.z();
}
Ref<HitTestingTransformState> RenderLayer::createLocalTransformState(RenderLayer* rootLayer, RenderLayer* containerLayer,
const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation,
const HitTestingTransformState* containerTransformState,
const LayoutSize& translationOffset) const
{
RefPtr<HitTestingTransformState> transformState;
LayoutSize offset;
if (containerTransformState) {
transformState = HitTestingTransformState::create(*containerTransformState);
offset = offsetFromAncestor(containerLayer);
} else {
transformState = HitTestingTransformState::create(hitTestLocation.transformedPoint(), hitTestLocation.transformedRect(), FloatQuad(hitTestRect));
offset = offsetFromAncestor(rootLayer);
}
offset += translationOffset;
RenderObject* containerRenderer = containerLayer ? &containerLayer->renderer() : nullptr;
if (renderer().shouldUseTransformFromContainer(containerRenderer)) {
TransformationMatrix containerTransform;
renderer().getTransformFromContainer(containerRenderer, offset, containerTransform);
transformState->applyTransform(containerTransform, HitTestingTransformState::AccumulateTransform);
} else {
transformState->translate(offset.width(), offset.height(), HitTestingTransformState::AccumulateTransform);
}
return transformState.releaseNonNull();
}
static bool isHitCandidate(const RenderLayer* hitLayer, bool canDepthSort, double* zOffset, const HitTestingTransformState* transformState)
{
if (!hitLayer)
return false;
if (canDepthSort)
return true;
if (zOffset) {
ASSERT(transformState);
double childZOffset = computeZOffset(*transformState);
if (childZOffset > *zOffset) {
*zOffset = childZOffset;
return true;
}
return false;
}
return true;
}
RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result,
const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, bool appliedTransform,
const HitTestingTransformState* transformState, double* zOffset)
{
updateLayerListsIfNeeded();
if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant())
return nullptr;
if (transform() && !appliedTransform) {
if (enclosingPaginationLayer(IncludeCompositedPaginatedLayers))
return hitTestTransformedLayerInFragments(rootLayer, containerLayer, request, result, hitTestRect, hitTestLocation, transformState, zOffset);
if (parent()) {
ClipRectsContext clipRectsContext(rootLayer, RootRelativeClipRects, IncludeOverlayScrollbarSize);
ClipRect clipRect = backgroundClipRect(clipRectsContext);
if (!clipRect.intersects(hitTestLocation))
return nullptr;
}
return hitTestLayerByApplyingTransform(rootLayer, containerLayer, request, result, hitTestRect, hitTestLocation, transformState, zOffset);
}
update3DTransformedDescendantStatus();
RefPtr<HitTestingTransformState> localTransformState;
if (appliedTransform) {
ASSERT(transformState);
localTransformState = const_cast<HitTestingTransformState*>(transformState);
} else if (transformState || has3DTransformedDescendant() || preserves3D()) {
localTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestLocation, transformState);
}
if (localTransformState && renderer().style().backfaceVisibility() == BackfaceVisibility::Hidden) {
Optional<TransformationMatrix> invertedMatrix = localTransformState->m_accumulatedTransform.inverse();
if (invertedMatrix && invertedMatrix.value().m33() < 0)
return nullptr;
}
RefPtr<HitTestingTransformState> unflattenedTransformState = localTransformState;
if (localTransformState && !preserves3D()) {
unflattenedTransformState = HitTestingTransformState::create(*localTransformState);
localTransformState->flatten();
}
double localZOffset = -std::numeric_limits<double>::infinity();
double* zOffsetForDescendantsPtr = nullptr;
double* zOffsetForContentsPtr = nullptr;
bool depthSortDescendants = false;
if (preserves3D()) {
depthSortDescendants = true;
zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset;
zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset;
} else if (zOffset) {
zOffsetForDescendantsPtr = nullptr;
zOffsetForContentsPtr = zOffset;
}
RenderLayer* candidateLayer = nullptr;
#if !ASSERT_DISABLED
LayerListMutationDetector mutationChecker(*this);
#endif
auto* hitLayer = hitTestList(positiveZOrderLayers(), rootLayer, request, result, hitTestRect, hitTestLocation,
localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants);
if (hitLayer) {
if (!depthSortDescendants)
return hitLayer;
candidateLayer = hitLayer;
}
hitLayer = hitTestList(normalFlowLayers(), rootLayer, request, result, hitTestRect, hitTestLocation,
localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants);
if (hitLayer) {
if (!depthSortDescendants)
return hitLayer;
candidateLayer = hitLayer;
}
LayerFragments layerFragments;
collectFragments(layerFragments, rootLayer, hitTestRect, IncludeCompositedPaginatedLayers, RootRelativeClipRects, IncludeOverlayScrollbarSize, RespectOverflowClip,
offsetFromAncestor(rootLayer));
if (canResize() && hitTestResizerInFragments(layerFragments, hitTestLocation)) {
renderer().updateHitTestResult(result, hitTestLocation.point());
return this;
}
if (isSelfPaintingLayer()) {
HitTestResult tempResult(result.hitTestLocation());
bool insideFragmentForegroundRect = false;
if (hitTestContentsForFragments(layerFragments, request, tempResult, hitTestLocation, HitTestDescendants, insideFragmentForegroundRect)
&& isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) {
if (request.resultIsElementList())
result.append(tempResult, request);
else
result = tempResult;
if (!depthSortDescendants)
return this;
candidateLayer = this;
} else if (insideFragmentForegroundRect && request.resultIsElementList())
result.append(tempResult, request);
}
hitLayer = hitTestList(negativeZOrderLayers(), rootLayer, request, result, hitTestRect, hitTestLocation,
localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants);
if (hitLayer) {
if (!depthSortDescendants)
return hitLayer;
candidateLayer = hitLayer;
}
if (candidateLayer)
return candidateLayer;
if (isSelfPaintingLayer()) {
HitTestResult tempResult(result.hitTestLocation());
bool insideFragmentBackgroundRect = false;
if (hitTestContentsForFragments(layerFragments, request, tempResult, hitTestLocation, HitTestSelf, insideFragmentBackgroundRect)
&& isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) {
if (request.resultIsElementList())
result.append(tempResult, request);
else
result = tempResult;
return this;
}
if (insideFragmentBackgroundRect && request.resultIsElementList())
result.append(tempResult, request);
}
return nullptr;
}
bool RenderLayer::hitTestContentsForFragments(const LayerFragments& layerFragments, const HitTestRequest& request, HitTestResult& result,
const HitTestLocation& hitTestLocation, HitTestFilter hitTestFilter, bool& insideClipRect) const
{
if (layerFragments.isEmpty())
return false;
for (int i = layerFragments.size() - 1; i >= 0; --i) {
const LayerFragment& fragment = layerFragments.at(i);
if ((hitTestFilter == HitTestSelf && !fragment.backgroundRect.intersects(hitTestLocation))
|| (hitTestFilter == HitTestDescendants && !fragment.foregroundRect.intersects(hitTestLocation)))
continue;
insideClipRect = true;
if (hitTestContents(request, result, fragment.layerBounds, hitTestLocation, hitTestFilter))
return true;
}
return false;
}
bool RenderLayer::hitTestResizerInFragments(const LayerFragments& layerFragments, const HitTestLocation& hitTestLocation) const
{
if (layerFragments.isEmpty())
return false;
for (int i = layerFragments.size() - 1; i >= 0; --i) {
const LayerFragment& fragment = layerFragments.at(i);
if (fragment.backgroundRect.intersects(hitTestLocation) && resizerCornerRect(*this, snappedIntRect(fragment.layerBounds)).contains(hitTestLocation.roundedPoint()))
return true;
}
return false;
}
RenderLayer* RenderLayer::hitTestTransformedLayerInFragments(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result,
const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset)
{
LayerFragments enclosingPaginationFragments;
LayoutSize offsetOfPaginationLayerFromRoot;
RenderLayer* paginatedLayer = enclosingPaginationLayer(IncludeCompositedPaginatedLayers);
LayoutRect transformedExtent = transparencyClipBox(*this, paginatedLayer, HitTestingTransparencyClipBox, RootOfTransparencyClipBox);
paginatedLayer->collectFragments(enclosingPaginationFragments, rootLayer, hitTestRect, IncludeCompositedPaginatedLayers,
RootRelativeClipRects, IncludeOverlayScrollbarSize, RespectOverflowClip, offsetOfPaginationLayerFromRoot, &transformedExtent);
for (int i = enclosingPaginationFragments.size() - 1; i >= 0; --i) {
const LayerFragment& fragment = enclosingPaginationFragments.at(i);
LayoutRect clipRect = fragment.backgroundRect.rect();
if (parent() != paginatedLayer) {
offsetOfPaginationLayerFromRoot = toLayoutSize(paginatedLayer->convertToLayerCoords(rootLayer, toLayoutPoint(offsetOfPaginationLayerFromRoot)));
ClipRectsContext clipRectsContext(paginatedLayer, RootRelativeClipRects, IncludeOverlayScrollbarSize);
LayoutRect parentClipRect = backgroundClipRect(clipRectsContext).rect();
parentClipRect.move(fragment.paginationOffset + offsetOfPaginationLayerFromRoot);
clipRect.intersect(parentClipRect);
}
if (!hitTestLocation.intersects(clipRect))
continue;
RenderLayer* hitLayer = hitTestLayerByApplyingTransform(rootLayer, containerLayer, request, result, hitTestRect, hitTestLocation,
transformState, zOffset, fragment.paginationOffset);
if (hitLayer)
return hitLayer;
}
return nullptr;
}
RenderLayer* RenderLayer::hitTestLayerByApplyingTransform(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result,
const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset,
const LayoutSize& translationOffset)
{
Ref<HitTestingTransformState> newTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestLocation, transformState, translationOffset);
if (!newTransformState->m_accumulatedTransform.isInvertible())
return nullptr;
FloatPoint localPoint = newTransformState->mappedPoint();
FloatQuad localPointQuad = newTransformState->mappedQuad();
LayoutRect localHitTestRect = newTransformState->boundsOfMappedArea();
HitTestLocation newHitTestLocation;
if (hitTestLocation.isRectBasedTest())
newHitTestLocation = HitTestLocation(localPoint, localPointQuad);
else
newHitTestLocation = HitTestLocation(localPoint);
return hitTestLayer(this, containerLayer, request, result, localHitTestRect, newHitTestLocation, true, newTransformState.ptr(), zOffset);
}
bool RenderLayer::hitTestContents(const HitTestRequest& request, HitTestResult& result, const LayoutRect& layerBounds, const HitTestLocation& hitTestLocation, HitTestFilter hitTestFilter) const
{
ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant());
if (!renderer().hitTest(request, result, hitTestLocation, toLayoutPoint(layerBounds.location() - renderBoxLocation()), hitTestFilter)) {
ASSERT(!result.innerNode() || (request.resultIsElementList() && result.listBasedTestResult().size()));
return false;
}
if (!result.innerNode() || !result.innerNonSharedNode()) {
if (isOutOfFlowRenderFragmentedFlow()) {
return false;
}
Element* e = enclosingElement();
if (!result.innerNode())
result.setInnerNode(e);
if (!result.innerNonSharedNode())
result.setInnerNonSharedNode(e);
}
return true;
}
RenderLayer* RenderLayer::hitTestList(LayerList layerIterator, RenderLayer* rootLayer,
const HitTestRequest& request, HitTestResult& result,
const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation,
const HitTestingTransformState* transformState,
double* zOffsetForDescendants, double* zOffset,
const HitTestingTransformState* unflattenedTransformState,
bool depthSortDescendants)
{
if (layerIterator.begin() == layerIterator.end())
return nullptr;
if (!hasSelfPaintingLayerDescendant())
return nullptr;
RenderLayer* resultLayer = nullptr;
for (auto iter = layerIterator.rbegin(); iter != layerIterator.rend(); ++iter) {
auto* childLayer = *iter;
HitTestResult tempResult(result.hitTestLocation());
auto* hitLayer = childLayer->hitTestLayer(rootLayer, this, request, tempResult, hitTestRect, hitTestLocation, false, transformState, zOffsetForDescendants);
ASSERT(!result.isRectBasedTest() || request.resultIsElementList());
if (request.resultIsElementList())
result.append(tempResult, request);
if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState)) {
resultLayer = hitLayer;
if (!request.resultIsElementList())
result = tempResult;
if (!depthSortDescendants)
break;
}
}
return resultLayer;
}
Ref<ClipRects> RenderLayer::updateClipRects(const ClipRectsContext& clipRectsContext)
{
ClipRectsType clipRectsType = clipRectsContext.clipRectsType;
ASSERT(clipRectsType < NumCachedClipRectsTypes);
if (m_clipRectsCache) {
if (auto* clipRects = m_clipRectsCache->getClipRects(clipRectsType, clipRectsContext.respectOverflowClip)) {
ASSERT(clipRectsContext.rootLayer == m_clipRectsCache->m_clipRectsRoot[clipRectsType]);
ASSERT(m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] == clipRectsContext.overlayScrollbarSizeRelevancy);
#ifdef CHECK_CACHED_CLIP_RECTS
ClipRectsContext tempContext(clipRectsContext);
tempContext.clipRectsType = TemporaryClipRects;
Ref<ClipRects> tempClipRects = ClipRects::create();
calculateClipRects(tempContext, tempClipRects);
ASSERT(tempClipRects.get() == *clipRects);
#endif
return *clipRects; }
}
if (!m_clipRectsCache)
m_clipRectsCache = std::make_unique<ClipRectsCache>();
#ifndef NDEBUG
m_clipRectsCache->m_clipRectsRoot[clipRectsType] = clipRectsContext.rootLayer;
m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] = clipRectsContext.overlayScrollbarSizeRelevancy;
#endif
RefPtr<ClipRects> parentClipRects;
if (auto* parentLayer = (clipRectsContext.rootLayer != this ? parent() : nullptr))
parentClipRects = parentLayer->updateClipRects(clipRectsContext);
auto clipRects = ClipRects::create();
calculateClipRects(clipRectsContext, clipRects);
if (parentClipRects && *parentClipRects == clipRects) {
m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, parentClipRects.copyRef());
return parentClipRects.releaseNonNull();
}
m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, clipRects.copyRef());
return clipRects;
}
ClipRects* RenderLayer::clipRects(const ClipRectsContext& context) const
{
ASSERT(context.clipRectsType < NumCachedClipRectsTypes);
if (!m_clipRectsCache)
return nullptr;
return m_clipRectsCache->getClipRects(context.clipRectsType, context.respectOverflowClip);
}
void RenderLayer::calculateClipRects(const ClipRectsContext& clipRectsContext, ClipRects& clipRects) const
{
if (!parent()) {
clipRects.reset();
return;
}
ClipRectsType clipRectsType = clipRectsContext.clipRectsType;
bool useCached = clipRectsType != TemporaryClipRects;
RenderLayer* parentLayer = clipRectsContext.rootLayer != this ? parent() : nullptr;
if (parentLayer) {
if (useCached && parentLayer->clipRects(clipRectsContext))
clipRects = *parentLayer->clipRects(clipRectsContext);
else {
ClipRectsContext parentContext(clipRectsContext);
parentContext.overlayScrollbarSizeRelevancy = IgnoreOverlayScrollbarSize; parentLayer->calculateClipRects(parentContext, clipRects);
}
} else
clipRects.reset();
if (renderer().isFixedPositioned()) {
clipRects.setPosClipRect(clipRects.fixedClipRect());
clipRects.setOverflowClipRect(clipRects.fixedClipRect());
clipRects.setFixed(true);
} else if (renderer().isInFlowPositioned())
clipRects.setPosClipRect(clipRects.overflowClipRect());
else if (renderer().isAbsolutelyPositioned())
clipRects.setOverflowClipRect(clipRects.posClipRect());
#if PLATFORM(IOS_FAMILY)
if (renderer().hasClipOrOverflowClip() && (clipRectsContext.respectOverflowClip == RespectOverflowClip || this != clipRectsContext.rootLayer)) {
#else
if ((renderer().hasOverflowClip() && (clipRectsContext.respectOverflowClip == RespectOverflowClip || this != clipRectsContext.rootLayer)) || renderer().hasClip()) {
#endif
LayoutPoint offset(renderer().localToContainerPoint(FloatPoint(), &clipRectsContext.rootLayer->renderer()));
if (clipRects.fixed() && &clipRectsContext.rootLayer->renderer() == &renderer().view())
offset -= toLayoutSize(renderer().view().frameView().scrollPositionForFixedPosition());
if (renderer().hasOverflowClip()) {
ClipRect newOverflowClip = downcast<RenderBox>(renderer()).overflowClipRectForChildLayers(offset, nullptr, clipRectsContext.overlayScrollbarSizeRelevancy);
newOverflowClip.setAffectedByRadius(renderer().style().hasBorderRadius());
clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect()));
if (renderer().isPositioned())
clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect()));
}
if (renderer().hasClip()) {
LayoutRect newPosClip = downcast<RenderBox>(renderer()).clipRect(offset, nullptr);
clipRects.setPosClipRect(intersection(newPosClip, clipRects.posClipRect()));
clipRects.setOverflowClipRect(intersection(newPosClip, clipRects.overflowClipRect()));
clipRects.setFixedClipRect(intersection(newPosClip, clipRects.fixedClipRect()));
}
}
}
Ref<ClipRects> RenderLayer::parentClipRects(const ClipRectsContext& clipRectsContext) const
{
ASSERT(parent());
if (clipRectsContext.clipRectsType == TemporaryClipRects) {
auto parentClipRects = ClipRects::create();
parent()->calculateClipRects(clipRectsContext, parentClipRects);
return parentClipRects;
}
return parent()->updateClipRects(clipRectsContext);
}
static inline ClipRect backgroundClipRectForPosition(const ClipRects& parentRects, PositionType position)
{
if (position == PositionType::Fixed)
return parentRects.fixedClipRect();
if (position == PositionType::Absolute)
return parentRects.posClipRect();
return parentRects.overflowClipRect();
}
ClipRect RenderLayer::backgroundClipRect(const ClipRectsContext& clipRectsContext) const
{
ASSERT(parent());
auto computeParentRects = [&] {
if (clipRectsContext.clipRectsType == TemporaryClipRects)
return parentClipRects(clipRectsContext);
bool crossesPaginationBoundary = parent()->enclosingPaginationLayer(IncludeCompositedPaginatedLayers) != enclosingPaginationLayer(IncludeCompositedPaginatedLayers);
bool crossesCompositingBoundary = parent()->enclosingCompositingLayerForRepaint() != enclosingCompositingLayerForRepaint();
if (!crossesPaginationBoundary && !crossesCompositingBoundary)
return parentClipRects(clipRectsContext);
ClipRectsContext tempContext(clipRectsContext);
tempContext.clipRectsType = TemporaryClipRects;
return parentClipRects(tempContext);
};
auto parentRects = computeParentRects();
ClipRect backgroundClipRect = backgroundClipRectForPosition(parentRects, renderer().style().position());
RenderView& view = renderer().view();
if (parentRects->fixed() && &clipRectsContext.rootLayer->renderer() == &view && !backgroundClipRect.isInfinite())
backgroundClipRect.moveBy(view.frameView().scrollPositionForFixedPosition());
return backgroundClipRect;
}
void RenderLayer::calculateRects(const ClipRectsContext& clipRectsContext, const LayoutRect& paintDirtyRect, LayoutRect& layerBounds,
ClipRect& backgroundRect, ClipRect& foregroundRect, const LayoutSize& offsetFromRoot) const
{
if (clipRectsContext.rootLayer != this && parent()) {
backgroundRect = backgroundClipRect(clipRectsContext);
backgroundRect.intersect(paintDirtyRect);
} else
backgroundRect = paintDirtyRect;
LayoutSize offsetFromRootLocal = offsetFromRoot;
if (clipRectsContext.rootLayer->isOutOfFlowRenderFragmentedFlow()) {
LayoutPoint absPos = LayoutPoint(renderer().view().localToAbsolute(FloatPoint(), IsFixed));
offsetFromRootLocal += toLayoutSize(absPos);
}
layerBounds = LayoutRect(toLayoutPoint(offsetFromRootLocal), size());
foregroundRect = backgroundRect;
if (renderer().hasClipOrOverflowClip()) {
if (renderer().hasOverflowClip() && (this != clipRectsContext.rootLayer || clipRectsContext.respectOverflowClip == RespectOverflowClip)) {
foregroundRect.intersect(downcast<RenderBox>(renderer()).overflowClipRect(toLayoutPoint(offsetFromRootLocal), nullptr, clipRectsContext.overlayScrollbarSizeRelevancy));
if (renderer().style().hasBorderRadius())
foregroundRect.setAffectedByRadius(true);
}
if (renderer().hasClip()) {
LayoutRect newPosClip = downcast<RenderBox>(renderer()).clipRect(toLayoutPoint(offsetFromRootLocal), nullptr);
backgroundRect.intersect(newPosClip);
foregroundRect.intersect(newPosClip);
}
if (renderBox()->hasVisualOverflow()) {
LayoutRect layerBoundsWithVisualOverflow = renderBox()->visualOverflowRect();
renderBox()->flipForWritingMode(layerBoundsWithVisualOverflow); layerBoundsWithVisualOverflow.move(offsetFromRootLocal);
if (this != clipRectsContext.rootLayer || clipRectsContext.respectOverflowClip == RespectOverflowClip)
backgroundRect.intersect(layerBoundsWithVisualOverflow);
} else {
LayoutRect bounds = renderBox()->borderBoxRectInFragment(nullptr);
bounds.move(offsetFromRootLocal);
if (this != clipRectsContext.rootLayer || clipRectsContext.respectOverflowClip == RespectOverflowClip)
backgroundRect.intersect(bounds);
}
}
}
LayoutRect RenderLayer::childrenClipRect() const
{
RenderLayer* clippingRootLayer = clippingRootForPainting();
LayoutRect layerBounds;
ClipRect backgroundRect;
ClipRect foregroundRect;
ClipRectsContext clipRectsContext(clippingRootLayer, TemporaryClipRects);
calculateRects(clipRectsContext, LayoutRect::infiniteRect(), layerBounds, backgroundRect, foregroundRect, offsetFromAncestor(clipRectsContext.rootLayer));
if (foregroundRect.rect().isInfinite())
return renderer().view().unscaledDocumentRect();
auto absoluteClippingRect = clippingRootLayer->renderer().localToAbsoluteQuad(FloatQuad(foregroundRect.rect())).enclosingBoundingBox();
return intersection(absoluteClippingRect, renderer().view().unscaledDocumentRect());
}
LayoutRect RenderLayer::clipRectRelativeToAncestor(RenderLayer* ancestor, LayoutSize offsetFromAncestor, const LayoutRect& constrainingRect) const
{
LayoutRect layerBounds;
ClipRect backgroundRect;
ClipRect foregroundRect;
auto clipRectType = !m_enclosingPaginationLayer || m_enclosingPaginationLayer == ancestor ? PaintingClipRects : TemporaryClipRects;
ClipRectsContext clipRectsContext(ancestor, clipRectType);
calculateRects(clipRectsContext, constrainingRect, layerBounds, backgroundRect, foregroundRect, offsetFromAncestor);
return backgroundRect.rect();
}
LayoutRect RenderLayer::selfClipRect() const
{
RenderLayer* clippingRootLayer = clippingRootForPainting();
LayoutRect clipRect = clipRectRelativeToAncestor(clippingRootLayer, offsetFromAncestor(clippingRootLayer), renderer().view().documentRect());
return clippingRootLayer->renderer().localToAbsoluteQuad(FloatQuad(clipRect)).enclosingBoundingBox();
}
LayoutRect RenderLayer::localClipRect(bool& clipExceedsBounds) const
{
clipExceedsBounds = false;
RenderLayer* clippingRootLayer = clippingRootForPainting();
LayoutSize offsetFromRoot = offsetFromAncestor(clippingRootLayer);
LayoutRect clipRect = clipRectRelativeToAncestor(clippingRootLayer, offsetFromRoot, LayoutRect::infiniteRect());
if (clipRect.isInfinite())
return clipRect;
if (renderer().hasClip()) {
LayoutRect cssClipRect = downcast<RenderBox>(renderer()).clipRect(toLayoutPoint(offsetFromRoot), nullptr);
clipExceedsBounds = !clipRect.contains(cssClipRect);
}
clipRect.move(-offsetFromRoot);
return clipRect;
}
void RenderLayer::addBlockSelectionGapsBounds(const LayoutRect& bounds)
{
m_blockSelectionGapsBounds.unite(enclosingIntRect(bounds));
}
void RenderLayer::clearBlockSelectionGapsBounds()
{
m_blockSelectionGapsBounds = IntRect();
for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
child->clearBlockSelectionGapsBounds();
}
void RenderLayer::repaintBlockSelectionGaps()
{
for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
child->repaintBlockSelectionGaps();
if (m_blockSelectionGapsBounds.isEmpty())
return;
LayoutRect rect = m_blockSelectionGapsBounds;
rect.moveBy(-scrollPosition());
if (renderer().hasOverflowClip() && !usesCompositedScrolling())
rect.intersect(downcast<RenderBox>(renderer()).overflowClipRect(LayoutPoint(), nullptr)); if (renderer().hasClip())
rect.intersect(downcast<RenderBox>(renderer()).clipRect(LayoutPoint(), nullptr)); if (!rect.isEmpty())
renderer().repaintRectangle(rect);
}
bool RenderLayer::intersectsDamageRect(const LayoutRect& layerBounds, const LayoutRect& damageRect, const RenderLayer* rootLayer, const LayoutSize& offsetFromRoot, const LayoutRect* cachedBoundingBox) const
{
if (isRenderViewLayer() || renderer().isDocumentElementRenderer())
return true;
if (damageRect.isInfinite())
return true;
if (damageRect.isEmpty())
return false;
if (!renderer().isRenderInline() && layerBounds.intersects(damageRect))
return true;
if (cachedBoundingBox)
return cachedBoundingBox->intersects(damageRect);
return boundingBox(rootLayer, offsetFromRoot).intersects(damageRect);
}
LayoutRect RenderLayer::localBoundingBox(OptionSet<CalculateLayerBoundsFlag> flags) const
{
LayoutRect result;
if (renderer().isInline() && is<RenderInline>(renderer()))
result = downcast<RenderInline>(renderer()).linesVisualOverflowBoundingBox();
else if (is<RenderTableRow>(renderer())) {
auto& tableRow = downcast<RenderTableRow>(renderer());
for (RenderTableCell* cell = tableRow.firstCell(); cell; cell = cell->nextCell()) {
LayoutRect bbox = cell->borderBoxRect();
result.unite(bbox);
LayoutRect overflowRect = tableRow.visualOverflowRect();
if (bbox != overflowRect)
result.unite(overflowRect);
}
} else {
RenderBox* box = renderBox();
ASSERT(box);
if (!(flags & DontConstrainForMask) && box->hasMask()) {
result = box->maskClipRect(LayoutPoint());
box->flipForWritingMode(result); } else {
LayoutRect bbox = box->borderBoxRect();
result = bbox;
LayoutRect overflowRect = box->visualOverflowRect();
if (bbox != overflowRect)
result.unite(overflowRect);
}
}
return result;
}
LayoutRect RenderLayer::boundingBox(const RenderLayer* ancestorLayer, const LayoutSize& offsetFromRoot, OptionSet<CalculateLayerBoundsFlag> flags) const
{
LayoutRect result = localBoundingBox(flags);
if (renderer().view().frameView().hasFlippedBlockRenderers()) {
if (renderer().isBox())
renderBox()->flipForWritingMode(result);
else
renderer().containingBlock()->flipForWritingMode(result);
}
PaginationInclusionMode inclusionMode = ExcludeCompositedPaginatedLayers;
if (flags & UseFragmentBoxesIncludingCompositing)
inclusionMode = IncludeCompositedPaginatedLayers;
const RenderLayer* paginationLayer = nullptr;
if (flags.containsAny({ UseFragmentBoxesExcludingCompositing, UseFragmentBoxesIncludingCompositing }))
paginationLayer = enclosingPaginationLayerInSubtree(ancestorLayer, inclusionMode);
const RenderLayer* childLayer = this;
bool isPaginated = paginationLayer;
while (paginationLayer) {
result.move(childLayer->offsetFromAncestor(paginationLayer));
auto& enclosingFragmentedFlow = downcast<RenderFragmentedFlow>(paginationLayer->renderer());
result = enclosingFragmentedFlow.fragmentsBoundingBox(result);
childLayer = paginationLayer;
paginationLayer = paginationLayer->parent()->enclosingPaginationLayerInSubtree(ancestorLayer, inclusionMode);
}
if (isPaginated) {
result.move(childLayer->offsetFromAncestor(ancestorLayer));
return result;
}
result.move(offsetFromRoot);
return result;
}
bool RenderLayer::getOverlapBoundsIncludingChildrenAccountingForTransformAnimations(LayoutRect& bounds, OptionSet<CalculateLayerBoundsFlag> additionalFlags) const
{
auto boundsFlags = additionalFlags | (defaultCalculateLayerBoundsFlags() - IncludeSelfTransform);
bounds = calculateLayerBounds(this, LayoutSize(), boundsFlags);
LayoutRect animatedBounds = bounds;
if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
if (auto* timeline = renderer().documentTimeline()) {
if (timeline->computeExtentOfAnimation(renderer(), animatedBounds)) {
bounds = animatedBounds;
return true;
}
}
} else {
if (renderer().animation().computeExtentOfAnimation(renderer(), animatedBounds)) {
bounds = animatedBounds;
return true;
}
}
return false;
}
IntRect RenderLayer::absoluteBoundingBox() const
{
const RenderLayer* rootLayer = root();
return snappedIntRect(boundingBox(rootLayer, offsetFromAncestor(rootLayer)));
}
FloatRect RenderLayer::absoluteBoundingBoxForPainting() const
{
const RenderLayer* rootLayer = root();
return snapRectToDevicePixels(boundingBox(rootLayer, offsetFromAncestor(rootLayer)), renderer().document().deviceScaleFactor());
}
LayoutRect RenderLayer::overlapBounds() const
{
if (overlapBoundsIncludeChildren())
return calculateLayerBounds(this, { }, defaultCalculateLayerBoundsFlags() | IncludeFilterOutsets);
return localBoundingBox();
}
LayoutRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, const LayoutSize& offsetFromRoot, OptionSet<CalculateLayerBoundsFlag> flags) const
{
if (!isSelfPaintingLayer())
return LayoutRect();
if ((flags & ExcludeHiddenDescendants) && this != ancestorLayer && !hasVisibleContent() && !hasVisibleDescendant())
return LayoutRect();
if (isRenderViewLayer()) {
return renderer().view().unscaledDocumentRect();
}
LayoutRect boundingBoxRect = localBoundingBox(flags);
if (renderer().view().frameView().hasFlippedBlockRenderers()) {
if (is<RenderBox>(renderer()))
downcast<RenderBox>(renderer()).flipForWritingMode(boundingBoxRect);
else
renderer().containingBlock()->flipForWritingMode(boundingBoxRect);
}
if (renderer().isDocumentElementRenderer()) {
const FrameView& frameView = renderer().view().frameView();
boundingBoxRect.setWidth(std::max(boundingBoxRect.width(), frameView.contentsWidth() - boundingBoxRect.x()));
boundingBoxRect.setHeight(std::max(boundingBoxRect.height(), frameView.contentsHeight() - boundingBoxRect.y()));
}
LayoutRect unionBounds = boundingBoxRect;
if (flags & UseLocalClipRectIfPossible) {
bool clipExceedsBounds = false;
LayoutRect localClipRect = this->localClipRect(clipExceedsBounds);
if (!localClipRect.isInfinite() && !clipExceedsBounds) {
if ((flags & IncludeSelfTransform) && paintsWithTransform(PaintBehavior::Normal))
localClipRect = transform()->mapRect(localClipRect);
localClipRect.move(offsetFromAncestor(ancestorLayer));
return localClipRect;
}
}
auto descendantFlags = defaultCalculateLayerBoundsFlags() | (flags & ExcludeHiddenDescendants) | (flags & IncludeCompositedDescendants);
const_cast<RenderLayer*>(this)->updateLayerListsIfNeeded();
if (RenderLayer* reflection = reflectionLayer()) {
if (!reflection->isComposited()) {
LayoutRect childUnionBounds = reflection->calculateLayerBounds(this, reflection->offsetFromAncestor(this), descendantFlags);
unionBounds.unite(childUnionBounds);
}
}
ASSERT(isStackingContext() || !positiveZOrderLayers().size());
#if !ASSERT_DISABLED
LayerListMutationDetector mutationChecker(const_cast<RenderLayer&>(*this));
#endif
auto computeLayersUnion = [this, &unionBounds, flags, descendantFlags] (const RenderLayer& childLayer) {
if (!(flags & IncludeCompositedDescendants) && childLayer.isComposited())
return;
LayoutRect childBounds = childLayer.calculateLayerBounds(this, childLayer.offsetFromAncestor(this), descendantFlags);
unionBounds.checkedUnite(childBounds);
};
for (auto* childLayer : negativeZOrderLayers())
computeLayersUnion(*childLayer);
for (auto* childLayer : positiveZOrderLayers())
computeLayersUnion(*childLayer);
for (auto* childLayer : normalFlowLayers())
computeLayersUnion(*childLayer);
if (flags.contains(IncludeFilterOutsets) || (flags.contains(IncludePaintedFilterOutsets) && paintsWithFilters()))
renderer().style().filterOutsets().expandRect(unionBounds);
if ((flags & IncludeSelfTransform) && paintsWithTransform(PaintBehavior::Normal)) {
TransformationMatrix* affineTrans = transform();
boundingBoxRect = affineTrans->mapRect(boundingBoxRect);
unionBounds = affineTrans->mapRect(unionBounds);
}
unionBounds.move(offsetFromRoot);
return unionBounds;
}
void RenderLayer::clearClipRectsIncludingDescendants(ClipRectsType typeToClear)
{
if (!m_clipRectsCache)
return;
clearClipRects(typeToClear);
for (RenderLayer* l = firstChild(); l; l = l->nextSibling())
l->clearClipRectsIncludingDescendants(typeToClear);
}
void RenderLayer::clearClipRects(ClipRectsType typeToClear)
{
if (typeToClear == AllClipRectTypes)
m_clipRectsCache = nullptr;
else {
ASSERT(typeToClear < NumCachedClipRectsTypes);
m_clipRectsCache->setClipRects(typeToClear, RespectOverflowClip, nullptr);
m_clipRectsCache->setClipRects(typeToClear, IgnoreOverflowClip, nullptr);
}
}
RenderLayerBacking* RenderLayer::ensureBacking()
{
if (!m_backing) {
m_backing = std::make_unique<RenderLayerBacking>(*this);
compositor().layerBecameComposited(*this);
updateFilterPaintingStrategy();
}
return m_backing.get();
}
void RenderLayer::clearBacking(bool layerBeingDestroyed)
{
if (m_backing && !renderer().renderTreeBeingDestroyed())
compositor().layerBecameNonComposited(*this);
m_backing = nullptr;
if (!layerBeingDestroyed)
updateFilterPaintingStrategy();
}
bool RenderLayer::hasCompositedMask() const
{
return m_backing && m_backing->hasMaskLayer();
}
GraphicsLayer* RenderLayer::layerForScrolling() const
{
return m_backing ? m_backing->scrollingContentsLayer() : nullptr;
}
GraphicsLayer* RenderLayer::layerForHorizontalScrollbar() const
{
return m_backing ? m_backing->layerForHorizontalScrollbar() : nullptr;
}
GraphicsLayer* RenderLayer::layerForVerticalScrollbar() const
{
return m_backing ? m_backing->layerForVerticalScrollbar() : nullptr;
}
GraphicsLayer* RenderLayer::layerForScrollCorner() const
{
return m_backing ? m_backing->layerForScrollCorner() : nullptr;
}
bool RenderLayer::paintsWithTransform(OptionSet<PaintBehavior> paintBehavior) const
{
bool paintsToWindow = !isComposited() || backing()->paintsIntoWindow();
return transform() && ((paintBehavior & PaintBehavior::FlattenCompositingLayers) || paintsToWindow);
}
bool RenderLayer::shouldPaintMask(OptionSet<PaintBehavior> paintBehavior, OptionSet<PaintLayerFlag> paintFlags) const
{
if (!renderer().hasMask())
return false;
bool paintsToWindow = !isComposited() || backing()->paintsIntoWindow();
if (paintsToWindow || (paintBehavior & PaintBehavior::FlattenCompositingLayers))
return true;
return paintFlags.contains(PaintLayerPaintingCompositingMaskPhase);
}
bool RenderLayer::shouldApplyClipPath(OptionSet<PaintBehavior> paintBehavior, OptionSet<PaintLayerFlag> paintFlags) const
{
if (!renderer().hasClipPath())
return false;
bool paintsToWindow = !isComposited() || backing()->paintsIntoWindow();
if (paintsToWindow || (paintBehavior & PaintBehavior::FlattenCompositingLayers))
return true;
return paintFlags.contains(PaintLayerPaintingCompositingClipPathPhase);
}
bool RenderLayer::scrollingMayRevealBackground() const
{
return scrollsOverflow() || usesCompositedScrolling();
}
bool RenderLayer::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) const
{
if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant())
return false;
if (paintsWithTransparency(PaintBehavior::Normal))
return false;
if (renderer().isDocumentElementRenderer()) {
return false;
}
if (renderer().style().visibility() != Visibility::Visible)
return false;
if (paintsWithFilters() && renderer().style().filter().hasFilterThatAffectsOpacity())
return false;
if (paintsWithTransform(PaintBehavior::Normal))
return false;
if (zOrderListsDirty() || normalFlowListDirty())
return false;
if (renderer().isTablePart())
return false;
if (renderer().backgroundIsKnownToBeOpaqueInRect(localRect))
return true;
if (renderer().hasOverflowClip())
return false;
return listBackgroundIsKnownToBeOpaqueInRect(positiveZOrderLayers(), localRect)
|| listBackgroundIsKnownToBeOpaqueInRect(negativeZOrderLayers(), localRect)
|| listBackgroundIsKnownToBeOpaqueInRect(normalFlowLayers(), localRect);
}
bool RenderLayer::listBackgroundIsKnownToBeOpaqueInRect(const LayerList& list, const LayoutRect& localRect) const
{
if (list.begin() == list.end())
return false;
for (auto iter = list.rbegin(); iter != list.rend(); ++iter) {
const auto* childLayer = *iter;
if (childLayer->isComposited())
continue;
if (!childLayer->canUseConvertToLayerCoords())
continue;
LayoutRect childLocalRect(localRect);
childLocalRect.move(-childLayer->offsetFromAncestor(this));
if (childLayer->backgroundIsKnownToBeOpaqueInRect(childLocalRect))
return true;
}
return false;
}
void RenderLayer::repaintIncludingDescendants()
{
renderer().repaint();
for (RenderLayer* current = firstChild(); current; current = current->nextSibling())
current->repaintIncludingDescendants();
}
void RenderLayer::setBackingNeedsRepaint(GraphicsLayer::ShouldClipToLayer shouldClip)
{
ASSERT(isComposited());
if (backing()->paintsIntoWindow()) {
renderer().view().repaintViewRectangle(absoluteBoundingBox());
} else
backing()->setContentsNeedDisplay(shouldClip);
}
void RenderLayer::setBackingNeedsRepaintInRect(const LayoutRect& r, GraphicsLayer::ShouldClipToLayer shouldClip)
{
ASSERT(isComposited());
if (!isComposited() || backing()->paintsIntoWindow()) {
LayoutRect absRect(r);
absRect.move(offsetFromAncestor(root()));
renderer().view().repaintViewRectangle(absRect);
} else
backing()->setContentsNeedDisplayInRect(r, shouldClip);
}
void RenderLayer::repaintIncludingNonCompositingDescendants(RenderLayerModelObject* repaintContainer)
{
renderer().repaintUsingContainer(repaintContainer, renderer().clippedOverflowRectForRepaint(repaintContainer));
for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling()) {
if (!curr->isComposited())
curr->repaintIncludingNonCompositingDescendants(repaintContainer);
}
}
bool RenderLayer::shouldBeSelfPaintingLayer() const
{
if (!isNormalFlowOnly())
return true;
return hasOverlayScrollbars()
|| canUseCompositedScrolling()
|| renderer().isTableRow()
|| renderer().isCanvas()
|| renderer().isVideo()
|| renderer().isEmbeddedObject()
|| (renderer().isRenderImage() && downcast<RenderImage>(renderer()).isEditableImage())
|| renderer().isRenderIFrame()
|| renderer().isInFlowRenderFragmentedFlow();
}
void RenderLayer::updateSelfPaintingLayer()
{
bool isSelfPaintingLayer = shouldBeSelfPaintingLayer();
if (m_isSelfPaintingLayer == isSelfPaintingLayer)
return;
m_isSelfPaintingLayer = isSelfPaintingLayer;
if (!parent())
return;
if (isSelfPaintingLayer)
parent()->setAncestorChainHasSelfPaintingLayerDescendant();
else
parent()->dirtyAncestorChainHasSelfPaintingLayerDescendantStatus();
}
static bool hasVisibleBoxDecorationsOrBackground(const RenderElement& renderer)
{
return renderer.hasVisibleBoxDecorations() || renderer.style().hasOutline();
}
static bool styleHasSmoothingTextMode(const RenderStyle& style)
{
FontSmoothingMode smoothingMode = style.fontDescription().fontSmoothing();
return smoothingMode == FontSmoothingMode::AutoSmoothing || smoothingMode == FontSmoothingMode::SubpixelAntialiased;
}
static const unsigned maxRendererTraversalCount = 200;
static void determineNonLayerDescendantsPaintedContent(const RenderElement& renderer, unsigned& renderersTraversed, RenderLayer::PaintedContentRequest& request)
{
for (const auto& child : childrenOfType<RenderObject>(renderer)) {
if (++renderersTraversed > maxRendererTraversalCount) {
request.makeStatesUndetermined();
return;
}
if (is<RenderText>(child)) {
const auto& renderText = downcast<RenderText>(child);
if (renderText.linesBoundingBox().isEmpty())
continue;
if (renderer.style().userSelect() != UserSelect::None)
request.setHasPaintedContent();
if (!renderText.text().isAllSpecialCharacters<isHTMLSpace>()) {
request.setHasPaintedContent();
if (request.needToDetermineSubpixelAntialiasedTextState() && styleHasSmoothingTextMode(child.style()))
request.setHasSubpixelAntialiasedText();
}
if (request.isSatisfied())
return;
}
if (!is<RenderElement>(child))
continue;
const RenderElement& renderElementChild = downcast<RenderElement>(child);
if (is<RenderLayerModelObject>(renderElementChild) && downcast<RenderLayerModelObject>(renderElementChild).hasSelfPaintingLayer())
continue;
if (hasVisibleBoxDecorationsOrBackground(renderElementChild)) {
request.setHasPaintedContent();
if (request.isSatisfied())
return;
}
if (is<RenderReplaced>(renderElementChild)) {
request.setHasPaintedContent();
if (is<RenderImage>(renderElementChild) && request.needToDetermineSubpixelAntialiasedTextState()) {
auto& imageRenderer = downcast<RenderImage>(renderElementChild);
if ((imageRenderer.isShowingAltText() || imageRenderer.hasNonBitmapImage()) && styleHasSmoothingTextMode(child.style()))
request.setHasSubpixelAntialiasedText();
}
if (request.isSatisfied())
return;
}
determineNonLayerDescendantsPaintedContent(renderElementChild, renderersTraversed, request);
if (request.isSatisfied())
return;
}
}
bool RenderLayer::hasNonEmptyChildRenderers(PaintedContentRequest& request) const
{
unsigned renderersTraversed = 0;
determineNonLayerDescendantsPaintedContent(renderer(), renderersTraversed, request);
return request.probablyHasPaintedContent();
}
bool RenderLayer::hasVisibleBoxDecorationsOrBackground() const
{
return WebCore::hasVisibleBoxDecorationsOrBackground(renderer());
}
bool RenderLayer::hasVisibleBoxDecorations() const
{
if (!hasVisibleContent())
return false;
return hasVisibleBoxDecorationsOrBackground() || hasOverflowControls();
}
bool RenderLayer::isVisuallyNonEmpty(PaintedContentRequest* request) const
{
ASSERT(!m_visibleDescendantStatusDirty);
if (!hasVisibleContent() || !renderer().style().opacity())
return false;
if (renderer().isRenderReplaced() || hasOverflowControls()) {
if (!request)
return true;
request->setHasPaintedContent();
if (request->isSatisfied())
return true;
}
if (hasVisibleBoxDecorationsOrBackground()) {
if (!request)
return true;
request->setHasPaintedContent();
if (request->isSatisfied())
return true;
}
PaintedContentRequest localRequest;
if (!request)
request = &localRequest;
return hasNonEmptyChildRenderers(*request);
}
void RenderLayer::updateScrollbarsAfterStyleChange(const RenderStyle* oldStyle)
{
RenderBox* box = renderBox();
if (!box)
return;
if (box->style().appearance() == ListboxPart)
return;
Overflow overflowX = box->style().overflowX();
Overflow overflowY = box->style().overflowY();
bool needsHorizontalScrollbar = box->hasOverflowClip() && ((hasHorizontalScrollbar() && styleDefinesAutomaticScrollbar(box->style(), HorizontalScrollbar)) || styleRequiresScrollbar(box->style(), HorizontalScrollbar));
bool needsVerticalScrollbar = box->hasOverflowClip() && ((hasVerticalScrollbar() && styleDefinesAutomaticScrollbar(box->style(), VerticalScrollbar)) || styleRequiresScrollbar(box->style(), VerticalScrollbar));
setHasHorizontalScrollbar(needsHorizontalScrollbar);
setHasVerticalScrollbar(needsVerticalScrollbar);
if (m_hBar && needsHorizontalScrollbar && oldStyle && oldStyle->overflowX() == Overflow::Scroll && overflowX != Overflow::Scroll)
m_hBar->setEnabled(true);
if (m_vBar && needsVerticalScrollbar && oldStyle && oldStyle->overflowY() == Overflow::Scroll && overflowY != Overflow::Scroll)
m_vBar->setEnabled(true);
if (!m_scrollDimensionsDirty)
updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
}
void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle* oldStyle)
{
setIsNormalFlowOnly(shouldBeNormalFlowOnly());
if (setIsStackingContext(shouldBeStackingContext())) {
#if ENABLE(CSS_COMPOSITING)
if (parent()) {
if (isStackingContext()) {
if (!hasNotIsolatedBlendingDescendantsStatusDirty() && hasNotIsolatedBlendingDescendants())
parent()->dirtyAncestorChainHasBlendingDescendants();
} else {
if (hasNotIsolatedBlendingDescendantsStatusDirty())
parent()->dirtyAncestorChainHasBlendingDescendants();
else if (hasNotIsolatedBlendingDescendants())
parent()->updateAncestorChainHasBlendingDescendants();
}
}
#endif
}
if (oldStyle) {
if (oldStyle->zIndex() != renderer().style().zIndex() || oldStyle->visibility() != renderer().style().visibility()) {
dirtyStackingContextZOrderLists();
if (isStackingContext())
dirtyZOrderLists();
}
}
if (renderer().isHTMLMarquee() && renderer().style().marqueeBehavior() != MarqueeBehavior::None && renderer().isBox()) {
if (!m_marquee)
m_marquee = std::make_unique<RenderMarquee>(this);
m_marquee->updateMarqueeStyle();
} else if (m_marquee)
m_marquee = nullptr;
updateScrollbarsAfterStyleChange(oldStyle);
updateSelfPaintingLayer();
if (!hasReflection() && m_reflection)
removeReflection();
else if (hasReflection()) {
if (!m_reflection)
createReflection();
else
m_reflection->setStyle(createReflectionStyle());
}
if (m_hBar)
m_hBar->styleChanged();
if (m_vBar)
m_vBar->styleChanged();
updateScrollCornerStyle();
updateResizerStyle();
updateDescendantDependentFlags();
updateTransform();
#if ENABLE(CSS_COMPOSITING)
updateBlendMode();
#endif
updateFiltersAfterStyleChange();
compositor().layerStyleChanged(diff, *this, oldStyle);
updateFilterPaintingStrategy();
#if PLATFORM(IOS_FAMILY) && ENABLE(TOUCH_EVENTS)
if (diff == StyleDifference::RecompositeLayer || diff >= StyleDifference::LayoutPositionedMovementOnly)
renderer().document().setTouchEventRegionsNeedUpdate();
#else
UNUSED_PARAM(diff);
#endif
}
void RenderLayer::updateScrollableAreaSet(bool hasOverflow)
{
FrameView& frameView = renderer().view().frameView();
bool isVisibleToHitTest = renderer().visibleToHitTesting();
if (HTMLFrameOwnerElement* owner = frameView.frame().ownerElement())
isVisibleToHitTest &= owner->renderer() && owner->renderer()->visibleToHitTesting();
bool isScrollable = hasOverflow && isVisibleToHitTest;
bool addedOrRemoved = false;
ASSERT(m_registeredScrollableArea == frameView.containsScrollableArea(this));
if (isScrollable) {
if (!m_registeredScrollableArea) {
addedOrRemoved = frameView.addScrollableArea(this);
m_registeredScrollableArea = true;
}
} else if (m_registeredScrollableArea) {
addedOrRemoved = frameView.removeScrollableArea(this);
m_registeredScrollableArea = false;
}
#if ENABLE(IOS_TOUCH_EVENTS)
if (addedOrRemoved) {
if (isScrollable && !canUseCompositedScrolling())
registerAsTouchEventListenerForScrolling();
else {
unregisterAsTouchEventListenerForScrolling();
}
}
#else
UNUSED_VARIABLE(addedOrRemoved);
#endif
}
void RenderLayer::updateScrollCornerStyle()
{
RenderElement* actualRenderer = rendererForScrollbar(renderer());
auto corner = renderer().hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(PseudoStyleRequest(PseudoId::ScrollbarCorner), &actualRenderer->style()) : nullptr;
if (!corner) {
clearScrollCorner();
return;
}
if (!m_scrollCorner) {
m_scrollCorner = createRenderer<RenderScrollbarPart>(renderer().document(), WTFMove(*corner));
m_scrollCorner->setParent(&renderer());
m_scrollCorner->initializeStyle();
} else
m_scrollCorner->setStyle(WTFMove(*corner));
}
void RenderLayer::clearScrollCorner()
{
if (!m_scrollCorner)
return;
m_scrollCorner->setParent(nullptr);
m_scrollCorner = nullptr;
}
void RenderLayer::updateResizerStyle()
{
RenderElement* actualRenderer = rendererForScrollbar(renderer());
auto resizer = renderer().hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(PseudoStyleRequest(PseudoId::Resizer), &actualRenderer->style()) : nullptr;
if (!resizer) {
clearResizer();
return;
}
if (!m_resizer) {
m_resizer = createRenderer<RenderScrollbarPart>(renderer().document(), WTFMove(*resizer));
m_resizer->setParent(&renderer());
m_resizer->initializeStyle();
} else
m_resizer->setStyle(WTFMove(*resizer));
}
void RenderLayer::clearResizer()
{
if (!m_resizer)
return;
m_resizer->setParent(nullptr);
m_resizer = nullptr;
}
RenderLayer* RenderLayer::reflectionLayer() const
{
return m_reflection ? m_reflection->layer() : nullptr;
}
bool RenderLayer::isReflectionLayer(const RenderLayer& layer) const
{
return m_reflection ? &layer == m_reflection->layer() : false;
}
void RenderLayer::createReflection()
{
ASSERT(!m_reflection);
m_reflection = createRenderer<RenderReplica>(renderer().document(), createReflectionStyle());
m_reflection->setParent(&renderer()); m_reflection->initializeStyle();
}
void RenderLayer::removeReflection()
{
if (!m_reflection->renderTreeBeingDestroyed())
m_reflection->removeLayers(this);
m_reflection->setParent(nullptr);
m_reflection = nullptr;
}
RenderStyle RenderLayer::createReflectionStyle()
{
auto newStyle = RenderStyle::create();
newStyle.inheritFrom(renderer().style());
TransformOperations transform;
switch (renderer().style().boxReflect()->direction()) {
case ReflectionBelow:
transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), Length(100., Percent), TransformOperation::TRANSLATE));
transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), renderer().style().boxReflect()->offset(), TransformOperation::TRANSLATE));
transform.operations().append(ScaleTransformOperation::create(1.0, -1.0, ScaleTransformOperation::SCALE));
break;
case ReflectionAbove:
transform.operations().append(ScaleTransformOperation::create(1.0, -1.0, ScaleTransformOperation::SCALE));
transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), Length(100., Percent), TransformOperation::TRANSLATE));
transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), renderer().style().boxReflect()->offset(), TransformOperation::TRANSLATE));
break;
case ReflectionRight:
transform.operations().append(TranslateTransformOperation::create(Length(100., Percent), Length(0, Fixed), TransformOperation::TRANSLATE));
transform.operations().append(TranslateTransformOperation::create(renderer().style().boxReflect()->offset(), Length(0, Fixed), TransformOperation::TRANSLATE));
transform.operations().append(ScaleTransformOperation::create(-1.0, 1.0, ScaleTransformOperation::SCALE));
break;
case ReflectionLeft:
transform.operations().append(ScaleTransformOperation::create(-1.0, 1.0, ScaleTransformOperation::SCALE));
transform.operations().append(TranslateTransformOperation::create(Length(100., Percent), Length(0, Fixed), TransformOperation::TRANSLATE));
transform.operations().append(TranslateTransformOperation::create(renderer().style().boxReflect()->offset(), Length(0, Fixed), TransformOperation::TRANSLATE));
break;
}
newStyle.setTransform(transform);
newStyle.setMaskBoxImage(renderer().style().boxReflect()->mask());
newStyle.setZIndex(0);
return newStyle;
}
void RenderLayer::ensureLayerFilters()
{
if (m_filters)
return;
m_filters = std::make_unique<RenderLayerFilters>(*this);
}
void RenderLayer::clearLayerFilters()
{
m_filters = nullptr;
}
void RenderLayer::updateFiltersAfterStyleChange()
{
if (!hasFilter()) {
clearLayerFilters();
return;
}
if (renderer().style().filter().hasReferenceFilter() && !renderer().isSVGRoot()) {
ensureLayerFilters();
m_filters->updateReferenceFilterClients(renderer().style().filter());
} else if (m_filters)
m_filters->removeReferenceFilterClients();
}
void RenderLayer::updateFilterPaintingStrategy()
{
if (!paintsWithFilters()) {
if (m_filters)
m_filters->setFilter(nullptr);
if (!renderer().style().filter().hasReferenceFilter())
return;
}
ensureLayerFilters();
m_filters->buildFilter(renderer(), page().deviceScaleFactor(), renderer().settings().acceleratedFiltersEnabled() ? Accelerated : Unaccelerated);
}
void RenderLayer::filterNeedsRepaint()
{
if (Element* element = enclosingElement()) {
element->invalidateStyleAndLayerComposition();
}
renderer().repaint();
}
TextStream& operator<<(TextStream& ts, const RenderLayer& layer)
{
ts << "RenderLayer " << &layer << " " << layer.size();
if (layer.transform())
ts << " has transform";
if (layer.hasFilter())
ts << " has filter";
if (layer.hasBackdropFilter())
ts << " has backdrop filter";
if (layer.hasBlendMode())
ts << " has blend mode";
if (layer.isolatesBlending())
ts << " isolates blending";
if (layer.isComposited())
ts << " " << *layer.backing();
return ts;
}
}
#if ENABLE(TREE_DEBUGGING)
void showLayerTree(const WebCore::RenderLayer* layer)
{
if (!layer)
return;
WTF::String output = externalRepresentation(&layer->renderer().frame(), WebCore::RenderAsTextShowAllLayers | WebCore::RenderAsTextShowLayerNesting | WebCore::RenderAsTextShowCompositedLayers | WebCore::RenderAsTextShowAddresses | WebCore::RenderAsTextShowIDAndClass | WebCore::RenderAsTextDontUpdateLayout | WebCore::RenderAsTextShowLayoutState | WebCore::RenderAsTextShowOverflow | WebCore::RenderAsTextShowSVGGeometry | WebCore::RenderAsTextShowLayerFragments);
fprintf(stderr, "\n%s\n", output.utf8().data());
}
void showLayerTree(const WebCore::RenderObject* renderer)
{
if (!renderer)
return;
showLayerTree(renderer->enclosingLayer());
}
static void outputPaintOrderTreeLegend(TextStream& stream)
{
stream.nextLine();
stream << "(S)tacking Context, (N)ormal flow only, (O)verflow clip, (A)lpha (opacity or mask), has (B)lend mode, (I)solates blending, (T)ransform-ish, (F)ilter, Fi(X)ed position, (C)omposited, (c)omposited descendant\n"
"Dirty (z)-lists, Dirty (n)ormal flow lists\n"
"Descendant needs overlap (t)raversal, Descendant needs (b)acking or hierarchy update, All descendants need (r)equirements traversal, All (s)ubsequent layers need requirements traversal, All descendants need (h)ierarchy traversal\n"
"Needs compositing paint order update on (s)ubsequent layers, Needs compositing paint (o)rder children update, "
"Needs post-(l)ayout update, Needs compositing (g)eometry update, (k)ids need geometry update, Needs compositing (c)onfig update, Needs compositing layer conne(x)ion update";
stream.nextLine();
}
static void outputIdent(TextStream& stream, unsigned depth)
{
unsigned i = 0;
while (++i <= depth * 2)
stream << " ";
}
static void outputPaintOrderTreeRecursive(TextStream& stream, const WebCore::RenderLayer& layer, const char* prefix, unsigned depth = 0)
{
stream << (layer.isStackingContext() ? "S" : "-");
stream << (layer.isNormalFlowOnly() ? "N" : "-");
stream << (layer.renderer().hasOverflowClip() ? "O" : "-");
stream << (layer.isTransparent() ? "A" : "-");
stream << (layer.hasBlendMode() ? "B" : "-");
stream << (layer.isolatesBlending() ? "I" : "-");
stream << (layer.renderer().hasTransformRelatedProperty() ? "T" : "-");
stream << (layer.hasFilter() ? "F" : "-");
stream << (layer.renderer().isFixedPositioned() ? "X" : "-");
stream << (layer.isComposited() ? "C" : "-");
stream << (layer.hasCompositingDescendant() ? "c" : "-");
stream << " ";
stream << (layer.zOrderListsDirty() ? "z" : "-");
stream << (layer.normalFlowListDirty() ? "n" : "-");
stream << " ";
stream << (layer.hasDescendantNeedingCompositingRequirementsTraversal() ? "t" : "-");
stream << (layer.hasDescendantNeedingUpdateBackingOrHierarchyTraversal() ? "b" : "-");
stream << (layer.descendantsNeedCompositingRequirementsTraversal() ? "r" : "-");
stream << (layer.subsequentLayersNeedCompositingRequirementsTraversal() ? "s" : "-");
stream << (layer.descendantsNeedUpdateBackingAndHierarchyTraversal() ? "h" : "-");
stream << " ";
stream << (layer.needsCompositingPaintOrderChildrenUpdate() ? "o" : "-");
stream << (layer.needsPostLayoutCompositingUpdate() ? "l" : "-");
stream << (layer.needsCompositingGeometryUpdate() ? "g" : "-");
stream << (layer.childrenNeedCompositingGeometryUpdate() ? "k" : "-");
stream << (layer.needsCompositingConfigurationUpdate() ? "c" : "-");
stream << (layer.needsCompositingLayerConnection() ? "x" : "-");
stream << " ";
outputIdent(stream, depth);
stream << prefix;
auto layerRect = layer.rect();
stream << &layer << " " << layerRect;
if (layer.isComposited())
stream << " (layerID " << layer.backing()->graphicsLayer()->primaryLayerID() << ")";
stream << " " << layer.name();
stream.nextLine();
const_cast<WebCore::RenderLayer&>(layer).updateLayerListsIfNeeded();
for (auto* child : layer.negativeZOrderLayers())
outputPaintOrderTreeRecursive(stream, *child, "- ", depth + 1);
for (auto* child : layer.normalFlowLayers())
outputPaintOrderTreeRecursive(stream, *child, "n ", depth + 1);
for (auto* child : layer.positiveZOrderLayers())
outputPaintOrderTreeRecursive(stream, *child, "+ ", depth + 1);
}
void showPaintOrderTree(const WebCore::RenderLayer* layer)
{
TextStream stream;
outputPaintOrderTreeLegend(stream);
if (layer)
outputPaintOrderTreeRecursive(stream, *layer, "");
WTFLogAlways("%s", stream.release().utf8().data());
}
#endif