Combine event and touch action regions into a single class
[WebKit-https.git] / Source / WebCore / rendering / RenderLayer.cpp
index d0df365..b2eb23e 100644 (file)
@@ -273,6 +273,7 @@ RenderLayer::RenderLayer(RenderLayerModelObject& rendererLayerModelObject)
     , m_forcedStackingContext(rendererLayerModelObject.isMedia())
     , m_zOrderListsDirty(false)
     , m_normalFlowListDirty(true)
+    , m_hadNegativeZOrderList(false)
     , m_inResizeMode(false)
     , m_scrollDimensionsDirty(true)
     , m_hasSelfPaintingLayerDescendant(false)
@@ -286,6 +287,7 @@ RenderLayer::RenderLayer(RenderLayerModelObject& rendererLayerModelObject)
     , m_visibleDescendantStatusDirty(false)
     , m_hasVisibleDescendant(false)
     , m_registeredScrollableArea(false)
+    , m_isFixedIntersectingViewport(false)
     , m_3DTransformedDescendantStatusDirty(true)
     , m_has3DTransformedDescendant(false)
     , m_hasCompositingDescendant(false)
@@ -293,16 +295,13 @@ RenderLayer::RenderLayer(RenderLayerModelObject& rendererLayerModelObject)
     , m_has3DTransformedAncestor(false)
     , m_indirectCompositingReason(static_cast<unsigned>(IndirectCompositingReason::None))
     , m_viewportConstrainedNotCompositedReason(NoNotCompositedReason)
-#if PLATFORM(IOS)
-    , m_adjustForIOSCaretWhenScrolling(false)
-#endif
-#if PLATFORM(IOS)
+#if PLATFORM(IOS_FAMILY)
 #if ENABLE(IOS_TOUCH_EVENTS)
     , m_registeredAsTouchEventListenerForScrolling(false)
 #endif
-    , m_inUserScroll(false)
-    , m_requiresScrollBoundsOriginUpdate(false)
+    , m_adjustForIOSCaretWhenScrolling(false)
 #endif
+    , m_requiresScrollPositionReconciliation(false)
     , m_containsDirtyOverlayScrollbars(false)
     , m_updatingMarqueePosition(false)
 #if !ASSERT_DISABLED
@@ -371,8 +370,8 @@ RenderLayer::~RenderLayer()
     clearBacking(true);
 
     // Layer and all its children should be removed from the tree before destruction.
-    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(renderer().renderTreeBeingDestroyed() || !m_parent);
-    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(renderer().renderTreeBeingDestroyed() || !m_first);
+    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(renderer().renderTreeBeingDestroyed() || !parent());
+    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(renderer().renderTreeBeingDestroyed() || !firstChild());
 }
 
 void RenderLayer::addChild(RenderLayer& child, RenderLayer* beforeChild)
@@ -394,15 +393,7 @@ void RenderLayer::addChild(RenderLayer& child, RenderLayer* beforeChild)
 
     child.setParent(this);
 
-    if (child.isNormalFlowOnly())
-        dirtyNormalFlowList();
-
-    if (!child.isNormalFlowOnly() || child.firstChild()) {
-        // Dirty the z-order list in which we are contained. The stackingContext() can be null in the
-        // case where we're building up generated content layers. This is ok, since the lists will start
-        // off dirty in that case anyway.
-        child.dirtyStackingContextZOrderLists();
-    }
+    dirtyPaintOrderListsOnChildChange(child);
 
     child.updateDescendantDependentFlags();
     if (child.m_hasVisibleContent || child.m_hasVisibleDescendant)
@@ -411,9 +402,18 @@ void RenderLayer::addChild(RenderLayer& child, RenderLayer* beforeChild)
     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();
+        updateAncestorChainHasBlendingDescendants(); // Why not just dirty?
 #endif
 
     compositor().layerWasAdded(*this, child);
@@ -435,14 +435,7 @@ void RenderLayer::removeChild(RenderLayer& oldChild)
     if (m_last == &oldChild)
         m_last = oldChild.previousSibling();
 
-    if (oldChild.isNormalFlowOnly())
-        dirtyNormalFlowList();
-    if (!oldChild.isNormalFlowOnly() || oldChild.firstChild()) {
-        // Dirty the z-order list in which we are contained. When called via the
-        // reattachment process in removeOnlyThisLayer, the layer may already be disconnected
-        // from the main layer tree, so we need to null-check the |stackingContext| value.
-        oldChild.dirtyStackingContextZOrderLists();
-    }
+    dirtyPaintOrderListsOnChildChange(oldChild);
 
     oldChild.setPreviousSibling(nullptr);
     oldChild.setNextSibling(nullptr);
@@ -455,12 +448,28 @@ void RenderLayer::removeChild(RenderLayer& oldChild)
     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()) {
+        // Dirty the z-order list in which we are contained. The stackingContext() can be null in the
+        // case where we're building up generated content layers. This is ok, since the lists will start
+        // off dirty in that case anyway.
+        child.dirtyStackingContextZOrderLists();
+    }
+}
+
 void RenderLayer::insertOnlyThisLayer()
 {
     if (!m_parent && renderer().parent()) {
@@ -531,9 +540,7 @@ static bool canCreateStackingContext(const RenderLayer& layer)
         || renderer.isPositioned() // Note that this only creates stacking context in conjunction with explicit z-index.
         || renderer.hasReflection()
         || renderer.style().hasIsolation()
-#if PLATFORM(IOS)
-        || layer.canUseAcceleratedTouchScrolling()
-#endif
+        || !renderer.style().hasAutoZIndex()
         || (renderer.style().willChange() && renderer.style().willChange()->canCreateStackingContext());
 }
 
@@ -547,14 +554,13 @@ bool RenderLayer::shouldBeNormalFlowOnly() const
         || renderer().isVideo()
         || renderer().isEmbeddedObject()
         || renderer().isRenderIFrame()
+        || (renderer().isRenderImage() && downcast<RenderImage>(renderer()).isEditableImage())
         || (renderer().style().specifiesColumns() && !isRenderViewLayer())
         || renderer().isInFlowRenderFragmentedFlow();
 }
 
 bool RenderLayer::shouldBeStackingContext() const
 {
-    // Non-auto z-index always implies stacking context here, because StyleResolver::adjustRenderStyle already adjusts z-index
-    // based on positioning and other criteria.
     return !renderer().style().hasAutoZIndex() || isRenderViewLayer() || isForcedStackingContext();
 }
 
@@ -594,16 +600,26 @@ void RenderLayer::setParent(RenderLayer* parent)
 
     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(m_layerListMutationAllowed);
+    ASSERT(layerListMutationAllowed());
     ASSERT(isStackingContext());
 
     if (m_posZOrderList)
@@ -612,8 +628,9 @@ void RenderLayer::dirtyZOrderLists()
         m_negZOrderList->clear();
     m_zOrderListsDirty = true;
 
-    if (!renderer().renderTreeBeingDestroyed())
-        compositor().setCompositingLayersNeedRebuild();
+    // FIXME: Ideally, we'd only dirty if the lists changed.
+    if (hasCompositingDescendant())
+        setNeedsCompositingPaintOrderChildrenUpdate();
 }
 
 void RenderLayer::dirtyStackingContextZOrderLists()
@@ -624,14 +641,14 @@ void RenderLayer::dirtyStackingContextZOrderLists()
 
 void RenderLayer::dirtyNormalFlowList()
 {
-    ASSERT(m_layerListMutationAllowed);
+    ASSERT(layerListMutationAllowed());
 
     if (m_normalFlowList)
         m_normalFlowList->clear();
     m_normalFlowListDirty = true;
 
-    if (!renderer().renderTreeBeingDestroyed())
-        compositor().setCompositingLayersNeedRebuild();
+    if (hasCompositingDescendant())
+        setNeedsCompositingPaintOrderChildrenUpdate();
 }
 
 void RenderLayer::updateNormalFlowList()
@@ -639,11 +656,11 @@ void RenderLayer::updateNormalFlowList()
     if (!m_normalFlowListDirty)
         return;
 
-    ASSERT(m_layerListMutationAllowed);
+    ASSERT(layerListMutationAllowed());
 
     for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
         // Ignore non-overflow layers and reflections.
-        if (child->isNormalFlowOnly() && (!m_reflection || reflectionLayer() != child)) {
+        if (child->isNormalFlowOnly() && !isReflectionLayer(*child)) {
             if (!m_normalFlowList)
                 m_normalFlowList = std::make_unique<Vector<RenderLayer*>>();
             m_normalFlowList->append(child);
@@ -655,17 +672,27 @@ void RenderLayer::updateNormalFlowList()
 
 void RenderLayer::rebuildZOrderLists()
 {
-    ASSERT(m_layerListMutationAllowed);
+    ASSERT(layerListMutationAllowed());
     ASSERT(isDirtyStackingContext());
     rebuildZOrderLists(m_posZOrderList, m_negZOrderList);
     m_zOrderListsDirty = false;
+    
+    bool hasNegativeZOrderList = m_negZOrderList && m_negZOrderList->size();
+    // Having negative z-order lists affect whether a compositing layer needs a foreground layer.
+    // Ideally we'd only trigger this when having z-order children changes, but we blow away the old z-order
+    // lists on dirtying so we don't know the old state.
+    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().inCompositingMode();
+    bool includeHiddenLayers = compositor().usesCompositing();
     for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
-        if (!m_reflection || reflectionLayer() != child)
+        if (!isReflectionLayer(*child))
             child->collectLayers(includeHiddenLayers, posZOrderList, negZOrderList);
     }
 
@@ -700,12 +727,21 @@ void RenderLayer::collectLayers(bool includeHiddenLayers, std::unique_ptr<Vector
     if ((includeHiddenLayers || m_hasVisibleDescendant) && !isStacking) {
         for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
             // Ignore reflections.
-            if (!m_reflection || reflectionLayer() != child)
+            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();
@@ -767,11 +803,13 @@ RenderLayerCompositor& RenderLayer::compositor() const
 
 void RenderLayer::contentChanged(ContentChangeType changeType)
 {
-    if ((changeType == CanvasChanged || changeType == VideoChanged || changeType == FullScreenChanged || changeType == ImageChanged) && compositor().updateLayerCompositingState(*this))
-        compositor().setCompositingLayersNeedRebuild();
+    if (changeType == CanvasChanged || changeType == VideoChanged || changeType == FullScreenChanged || (isComposited() && changeType == ImageChanged)) {
+        setNeedsPostLayoutCompositingUpdate();
+        setNeedsCompositingConfigurationUpdate();
+    }
 
-    if (m_backing)
-        m_backing->contentChanged(changeType);
+    if (auto* backing = this->backing())
+        backing->contentChanged(changeType);
 }
 
 bool RenderLayer::canRender3DTransforms() const
@@ -809,10 +847,7 @@ void RenderLayer::updateLayerPositionsAfterLayout(const RenderLayer* rootLayer,
 
 void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, OptionSet<UpdateLayerPositionsFlag> flags)
 {
-    updateLayerPosition(); // For relpositioned layers or non-positioned layers,
-                           // we need to keep in sync, since we may have shifted relative
-                           // to our parent layer.
-
+    updateLayerPosition(&flags);
     applyPostLayoutScrollPositionIfNeeded();
 
     if (geometryMap)
@@ -881,11 +916,6 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, OptionSet
     if (m_reflection)
         m_reflection->layout();
 
-    // Clear the IsCompositingUpdateRoot flag once we've found the first compositing layer in this update.
-    bool isUpdateRoot = flags.contains(IsCompositingUpdateRoot);
-    if (isComposited())
-        flags.remove(IsCompositingUpdateRoot);
-
     if (renderer().isInFlowRenderFragmentedFlow()) {
         updatePagination();
         flags.add(UpdatePagination);
@@ -900,15 +930,6 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, OptionSet
     for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
         child->updateLayerPositions(geometryMap, flags);
 
-    if ((flags & UpdateCompositingLayers) && isComposited()) {
-        OptionSet<RenderLayerBacking::UpdateAfterLayoutFlags> updateFlags;
-        if (flags & NeedsFullRepaintInBacking)
-            updateFlags.add(RenderLayerBacking::UpdateAfterLayoutFlags::NeedsFullRepaint);
-        if (isUpdateRoot)
-            updateFlags.add(RenderLayerBacking::UpdateAfterLayoutFlags::IsUpdateRoot);
-        backing()->updateAfterLayout(updateFlags);
-    }
-        
     // With all our children positioned, now update our marquee if we need to.
     if (m_marquee) {
         // FIXME: would like to use SetForScope<> but it doesn't work with bitfields.
@@ -918,6 +939,17 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, OptionSet
         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());
 
@@ -1141,8 +1173,11 @@ void RenderLayer::updateTransform()
         makeMatrixRenderable(*m_transform, canRender3DTransforms());
     }
 
-    if (had3DTransform != has3DTransform())
+    if (had3DTransform != has3DTransform()) {
         dirty3DTransformedDescendantStatus();
+        // Having a 3D transform affects whether enclosing perspective and preserve-3d layers composite, so trigger an update.
+        setNeedsPostLayoutCompositingUpdateOnAncestors();
+    }
 }
 
 TransformationMatrix RenderLayer::currentTransform(RenderStyle::ApplyTransformOrigin applyOrigin) const
@@ -1164,7 +1199,7 @@ TransformationMatrix RenderLayer::currentTransform(RenderStyle::ApplyTransformOr
             }
         }
     } else {
-        if (renderer().animation().isRunningAcceleratedAnimationOnRenderer(renderer(), CSSPropertyTransform, AnimationBase::Running | AnimationBase::Paused)) {
+        if (renderer().animation().isRunningAcceleratedAnimationOnRenderer(renderer(), CSSPropertyTransform)) {
             TransformationMatrix currTransform;
             FloatRect pixelSnappedBorderRect = snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor());
             std::unique_ptr<RenderStyle> style = renderer().animation().animatedStyleForRenderer(renderer());
@@ -1459,7 +1494,7 @@ bool RenderLayer::update3DTransformedDescendantStatus()
     return has3DTransform();
 }
 
-bool RenderLayer::updateLayerPosition()
+bool RenderLayer::updateLayerPosition(OptionSet<UpdateLayerPositionsFlag>* flags)
 {
     LayoutPoint localPoint;
     LayoutSize inlineBoundingBoxOffset; // We don't put this into the RenderLayer x/y for inlines, so we need to subtract it out when done.
@@ -1471,7 +1506,19 @@ bool RenderLayer::updateLayerPosition()
         localPoint += inlineBoundingBoxOffset;
     } else if (RenderBox* box = renderBox()) {
         // FIXME: Is snapping the size really needed here for the RenderBox case?
-        setSize(snappedIntRect(box->frameRect()).size());
+        auto newSize = snappedIntRect(box->frameRect()).size();
+        if (newSize != size()) {
+            if (is<RenderWidget>(*box) && downcast<RenderWidget>(*box).requiresAcceleratedCompositing()) {
+                // Trigger RenderLayerCompositor::requiresCompositingForFrame() which depends on the contentBoxRect size.
+                setNeedsPostLayoutCompositingUpdate();
+            }
+
+            if (flags && renderer().hasOverflowClip())
+                flags->add(ContainingClippingLayerChangedSize);
+
+            setSize(newSize);
+        }
+        
         box->applyTopLeftLocationOffset(localPoint);
     }
 
@@ -1524,6 +1571,16 @@ bool RenderLayer::updateLayerPosition()
     
     positionOrOffsetChanged |= location() != localPoint;
     setLocation(localPoint);
+    
+    if (positionOrOffsetChanged && compositor().hasContentCompositingLayers()) {
+        if (isComposited())
+            setNeedsCompositingGeometryUpdate();
+        // This layer's position can affect the location of a composited descendant (which may be a sibling in z-order),
+        // so trigger a descendant walk from the paint-order parent.
+        if (auto* paintParent = paintOrderParent())
+            paintParent->setDescendantsNeedUpdateBackingAndHierarchyTraversal();
+    }
+
     return positionOrOffsetChanged;
 }
 
@@ -1570,16 +1627,6 @@ FloatPoint RenderLayer::perspectiveOrigin() const
                       floatValueForLength(style.perspectiveOriginY(), borderBox.height()));
 }
 
-RenderLayer* RenderLayer::stackingContext() const
-{
-    RenderLayer* layer = parent();
-    while (layer && !layer->isStackingContext())
-        layer = layer->parent();
-
-    ASSERT(!layer || layer->isStackingContext());
-    return layer;
-}
-
 static inline bool isContainerForPositioned(RenderLayer& layer, PositionType position)
 {
     switch (position) {
@@ -1604,22 +1651,27 @@ RenderLayer* RenderLayer::enclosingAncestorForPosition(PositionType position) co
     return curr;
 }
 
-static RenderLayer* parentLayerCrossFrame(const RenderLayer& layer)
+static RenderLayer* enclosingFrameRenderLayer(const RenderLayer& layer)
 {
-    if (layer.parent())
-        return layer.parent();
-
-    HTMLFrameOwnerElement* ownerElement = layer.renderer().document().ownerElement();
+    auto* ownerElement = layer.renderer().document().ownerElement();
     if (!ownerElement)
         return nullptr;
 
-    RenderElement* ownerRenderer = ownerElement->renderer();
+    auto* ownerRenderer = ownerElement->renderer();
     if (!ownerRenderer)
         return nullptr;
 
     return ownerRenderer->enclosingLayer();
 }
 
+static RenderLayer* parentLayerCrossFrame(const RenderLayer& layer)
+{
+    if (auto* parent = layer.parent())
+        return parent;
+
+    return enclosingFrameRenderLayer(layer);
+}
+
 RenderLayer* RenderLayer::enclosingScrollableLayer() const
 {
     for (RenderLayer* nextLayer = parentLayerCrossFrame(*this); nextLayer; nextLayer = parentLayerCrossFrame(*nextLayer)) {
@@ -1662,11 +1714,6 @@ RenderLayer* RenderLayer::enclosingTransformedAncestor() const
     return curr;
 }
 
-static inline const RenderLayer* compositingContainer(const RenderLayer& layer)
-{
-    return layer.isNormalFlowOnly() ? layer.parent() : layer.stackingContext();
-}
-
 inline bool RenderLayer::shouldRepaintAfterLayout() const
 {
     if (m_repaintStatus == NeedsNormalRepaint)
@@ -1688,7 +1735,7 @@ RenderLayer* RenderLayer::enclosingCompositingLayer(IncludeSelfOrNot includeSelf
     if (includeSelf == IncludeSelf && isComposited())
         return const_cast<RenderLayer*>(this);
 
-    for (const RenderLayer* curr = compositingContainer(*this); curr; curr = compositingContainer(*curr)) {
+    for (const RenderLayer* curr = paintOrderParent(); curr; curr = curr->paintOrderParent()) {
         if (curr->isComposited())
             return const_cast<RenderLayer*>(curr);
     }
@@ -1701,7 +1748,7 @@ RenderLayer* RenderLayer::enclosingCompositingLayerForRepaint(IncludeSelfOrNot i
     if (includeSelf == IncludeSelf && compositedWithOwnBackingStore(*this))
         return const_cast<RenderLayer*>(this);
 
-    for (const RenderLayer* curr = compositingContainer(*this); curr; curr = compositingContainer(*curr)) {
+    for (const RenderLayer* curr = paintOrderParent(); curr; curr = curr->paintOrderParent()) {
         if (compositedWithOwnBackingStore(*curr))
             return const_cast<RenderLayer*>(curr);
     }
@@ -1790,7 +1837,7 @@ RenderLayer* RenderLayer::clippingRootForPainting() const
         if (current->isRenderViewLayer())
             return const_cast<RenderLayer*>(current);
 
-        current = compositingContainer(*current);
+        current = current->paintOrderParent();
         ASSERT(current);
         if (current->transform() || compositedWithOwnBackingStore(*current))
             return const_cast<RenderLayer*>(current);
@@ -1850,7 +1897,7 @@ static void expandClipRectForDescendantsAndReflection(LayoutRect& clipRect, cons
         // Note: we don't have to walk z-order lists since transparent elements always establish
         // a stacking container. This means we can just walk the layer tree directly.
         for (RenderLayer* curr = layer.firstChild(); curr; curr = curr->nextSibling()) {
-            if (!layer.reflection() || layer.reflectionLayer() != curr)
+            if (!layer.isReflectionLayer(*curr))
                 clipRect.unite(transparencyClipBox(*curr, rootLayer, transparencyBehavior, DescendantsOfTransparencyClipBox, paintBehavior));
         }
     }
@@ -1955,7 +2002,7 @@ void RenderLayer::beginTransparencyLayers(GraphicsContext& context, const LayerP
     }
 }
 
-#if PLATFORM(IOS)
+#if PLATFORM(IOS_FAMILY)
 void RenderLayer::willBeDestroyed()
 {
     if (RenderLayerBacking* layerBacking = backing())
@@ -2124,25 +2171,28 @@ LayoutSize RenderLayer::offsetFromAncestor(const RenderLayer* ancestorLayer, Col
     return toLayoutSize(convertToLayerCoords(ancestorLayer, LayoutPoint(), adjustForColumns));
 }
 
-bool RenderLayer::canUseAcceleratedTouchScrolling() const
+bool RenderLayer::canUseCompositedScrolling() const
 {
-#if PLATFORM(IOS) && ENABLE(ACCELERATED_OVERFLOW_SCROLLING)
+    if (renderer().settings().asyncOverflowScrollingEnabled())
+        return scrollsOverflow();
+
+#if PLATFORM(IOS_FAMILY) && ENABLE(OVERFLOW_SCROLLING_TOUCH)
     return scrollsOverflow() && (renderer().style().useTouchOverflowScrolling() || renderer().settings().alwaysUseAcceleratedOverflowScroll());
 #else
     return false;
 #endif
 }
 
-bool RenderLayer::hasTouchScrollableOverflow() const
+bool RenderLayer::hasCompositedScrollableOverflow() const
 {
-    return canUseAcceleratedTouchScrolling() && (hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
+    return canUseCompositedScrolling() && (hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
 }
 
 #if ENABLE(IOS_TOUCH_EVENTS)
 bool RenderLayer::handleTouchEvent(const PlatformTouchEvent& touchEvent)
 {
     // If we have accelerated scrolling, let the scrolling be handled outside of WebKit.
-    if (hasTouchScrollableOverflow())
+    if (hasCompositedScrollableOverflow())
         return false;
 
     return ScrollableArea::handleTouchEvent(touchEvent);
@@ -2176,7 +2226,7 @@ bool RenderLayer::usesCompositedScrolling() const
 // FIXME: this is only valid after we've made layers.
 bool RenderLayer::usesAsyncScrolling() const
 {
-    return canUseAcceleratedTouchScrolling() && usesCompositedScrolling();
+    return compositor().useCoordinatedScrollingForLayer(*this);
 }
 
 static inline int adjustedScrollDelta(int beginningDelta)
@@ -2256,7 +2306,7 @@ void RenderLayer::scrollByRecursively(const IntSize& delta, ScrollableArea** scr
     }
 }
 
-void RenderLayer::setPostLayoutScrollPosition(std::optional<ScrollPosition> position)
+void RenderLayer::setPostLayoutScrollPosition(Optional<ScrollPosition> position)
 {
     m_postLayoutScrollPosition = position;
 }
@@ -2267,19 +2317,19 @@ void RenderLayer::applyPostLayoutScrollPositionIfNeeded()
         return;
 
     scrollToOffset(scrollOffsetFromPosition(m_postLayoutScrollPosition.value()));
-    m_postLayoutScrollPosition = std::nullopt;
+    m_postLayoutScrollPosition = WTF::nullopt;
 }
 
-void RenderLayer::scrollToXPosition(int x, ScrollClamping clamping)
+void RenderLayer::scrollToXPosition(int x, ScrollType scrollType, ScrollClamping clamping)
 {
     ScrollPosition position(x, m_scrollPosition.y());
-    scrollToOffset(scrollOffsetFromPosition(position), clamping);
+    scrollToOffset(scrollOffsetFromPosition(position), scrollType, clamping);
 }
 
-void RenderLayer::scrollToYPosition(int y, ScrollClamping clamping)
+void RenderLayer::scrollToYPosition(int y, ScrollType scrollType, ScrollClamping clamping)
 {
     ScrollPosition position(m_scrollPosition.x(), y);
-    scrollToOffset(scrollOffsetFromPosition(position), clamping);
+    scrollToOffset(scrollOffsetFromPosition(position), scrollType, clamping);
 }
 
 ScrollOffset RenderLayer::clampScrollOffset(const ScrollOffset& scrollOffset) const
@@ -2287,11 +2337,25 @@ ScrollOffset RenderLayer::clampScrollOffset(const ScrollOffset& scrollOffset) co
     return scrollOffset.constrainedBetween(IntPoint(), maximumScrollOffset());
 }
 
-void RenderLayer::scrollToOffset(const ScrollOffset& scrollOffset, ScrollClamping clamping)
+void RenderLayer::scrollToOffset(const ScrollOffset& scrollOffset, ScrollType scrollType, ScrollClamping clamping)
 {
     ScrollOffset newScrollOffset = clamping == ScrollClamping::Clamped ? clampScrollOffset(scrollOffset) : scrollOffset;
-    if (newScrollOffset != this->scrollOffset())
+    if (newScrollOffset == this->scrollOffset())
+        return;
+
+    auto previousScrollType = currentScrollType();
+    setCurrentScrollType(scrollType);
+
+    bool handled = false;
+#if ENABLE(ASYNC_SCROLLING)
+    if (ScrollingCoordinator* scrollingCoordinator = page().scrollingCoordinator())
+        handled = scrollingCoordinator->requestScrollPositionUpdate(*this, scrollPositionFromOffset(scrollOffset));
+#endif
+
+    if (!handled)
         scrollToOffsetWithoutAnimation(newScrollOffset, clamping);
+
+    setCurrentScrollType(previousScrollType);
 }
 
 void RenderLayer::scrollTo(const ScrollPosition& position)
@@ -2300,14 +2364,14 @@ void RenderLayer::scrollTo(const ScrollPosition& position)
     if (!box)
         return;
 
-    LOG_WITH_STREAM(Scrolling, stream << "RenderLayer::scrollTo " << position);
+    LOG_WITH_STREAM(Scrolling, stream << "RenderLayer::scrollTo " << position << " from " << m_scrollPosition << " (is user scroll " << (currentScrollType() == ScrollType::User) << ")");
 
     ScrollPosition newPosition = position;
     if (!box->isHTMLMarquee()) {
         // Ensure that the dimensions will be computed if they need to be (for overflow:hidden blocks).
         if (m_scrollDimensionsDirty)
             computeScrollDimensions();
-#if PLATFORM(IOS)
+#if PLATFORM(IOS_FAMILY)
         if (adjustForIOSCaretWhenScrolling()) {
             // FIXME: It's not clear what this code is trying to do. Behavior seems reasonable with it removed.
             int maxOffset = scrollWidth() - roundToInt(box->clientWidth());
@@ -2327,10 +2391,12 @@ void RenderLayer::scrollTo(const ScrollPosition& position)
     }
     
     if (m_scrollPosition == newPosition) {
-#if PLATFORM(IOS)
-        if (m_requiresScrollBoundsOriginUpdate)
+        // FIXME: Nothing guarantees we get a scrollTo() with an unchanged position at the end of a user gesture.
+        // The ScrollingCoordinator probably needs to message the main thread when a gesture ends.
+        if (requiresScrollPositionReconciliation()) {
+            setNeedsCompositingGeometryUpdate();
             updateCompositingLayersAfterScroll();
-#endif
+        }
         return;
     }
 
@@ -2343,10 +2409,7 @@ void RenderLayer::scrollTo(const ScrollPosition& position)
     if (!view.frameView().layoutContext().isInRenderTreeLayout()) {
         // If we're in the middle of layout, we'll just update layers once layout has finished.
         updateLayerPositionsAfterOverflowScroll();
-        // Update regions, scrolling may change the clip of a particular region.
-#if ENABLE(DASHBOARD_SUPPORT)
-        view.frameView().updateAnnotatedRegions();
-#endif
+
         view.frameView().scheduleUpdateWidgetPositions();
 
         if (!m_updatingMarqueePosition) {
@@ -2354,12 +2417,16 @@ void RenderLayer::scrollTo(const ScrollPosition& position)
             // positions. Updating layer positions requires a full walk of up-to-date RenderLayers, and
             // in this case we're still updating their positions; we'll update compositing layers later
             // when that completes.
+            if (usesCompositedScrolling()) {
+                setNeedsCompositingGeometryUpdate();
+                setDescendantsNeedUpdateBackingAndHierarchyTraversal();
+            }
+
             updateCompositingLayersAfterScroll();
         }
 
-#if PLATFORM(IOS) && ENABLE(TOUCH_EVENTS)
-        renderer().document().setTouchEventRegionsNeedUpdate();
-#endif
+        // Update regions, scrolling may change the clip of a particular region.
+        renderer().document().invalidateRenderingDependentRegions(Document::AnnotationsAction::Update);
         DebugPageOverlays::didLayout(renderer().frame());
     }
 
@@ -2376,8 +2443,11 @@ void RenderLayer::scrollTo(const ScrollPosition& position)
     frame.eventHandler().dispatchFakeMouseMoveEventSoonInQuad(quadForFakeMouseMoveEvent);
 
     bool requiresRepaint = true;
-    if (compositor().inCompositingMode() && usesCompositedScrolling())
+    if (usesCompositedScrolling()) {
+        setNeedsCompositingGeometryUpdate();
+        setDescendantsNeedUpdateBackingAndHierarchyTraversal();
         requiresRepaint = false;
+    }
 
     // Just schedule a full repaint of our object.
     if (requiresRepaint)
@@ -2451,7 +2521,7 @@ void RenderLayer::scrollRectToVisible(const LayoutRect& absoluteRect, bool insid
         RenderBox* box = renderBox();
         ASSERT(box);
         LayoutRect localExposeRect(box->absoluteToLocalQuad(FloatQuad(FloatRect(absoluteRect))).boundingBox());
-        LayoutRect layerBounds(0, 0, box->clientWidth(), box->clientHeight());
+        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()));
@@ -2495,10 +2565,10 @@ void RenderLayer::scrollRectToVisible(const LayoutRect& absoluteRect, bool insid
             if (options.revealMode == SelectionRevealMode::RevealUpToMainFrame && frameView.frame().isMainFrame())
                 return;
 
-#if !PLATFORM(IOS)
+#if !PLATFORM(IOS_FAMILY)
             LayoutRect viewRect = frameView.visibleContentRect();
 #else
-            LayoutRect viewRect = frameView.unobscuredContentRect();
+            LayoutRect viewRect = frameView.unobscuredContentRectExpandedByContentInsets();
 #endif
             // Move the target rect into "scrollView contents" coordinates.
             LayoutRect targetRect = absoluteRect;
@@ -2523,19 +2593,22 @@ void RenderLayer::scrollRectToVisible(const LayoutRect& absoluteRect, bool insid
 
 void RenderLayer::updateCompositingLayersAfterScroll()
 {
-    if (compositor().inCompositingMode()) {
+    if (compositor().hasContentCompositingLayers()) {
         // Our stacking container is guaranteed to contain all of our descendants that may need
         // repositioning, so update compositing layers from there.
         if (RenderLayer* compositingAncestor = stackingContext()->enclosingCompositingLayer()) {
             if (usesCompositedScrolling())
                 compositor().updateCompositingLayers(CompositingUpdateType::OnCompositedScroll, compositingAncestor);
-            else
+            else {
+                // FIXME: would be nice to only dirty layers whose positions were affected by scrolling.
+                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
+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) {
@@ -2695,7 +2768,7 @@ void RenderLayer::resize(const PlatformMouseEvent& evt, const LayoutSize& oldOff
             styledElement->setInlineStyleProperty(CSSPropertyMarginLeft, renderer->marginLeft() / zoomFactor, CSSPrimitiveValue::CSS_PX);
             styledElement->setInlineStyleProperty(CSSPropertyMarginRight, renderer->marginRight() / zoomFactor, CSSPrimitiveValue::CSS_PX);
         }
-        LayoutUnit baseWidth = renderer->width() - (isBoxSizingBorder ? LayoutUnit() : renderer->horizontalBorderAndPaddingExtent());
+        LayoutUnit baseWidth = renderer->width() - (isBoxSizingBorder ? 0_lu : renderer->horizontalBorderAndPaddingExtent());
         baseWidth = baseWidth / zoomFactor;
         styledElement->setInlineStyleProperty(CSSPropertyWidth, roundToInt(baseWidth + difference.width()), CSSPrimitiveValue::CSS_PX);
     }
@@ -2706,7 +2779,7 @@ void RenderLayer::resize(const PlatformMouseEvent& evt, const LayoutSize& oldOff
             styledElement->setInlineStyleProperty(CSSPropertyMarginTop, renderer->marginTop() / zoomFactor, CSSPrimitiveValue::CSS_PX);
             styledElement->setInlineStyleProperty(CSSPropertyMarginBottom, renderer->marginBottom() / zoomFactor, CSSPrimitiveValue::CSS_PX);
         }
-        LayoutUnit baseHeight = renderer->height() - (isBoxSizingBorder ? LayoutUnit() : renderer->verticalBorderAndPaddingExtent());
+        LayoutUnit baseHeight = renderer->height() - (isBoxSizingBorder ? 0_lu : renderer->verticalBorderAndPaddingExtent());
         baseHeight = baseHeight / zoomFactor;
         styledElement->setInlineStyleProperty(CSSPropertyHeight, roundToInt(baseHeight + difference.height()), CSSPrimitiveValue::CSS_PX);
     }
@@ -2738,14 +2811,22 @@ int RenderLayer::scrollOffset(ScrollbarOrientation orientation) const
     return 0;
 }
 
+ScrollingNodeID RenderLayer::scrollingNodeID() const
+{
+    if (!isComposited())
+        return 0;
+
+    return backing()->scrollingNodeIDForRole(ScrollCoordinationRole::Scrolling);
+}
+
 IntRect RenderLayer::visibleContentRectInternal(VisibleContentRectIncludesScrollbars scrollbarInclusion, VisibleContentRectBehavior) const
 {
     IntSize scrollbarSpace;
     if (showsOverflowControls() && scrollbarInclusion == IncludeScrollbars)
         scrollbarSpace = scrollbarIntrusion();
     
-    // FIXME: This seems wrong: m_layerSize includes borders. Can we just use the ScrollableArea implementation?
-    return IntRect(scrollPosition(), IntSize(std::max(0, m_layerSize.width() - scrollbarSpace.width()), std::max(0, m_layerSize.height() - scrollbarSpace.height())));
+    auto visibleSize = this->visibleSize();
+    return { scrollPosition(), { std::max(0, visibleSize.width() - scrollbarSpace.width()), std::max(0, visibleSize.height() - scrollbarSpace.height()) } };
 }
 
 IntSize RenderLayer::overhangAmount() const
@@ -2758,15 +2839,16 @@ IntSize RenderLayer::overhangAmount() const
 
     // FIXME: use maximumScrollOffset(), or just move this to ScrollableArea.
     ScrollOffset scrollOffset = scrollOffsetFromPosition(scrollPosition());
+    auto reachableSize = reachableTotalContentsSize();
     if (scrollOffset.y() < 0)
         stretch.setHeight(scrollOffset.y());
-    else if (scrollableContentsSize().height() && scrollOffset.y() > scrollableContentsSize().height() - visibleHeight())
-        stretch.setHeight(scrollOffset.y() - (scrollableContentsSize().height() - visibleHeight()));
+    else if (reachableSize.height() && scrollOffset.y() > reachableSize.height() - visibleHeight())
+        stretch.setHeight(scrollOffset.y() - (reachableSize.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()));
+    else if (reachableSize.width() && scrollOffset.x() > reachableSize.width() - visibleWidth())
+        stretch.setWidth(scrollOffset.x() - (reachableSize.width() - visibleWidth()));
 
     return stretch;
 #else
@@ -2893,7 +2975,7 @@ IntSize RenderLayer::contentsSize() const
     return IntSize(scrollWidth(), scrollHeight());
 }
 
-IntSize RenderLayer::scrollableContentsSize() const
+IntSize RenderLayer::reachableTotalContentsSize() const
 {
     IntSize contentsSize = this->contentsSize();
 
@@ -2922,7 +3004,7 @@ bool RenderLayer::shouldSuspendScrollAnimations() const
     return renderer().view().frameView().shouldSuspendScrollAnimations();
 }
 
-#if PLATFORM(IOS)
+#if PLATFORM(IOS_FAMILY)
 void RenderLayer::didStartScroll()
 {
     page().chrome().client().didStartOverflowScroll();
@@ -3058,6 +3140,26 @@ void RenderLayer::invalidateScrollCornerRect(const IntRect& rect)
         m_resizer->repaintRectangle(rect);
 }
 
+static bool scrollbarHiddenByStyle(Scrollbar* scrollbar)
+{
+    if (!scrollbar || !scrollbar->isCustomScrollbar())
+        return false;
+
+    std::unique_ptr<RenderStyle> scrollbarStyle = static_cast<RenderScrollbar*>(scrollbar)->getScrollbarPseudoStyle(ScrollbarBGPart, PseudoId::Scrollbar);
+
+    return scrollbarStyle && scrollbarStyle->display() == DisplayType::None;
+}
+
+bool RenderLayer::horizontalScrollbarHiddenByStyle() const
+{
+    return scrollbarHiddenByStyle(horizontalScrollbar());
+}
+
+bool RenderLayer::verticalScrollbarHiddenByStyle() const
+{
+    return scrollbarHiddenByStyle(verticalScrollbar());
+}
+
 static inline RenderElement* rendererForScrollbar(RenderLayerModelObject& renderer)
 {
     if (Element* element = renderer.element()) {
@@ -3133,11 +3235,7 @@ void RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar)
     if (m_vBar)
         m_vBar->styleChanged();
 
-    // Force an update since we know the scrollbars have changed things.
-#if ENABLE(DASHBOARD_SUPPORT)
-    if (renderer().document().hasAnnotatedRegions())
-        renderer().document().setAnnotatedRegionsDirty(true);
-#endif
+    renderer().document().invalidateScrollbarDependentRegions();
 }
 
 void RenderLayer::setHasVerticalScrollbar(bool hasScrollbar)
@@ -3164,11 +3262,7 @@ void RenderLayer::setHasVerticalScrollbar(bool hasScrollbar)
     if (m_vBar)
         m_vBar->styleChanged();
 
-    // Force an update since we know the scrollbars have changed things.
-#if ENABLE(DASHBOARD_SUPPORT)
-    if (renderer().document().hasAnnotatedRegions())
-        renderer().document().setAnnotatedRegionsDirty(true);
-#endif
+    renderer().document().invalidateScrollbarDependentRegions();
 }
 
 ScrollableArea* RenderLayer::enclosingScrollableArea() const
@@ -3196,6 +3290,11 @@ bool RenderLayer::hasScrollableOrRubberbandableAncestor()
     return false;
 }
 
+bool RenderLayer::useDarkAppearance() const
+{
+    return renderer().useDarkAppearance();
+}
+
 #if ENABLE(CSS_SCROLL_SNAP)
 void RenderLayer::updateSnapOffsets()
 {
@@ -3434,12 +3533,7 @@ void RenderLayer::updateScrollbarsAfterLayout()
 
         updateSelfPaintingLayer();
 
-        // Force an update since we know the scrollbars have changed things.
-#if ENABLE(DASHBOARD_SUPPORT)
-        if (renderer().document().hasAnnotatedRegions())
-            renderer().document().setAnnotatedRegionsDirty(true);
-#endif
-
+        renderer().document().invalidateScrollbarDependentRegions();
         renderer().repaint();
 
         if (renderer().style().overflowX() == Overflow::Auto || renderer().style().overflowY() == Overflow::Auto) {
@@ -3500,7 +3594,7 @@ void RenderLayer::updateScrollInfoAfterLayout()
         // Layout may cause us to be at an invalid scroll position. In this case we need
         // to pull our scroll offsets back to the max (or push them up to the min).
         ScrollOffset clampedScrollOffset = clampScrollOffset(scrollOffset());
-#if PLATFORM(IOS)
+#if PLATFORM(IOS_FAMILY)
         // FIXME: This looks wrong. The caret adjust mode should only be enabled on editing related entry points.
         // This code was added to fix an issue where the text insertion point would always be drawn on the right edge
         // of a text field whose content overflowed its bounds. See <rdar://problem/15579797> for more details.
@@ -3509,7 +3603,7 @@ void RenderLayer::updateScrollInfoAfterLayout()
         if (clampedScrollOffset != scrollOffset())
             scrollToOffset(clampedScrollOffset);
 
-#if PLATFORM(IOS)
+#if PLATFORM(IOS_FAMILY)
         setAdjustForIOSCaretWhenScrolling(false);
 #endif
     }
@@ -3519,10 +3613,15 @@ void RenderLayer::updateScrollInfoAfterLayout()
     if (originalScrollOffset != scrollOffset())
         scrollToOffsetWithoutAnimation(IntPoint(scrollOffset()));
 
-    // Composited scrolling may need to be enabled or disabled if the amount of overflow changed.
-    // FIXME: this should just dirty the layer and updateLayerPositions should do stuff.
-    if (compositor().updateLayerCompositingState(*this))
-        compositor().setCompositingLayersNeedRebuild();
+    if (isComposited()) {
+        setNeedsCompositingGeometryUpdate();
+        setNeedsCompositingConfigurationUpdate();
+    }
+
+#if PLATFORM(IOS_FAMILY)
+    if (canUseCompositedScrolling())
+        setNeedsPostLayoutCompositingUpdate();
+#endif
 
     updateScrollSnapState();
 }
@@ -3548,10 +3647,9 @@ bool RenderLayer::overflowControlsIntersectRect(const IntRect& localRect) const
 
 bool RenderLayer::showsOverflowControls() const
 {
-#if PLATFORM(IOS)
-    // Don't render (custom) scrollbars if we have accelerated scrolling.
-    if (canUseAcceleratedTouchScrolling())
-        return false;
+#if PLATFORM(IOS_FAMILY)
+    // On iOS, the scrollbars are made in the UI process.
+    return !canUseCompositedScrolling();
 #endif
 
     return true;
@@ -3706,7 +3804,7 @@ void RenderLayer::paintResizer(GraphicsContext& context, const LayoutPoint& pain
         GraphicsContextStateSaver stateSaver(context);
         context.clip(absRect);
         LayoutRect largerCorner = absRect;
-        largerCorner.setSize(LayoutSize(largerCorner.width() + LayoutUnit::fromPixel(1), largerCorner.height() + LayoutUnit::fromPixel(1)));
+        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);
@@ -4186,21 +4284,21 @@ void RenderLayer::paintLayerContents(GraphicsContext& context, const LayerPainti
     bool isPaintingCompositedForeground = paintFlags.contains(PaintLayerPaintingCompositingForegroundPhase);
     bool isPaintingCompositedBackground = paintFlags.contains(PaintLayerPaintingCompositingBackgroundPhase);
     bool isPaintingOverflowContents = paintFlags.contains(PaintLayerPaintingOverflowContents);
+    bool isCollectingEventRegion = paintFlags.contains(PaintLayerCollectingEventRegion);
     // Outline always needs to be painted even if we have no visible content. Also,
     // the outline is painted in the background phase during composited scrolling.
     // If it were painted in the foreground phase, it would move with the scrolled
     // content. When not composited scrolling, the outline is painted in the
     // foreground phase. Since scrolled contents are moved by repainting in this
     // case, the outline won't get 'dragged along'.
-    bool shouldPaintOutline = isSelfPaintingLayer && !isPaintingOverlayScrollbars
+    bool shouldPaintOutline = isSelfPaintingLayer && !isPaintingOverlayScrollbars && !isCollectingEventRegion
         && ((isPaintingScrollingContent && isPaintingCompositedBackground)
         || (!isPaintingScrollingContent && isPaintingCompositedForeground));
-    bool shouldPaintContent = m_hasVisibleContent && isSelfPaintingLayer && !isPaintingOverlayScrollbars;
+    bool shouldPaintContent = m_hasVisibleContent && isSelfPaintingLayer && !isPaintingOverlayScrollbars && !isCollectingEventRegion;
 
     if (localPaintFlags & PaintLayerPaintingRootBackgroundOnly && !renderer().isRenderView() && !renderer().isDocumentElementRenderer())
         return;
 
-    // Ensure our lists are up-to-date.
     updateLayerListsIfNeeded();
 
     LayoutSize offsetFromRoot = offsetFromAncestor(paintingInfo.rootLayer);
@@ -4267,7 +4365,7 @@ void RenderLayer::paintLayerContents(GraphicsContext& context, const LayerPainti
             paintBehavior.add(PaintBehavior::ExcludeSelection);
 
         LayoutRect paintDirtyRect = localPaintingInfo.paintDirtyRect;
-        if (shouldPaintContent || shouldPaintOutline || isPaintingOverlayScrollbars) {
+        if (shouldPaintContent || shouldPaintOutline || isPaintingOverlayScrollbars || isCollectingEventRegion) {
             // Collect the fragments. This will compute the clip rectangles and paint offsets for each layer fragment, as well as whether or not the content of each
             // fragment should paint. If the parent's filter dictates full repaint to ensure proper filter effect,
             // use the overflow clip as dirty rect, instead of no clipping. It maintains proper clipping for overflow::scroll.
@@ -4301,6 +4399,9 @@ void RenderLayer::paintLayerContents(GraphicsContext& context, const LayerPainti
             }
         }
 
+        if (isCollectingEventRegion)
+            collectEventRegionForFragments(layerFragments, currentContext, localPaintingInfo);
+
         if (shouldPaintOutline)
             paintOutlineForFragments(layerFragments, currentContext, localPaintingInfo, paintBehavior, subtreePaintRootForRenderer);
 
@@ -4394,7 +4495,7 @@ void RenderLayer::paintLayerByApplyingTransform(GraphicsContext& context, const
     LayoutSize adjustedSubpixelOffset = offsetForThisLayer - LayoutSize(devicePixelSnappedOffsetForThisLayer);
     LayerPaintingInfo transformedPaintingInfo(paintingInfo);
     transformedPaintingInfo.rootLayer = this;
-    transformedPaintingInfo.paintDirtyRect = LayoutRect(encloseRectToDevicePixels(transform.inverse().value_or(AffineTransform()).mapRect(paintingInfo.paintDirtyRect), deviceScaleFactor));
+    transformedPaintingInfo.paintDirtyRect = LayoutRect(encloseRectToDevicePixels(transform.inverse().valueOr(AffineTransform()).mapRect(paintingInfo.paintDirtyRect), deviceScaleFactor));
     transformedPaintingInfo.subpixelOffset = adjustedSubpixelOffset;
     paintLayerContentsAndReflection(context, transformedPaintingInfo, paintFlags);
     context.setCTM(oldTransfrom);
@@ -4409,7 +4510,7 @@ void RenderLayer::paintList(LayerList layerIterator, GraphicsContext& context, c
         return;
 
 #if !ASSERT_DISABLED
-    LayerListMutationDetector mutationChecker(this);
+    LayerListMutationDetector mutationChecker(*this);
 #endif
 
     for (auto* childLayer : layerIterator)
@@ -4783,6 +4884,17 @@ void RenderLayer::paintOverflowControlsForFragments(const LayerFragments& layerF
     }
 }
 
+void RenderLayer::collectEventRegionForFragments(const LayerFragments& layerFragments, GraphicsContext& context, const LayerPaintingInfo& localPaintingInfo)
+{
+    ASSERT(localPaintingInfo.eventRegion);
+
+    for (const auto& fragment : layerFragments) {
+        PaintInfo paintInfo(context, fragment.foregroundRect.rect(), PaintPhase::EventRegion, { });
+        paintInfo.eventRegion = localPaintingInfo.eventRegion;
+        renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset));
+    }
+}
+
 bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result)
 {
     return hitTest(request, result.hitTestLocation(), result);
@@ -4793,8 +4905,6 @@ bool RenderLayer::hitTest(const HitTestRequest& request, const HitTestLocation&
     ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant());
     ASSERT(!renderer().view().needsLayout());
     
-    updateLayerListsIfNeeded();
-
     ASSERT(!isRenderFragmentedFlow());
     LayoutRect hitTestArea = renderer().view().documentRect();
     if (!request.ignoreClipping()) {
@@ -4937,6 +5047,8 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont
                                        const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, bool appliedTransform,
                                        const HitTestingTransformState* transformState, double* zOffset)
 {
+    updateLayerListsIfNeeded();
+
     if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant())
         return nullptr;
 
@@ -4960,7 +5072,6 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont
     }
 
     // Ensure our lists and 3d status are up-to-date.
-    updateCompositingAndLayerListsIfNeeded();
     update3DTransformedDescendantStatus();
 
     RefPtr<HitTestingTransformState> localTransformState;
@@ -4975,7 +5086,7 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont
 
     // Check for hit test on backface if backface-visibility is 'hidden'
     if (localTransformState && renderer().style().backfaceVisibility() == BackfaceVisibility::Hidden) {
-        std::optional<TransformationMatrix> invertedMatrix = localTransformState->m_accumulatedTransform.inverse();
+        Optional<TransformationMatrix> invertedMatrix = localTransformState->m_accumulatedTransform.inverse();
         // If the z-vector of the matrix is negative, the back is facing towards the viewer.
         if (invertedMatrix && invertedMatrix.value().m33() < 0)
             return nullptr;
@@ -5010,7 +5121,7 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont
     // This variable tracks which layer the mouse ends up being inside.
     RenderLayer* candidateLayer = nullptr;
 #if !ASSERT_DISABLED
-    LayerListMutationDetector mutationChecker(this);
+    LayerListMutationDetector mutationChecker(*this);
 #endif
 
     // Begin by walking our list of positive layers from highest z-index down to the lowest z-index.
@@ -5359,7 +5470,7 @@ void RenderLayer::calculateClipRects(const ClipRectsContext& clipRectsContext, C
         clipRects.setOverflowClipRect(clipRects.posClipRect());
     
     // Update the clip rects that will be passed to child layers.
-#if PLATFORM(IOS)
+#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()) {
@@ -5416,16 +5527,18 @@ static inline ClipRect backgroundClipRectForPosition(const ClipRects& parentRect
 ClipRect RenderLayer::backgroundClipRect(const ClipRectsContext& clipRectsContext) const
 {
     ASSERT(parent());
-    auto computeParentRects = [this, &clipRectsContext] () {
-        // If we cross into a different pagination context, then we can't rely on the cache.
-        // Just switch over to using TemporaryClipRects.
-        if (clipRectsContext.clipRectsType != TemporaryClipRects
-            && parent()->enclosingPaginationLayer(IncludeCompositedPaginatedLayers) != enclosingPaginationLayer(IncludeCompositedPaginatedLayers)) {
-            ClipRectsContext tempContext(clipRectsContext);
-            tempContext.clipRectsType = TemporaryClipRects;
-            return parentClipRects(tempContext);
-        }
-        return parentClipRects(clipRectsContext);
+    auto computeParentRects = [&] {
+        if (clipRectsContext.clipRectsType == TemporaryClipRects)
+            return parentClipRects(clipRectsContext);
+        // If we cross into a different composition/pagination context, then we can't rely on the cache since the root layer differs.
+        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();
@@ -5729,6 +5842,14 @@ FloatRect RenderLayer::absoluteBoundingBoxForPainting() const
     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())
@@ -5789,7 +5910,7 @@ LayoutRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, c
     ASSERT(isStackingContext() || !positiveZOrderLayers().size());
 
 #if !ASSERT_DISABLED
-    LayerListMutationDetector mutationChecker(const_cast<RenderLayer*>(this));
+    LayerListMutationDetector mutationChecker(const_cast<RenderLayer&>(*this));
 #endif
 
     auto computeLayersUnion = [this, &unionBounds, flags, descendantFlags] (const RenderLayer& childLayer) {
@@ -5810,10 +5931,7 @@ LayoutRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, c
     for (auto* childLayer : normalFlowLayers())
         computeLayersUnion(*childLayer);
 
-    // FIXME: We can optimize the size of the composited layers, by not enlarging
-    // filtered areas with the outsets if we know that the filter is going to render in hardware.
-    // https://bugs.webkit.org/show_bug.cgi?id=81239
-    if (flags & IncludeLayerFilterOutsets)
+    if (flags.contains(IncludeFilterOutsets) || (flags.contains(IncludePaintedFilterOutsets) && paintsWithFilters()))
         renderer().style().filterOutsets().expandRect(unionBounds);
 
     if ((flags & IncludeSelfTransform) && paintsWithTransform(PaintBehavior::Normal)) {
@@ -5861,8 +5979,13 @@ RenderLayerBacking* RenderLayer::ensureBacking()
 
 void RenderLayer::clearBacking(bool layerBeingDestroyed)
 {
-    if (m_backing && !renderer().renderTreeBeingDestroyed())
+    if (!m_backing)
+        return;
+
+    if (!renderer().renderTreeBeingDestroyed())
         compositor().layerBecameNonComposited(*this);
+    
+    m_backing->willBeDestroyed();
     m_backing = nullptr;
 
     if (!layerBeingDestroyed)
@@ -5874,11 +5997,6 @@ 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;
@@ -5958,7 +6076,7 @@ bool RenderLayer::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect)
     // FIXME: Remove this check.
     // This function should not be called when layer-lists are dirty.
     // It is somehow getting triggered during style update.
-    if (m_zOrderListsDirty || m_normalFlowListDirty)
+    if (zOrderListsDirty() || normalFlowListDirty())
         return false;
 
     // Table painting is special; a table paints its sections.
@@ -6002,17 +6120,6 @@ bool RenderLayer::listBackgroundIsKnownToBeOpaqueInRect(const LayerList& list, c
     return false;
 }
 
-void RenderLayer::updateCompositingAndLayerListsIfNeeded()
-{
-    if (compositor().inCompositingMode()) {
-        if (isDirtyStackingContext() || m_normalFlowListDirty)
-            compositor().updateCompositingLayers(CompositingUpdateType::OnHitTest, this);
-        return;
-    }
-
-    updateLayerListsIfNeeded();
-}
-
 void RenderLayer::repaintIncludingDescendants()
 {
     renderer().repaint();
@@ -6064,10 +6171,12 @@ bool RenderLayer::shouldBeSelfPaintingLayer() const
         return true;
 
     return hasOverlayScrollbars()
+        || canUseCompositedScrolling()
         || renderer().isTableRow()
         || renderer().isCanvas()
         || renderer().isVideo()
         || renderer().isEmbeddedObject()
+        || (renderer().isRenderImage() && downcast<RenderImage>(renderer()).isEditableImage())
         || renderer().isRenderIFrame()
         || renderer().isInFlowRenderFragmentedFlow();
 }
@@ -6317,9 +6426,9 @@ void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle* oldStyle
 
     updateFilterPaintingStrategy();
 
-#if PLATFORM(IOS) && ENABLE(TOUCH_EVENTS)
+#if PLATFORM(IOS_FAMILY) && ENABLE(TOUCH_EVENTS)
     if (diff == StyleDifference::RecompositeLayer || diff >= StyleDifference::LayoutPositionedMovementOnly)
-        renderer().document().setTouchEventRegionsNeedUpdate();
+        renderer().document().invalidateRenderingDependentRegions();
 #else
     UNUSED_PARAM(diff);
 #endif
@@ -6350,7 +6459,7 @@ void RenderLayer::updateScrollableAreaSet(bool hasOverflow)
 
 #if ENABLE(IOS_TOUCH_EVENTS)
     if (addedOrRemoved) {
-        if (isScrollable && !canUseAcceleratedTouchScrolling())
+        if (isScrollable && !canUseCompositedScrolling())
             registerAsTouchEventListenerForScrolling();
         else {
             // We only need the touch listener for unaccelerated overflow scrolling, so if we became
@@ -6422,6 +6531,11 @@ 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);
@@ -6534,11 +6648,47 @@ void RenderLayer::updateFilterPaintingStrategy()
 void RenderLayer::filterNeedsRepaint()
 {
     // We use the enclosing element so that we recalculate style for the ancestor of an anonymous object.
-    if (Element* element = enclosingElement())
+    if (Element* element = enclosingElement()) {
+        // FIXME: This really shouldn't have to invalidate layer composition, but tests like css3/filters/effect-reference-delete.html fail if that doesn't happen.
         element->invalidateStyleAndLayerComposition();
+    }
     renderer().repaint();
 }
 
+bool RenderLayer::isTransparentOrFullyClippedRespectingParentFrames() const
+{
+    static const double minimumVisibleOpacity = 0.01;
+
+    float currentOpacity = 1;
+    for (auto* layer = this; layer; layer = parentLayerCrossFrame(*layer)) {
+        currentOpacity *= layer->renderer().style().opacity();
+        if (currentOpacity < minimumVisibleOpacity)
+            return true;
+    }
+
+    RenderLayer* enclosingClipLayer = nullptr;
+    for (auto* layer = this; layer; layer = enclosingClipLayer ? enclosingClipLayer->parent() : enclosingFrameRenderLayer(*layer)) {
+        enclosingClipLayer = layer->enclosingOverflowClipLayer(IncludeSelfOrNot::IncludeSelf);
+        if (!enclosingClipLayer)
+            continue;
+
+        LayoutRect layerBounds;
+        ClipRect backgroundRect;
+        ClipRect foregroundRect;
+        layer->calculateRects({ enclosingClipLayer, TemporaryClipRects }, LayoutRect::infiniteRect(), layerBounds, backgroundRect, foregroundRect, layer->offsetFromAncestor(enclosingClipLayer));
+        if (backgroundRect.isEmpty())
+            return true;
+    }
+
+    return false;
+}
+
+void RenderLayer::invalidateEventRegion()
+{
+    if (auto* compositingLayer = enclosingCompositingLayerForRepaint())
+        compositingLayer->setNeedsCompositingConfigurationUpdate();
+}
+
 TextStream& operator<<(TextStream& ts, const RenderLayer& layer)
 {
     ts << "RenderLayer " << &layer << " " << layer.size();
@@ -6577,4 +6727,131 @@ void showLayerTree(const WebCore::RenderObject* renderer)
     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"
+        "Traversal needs: requirements (t)raversal on descendants, (b)acking or hierarchy traversal on descendants, (r)equirements traversal on all descendants, requirements traversal on all (s)ubsequent layers, (h)ierarchy traversal on all descendants, update of paint (o)rder children\n"
+        "Update needs:    post-(l)ayout requirements, (g)eometry, (k)ids geometry, (c)onfig, layer conne(x)ion, (s)crolling tree\n";
+    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 << (layer.needsCompositingPaintOrderChildrenUpdate() ? "o" : "-");
+
+    stream << " ";
+
+    stream << (layer.needsPostLayoutCompositingUpdate() ? "l" : "-");
+    stream << (layer.needsCompositingGeometryUpdate() ? "g" : "-");
+    stream << (layer.childrenNeedCompositingGeometryUpdate() ? "k" : "-");
+    stream << (layer.needsCompositingConfigurationUpdate() ? "c" : "-");
+    stream << (layer.needsCompositingLayerConnection() ? "x" : "-");
+    stream << (layer.needsScrollingTreeUpdate() ? "s" : "-");
+
+    stream << " ";
+
+    outputIdent(stream, depth);
+
+    stream << prefix;
+
+    auto layerRect = layer.rect();
+
+    stream << &layer << " " << layerRect;
+    if (layer.isComposited()) {
+        auto& backing = *layer.backing();
+        stream << " (layerID " << backing.graphicsLayer()->primaryLayerID() << ")";
+
+        auto scrollingNodeID = backing.scrollingNodeIDForRole(WebCore::ScrollCoordinationRole::Scrolling);
+        auto frameHostingNodeID = backing.scrollingNodeIDForRole(WebCore::ScrollCoordinationRole::FrameHosting);
+        auto viewportConstrainedNodeID = backing.scrollingNodeIDForRole(WebCore::ScrollCoordinationRole::ViewportConstrained);
+        auto positionedNodeID = backing.scrollingNodeIDForRole(WebCore::ScrollCoordinationRole::Positioning);
+
+        if (scrollingNodeID || frameHostingNodeID || viewportConstrainedNodeID || positionedNodeID) {
+            stream << " {";
+            bool first = true;
+            if (scrollingNodeID) {
+                stream << "sc " << scrollingNodeID;
+                first = false;
+            }
+
+            if (frameHostingNodeID) {
+                if (!first)
+                    stream << ", ";
+                stream << "fh " << frameHostingNodeID;
+                first = false;
+            }
+
+            if (viewportConstrainedNodeID) {
+                if (!first)
+                    stream << ", ";
+                stream << "vc " << viewportConstrainedNodeID;
+            }
+
+            if (positionedNodeID) {
+                if (!first)
+                    stream << ", ";
+                stream << "pos " << positionedNodeID;
+            }
+
+            stream << "}";
+        }
+    }
+    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