Repository: WebKit/WebKit
Pull request #45730: View transitions don't capture composited filters in the old snapshot.
Author: login: mattwoodrow
Labels: name: New Bugs
description: Unclassified bugs are placed in this component until the correct component can be determined.
Created: 2025-05-21T23:00:13Z | Merged: 2025-05-23T19:48:46Z

#### a73751a1a8cbe720473262dc7b04fb874aff3e8e
<pre>
View transitions don&apos;t capture composited filters in the old snapshot.
<a href="https://bugs.webkit.org/show_bug.cgi?id=293393">https://bugs.webkit.org/show_bug.cgi?id=293393</a>
&lt;<a href="https://rdar.apple.com/151771464">rdar://151771464</a>&gt;

Reviewed by Said Abou-Hallawa.

RenderLayer::paintsWithFilter needs to take
PaintBehavior::FlattenCompositingLayers into account for the filters to be
painted into the snapshot.

RenderLayer::m_filters is retained state that depends on paintsWithFilter, so
make sure we initialize it when needed for snapshot painting (and clear again
afterwards if necessary).

* LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/inline-child-with-composited-filter-expected.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/inline-child-with-composited-filter-ref.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/inline-child-with-composited-filter.html: Added.
* Source/WebCore/rendering/RenderLayer.cpp:
(WebCore::RenderLayer::paintsWithFilters const):
(WebCore::RenderLayer::hasFiltersForPainting const):
(WebCore::RenderLayer::filtersForPainting):
(WebCore::RenderLayer::setupFilters):
(WebCore::RenderLayer::paintLayerContents):
(WebCore::RenderLayer::calculateClipRects const):
(WebCore::RenderLayer::filtersForPainting const): Deleted.
* Source/WebCore/rendering/RenderLayer.h:

Canonical link: <a href="https://commits.webkit.org/295357@main">https://commits.webkit.org/295357@main</a>
</pre>
<!--EWS-Status-Bubble-Start-->
https://github.com/WebKit/WebKit/commit/79280ecb44eb7d4ce0920cb448e65d974adee71f

| Misc | iOS, visionOS, tvOS & watchOS  | macOS  | Linux |  Windows |
| ----- | ---------------------- | ------- |  ----- |  --------- |
| [✅ 🧪 style](https://ews-build.webkit.org/#/builders/38/builds/104774 "Passed style check") | [✅ 🛠 ios](https://ews-build.webkit.org/#/builders/131/builds/24486 "Built successfully") | [✅ 🛠 mac](https://ews-build.webkit.org/#/builders/138/builds/14908 "Built successfully") | [✅ 🛠 wpe](https://ews-build.webkit.org/#/builders/5/builds/109988 "Built successfully") | [✅ 🛠 win](https://ews-build.webkit.org/#/builders/59/builds/55448 "Built successfully") 
| [✅ 🧪 bindings](https://ews-build.webkit.org/#/builders/9/builds/106814 "Passed tests") | [✅ 🛠 ios-sim](https://ews-build.webkit.org/#/builders/130/builds/24877 "Built successfully") | [✅ 🛠 mac-AS-debug](https://ews-build.webkit.org/#/builders/123/builds/33032 "Built successfully") | [✅ 🧪 wpe-wk2](https://ews-build.webkit.org/#/builders/34/builds/79555 "Passed tests") | [⏳ 🧪 win-tests](https://ews-build.webkit.org/#/builders/Win-Tests-EWS "Waiting to run tests") 
| [✅ 🧪 webkitperl](https://ews-build.webkit.org/#/builders/11/builds/107780 "Passed tests") | [✅ 🧪 ios-wk2](https://ews-build.webkit.org/#/builders/132/builds/19340 "Passed tests") | [✅ 🧪 api-mac](https://ews-build.webkit.org/#/builders/18/builds/94555 "Passed tests") | [✅ 🧪 api-wpe](https://ews-build.webkit.org/#/builders/41/builds/59862 "Passed tests") | 
| | [✅ 🧪 ios-wk2-wpt](https://ews-build.webkit.org/#/builders/133/builds/19099 "Passed tests") | [✅ 🧪 mac-wk1](https://ews-build.webkit.org/#/builders/135/builds/12632 "Passed tests") | [✅ 🛠 wpe-cairo](https://ews-build.webkit.org/#/builders/65/builds/54830 "Built successfully") | 
| | [✅ 🧪 api-ios](https://ews-build.webkit.org/#/builders/13/builds/88796 "Passed tests") | [✅ 🧪 mac-wk2](https://ews-build.webkit.org/#/builders/136/builds/12679 "Passed tests") | [✅ 🛠 gtk](https://ews-build.webkit.org/#/builders/2/builds/112390 "Built successfully") | 
| | [✅ 🛠 vision](https://ews-build.webkit.org/#/builders/128/builds/31939 "Built successfully") | [✅ 🧪 mac-AS-debug-wk2](https://ews-build.webkit.org/#/builders/122/builds/23485 "Passed tests") | [✅ 🧪 gtk-wk2](https://ews-build.webkit.org/#/builders/1/builds/88634 "Passed tests") | 
| | [✅ 🛠 vision-sim](https://ews-build.webkit.org/#/builders/121/builds/32303 "Built successfully") | [✅ 🧪 mac-wk2-stress](https://ews-build.webkit.org/#/builders/8/builds/90781 "Passed tests") | [✅ 🧪 api-gtk](https://ews-build.webkit.org/#/builders/21/builds/88258 "Passed tests") | 
| [✅ 🛠 🧪 merge](https://ews-build.webkit.org/#/builders/19/builds/22510 "Built successfully and passed tests") | [✅ 🧪 vision-wk2](https://ews-build.webkit.org/#/builders/126/builds/33150 "Passed tests") | [✅ 🧪 mac-intel-wk2](https://ews-build.webkit.org/#/builders/137/builds/10919 "Passed tests") | [✅ 🛠 playstation](https://ews-build.webkit.org/#/builders/134/builds/27252 "Built successfully") | 
| | [✅ 🛠 tv](https://ews-build.webkit.org/#/builders/127/builds/31864 "Built successfully") | [✅ 🛠 mac-safer-cpp](https://ews-build.webkit.org/#/builders/120/builds/37218 "Built successfully") | | 
| | [✅ 🛠 tv-sim](https://ews-build.webkit.org/#/builders/125/builds/31656 "Built successfully") | | | 
| | [✅ 🛠 watch](https://ews-build.webkit.org/#/builders/129/builds/34997 "Built successfully") | | | 
| | [✅ 🛠 watch-sim](https://ews-build.webkit.org/#/builders/124/builds/33215 "Built successfully") | | | 
<!--EWS-Status-Bubble-End-->


Diff:
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/inline-child-with-composited-filter-expected.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/inline-child-with-composited-filter-expected.html
new file mode 100644
index 000000000000..9dd26f2c3b1a
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/inline-child-with-composited-filter-expected.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>View transitions: inline child with filter (ref)</title>
+<link rel="help" href="https://www.w3.org/TR/css-view-transitions-1/">
+<link rel="author" href="mailto:vmpstr@chromium.org">
+<link rel="author" href="mailto:mattwoodrow@apple.com">
+
+<style>
+body { margin : 0; }
+#target {
+  width: 100px;
+  height: 100px;
+  background-color: grey;
+  overflow-clip-margin: 40px;
+  contain: paint;
+  view-transition-name: target;
+}
+
+#child {
+  position: relative;
+  left: 100px;
+  top: 100px;
+  color: lightgreen;
+  background-color: darkgreen;
+  filter: blur(30px);
+  transform: translateZ(0px);
+}
+</style>
+
+<div id="target">
+  <span id="child">INLINEBOX</span>
+</div>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/inline-child-with-composited-filter-ref.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/inline-child-with-composited-filter-ref.html
new file mode 100644
index 000000000000..9dd26f2c3b1a
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/inline-child-with-composited-filter-ref.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>View transitions: inline child with filter (ref)</title>
+<link rel="help" href="https://www.w3.org/TR/css-view-transitions-1/">
+<link rel="author" href="mailto:vmpstr@chromium.org">
+<link rel="author" href="mailto:mattwoodrow@apple.com">
+
+<style>
+body { margin : 0; }
+#target {
+  width: 100px;
+  height: 100px;
+  background-color: grey;
+  overflow-clip-margin: 40px;
+  contain: paint;
+  view-transition-name: target;
+}
+
+#child {
+  position: relative;
+  left: 100px;
+  top: 100px;
+  color: lightgreen;
+  background-color: darkgreen;
+  filter: blur(30px);
+  transform: translateZ(0px);
+}
+</style>
+
+<div id="target">
+  <span id="child">INLINEBOX</span>
+</div>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/inline-child-with-composited-filter.html b/LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/inline-child-with-composited-filter.html
new file mode 100644
index 000000000000..36ba9803e1ed
--- /dev/null
+++ b/LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/inline-child-with-composited-filter.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<title>View transitions: inline child with filter</title>
+<link rel="help" href="https://www.w3.org/TR/css-view-transitions-1/">
+<link rel="author" href="mailto:vmpstr@chromium.org">
+<link rel="author" href="mailto:mattwoodrow@apple.com">
+<link rel="match" href="inline-child-with-filter-ref.html">
+<meta name=fuzzy content="maxDifference=0-2;totalPixels=0-2400">
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+body { margin : 0; }
+#target {
+  width: 100px;
+  height: 100px;
+  background-color: grey;
+  overflow-clip-margin: 40px;
+  contain: paint;
+  view-transition-name: target;
+}
+
+#child {
+  position: relative;
+  left: 100px;
+  top: 100px;
+  color: lightgreen;
+  background-color: darkgreen;
+  filter: blur(30px);
+  transform: translateZ(0px);
+}
+
+html::view-transition-group(root) { animation-duration: 300s; }
+html::view-transition-old(target) {
+  animation: unset;
+  opacity: 1;
+}
+html::view-transition-new(target) {
+  animation: unset;
+  opacity: 0;
+}
+</style>
+
+<div id="target">
+  <span id="child">INLINEBOX</span>
+</div>
+
+<script>
+failIfNot(document.startViewTransition, "Missing document.startViewTransition");
+
+async function runTest() {
+  let transition = document.startViewTransition(async () => {
+    document.getElementById("target").remove();
+  });
+  transition.ready.then(() => requestAnimationFrame(takeScreenshot));
+}
+onload = () => requestAnimationFrame(() => requestAnimationFrame(runTest));
+</script>
diff --git a/Source/WebCore/rendering/RenderElement.cpp b/Source/WebCore/rendering/RenderElement.cpp
index 4b8890ac8492..2414f5a3cf85 100644
--- a/Source/WebCore/rendering/RenderElement.cpp
+++ b/Source/WebCore/rendering/RenderElement.cpp
@@ -315,7 +315,7 @@ StyleDifference RenderElement::adjustStyleDifference(StyleDifference diff, Optio
     
     if ((contextSensitiveProperties & StyleDifferenceContextSensitiveProperty::Filter) && hasLayer()) {
         auto& layer = *downcast<RenderLayerModelObject>(*this).layer();
-        if (!layer.isComposited() || layer.paintsWithFilters())
+        if (!layer.isComposited() || layer.shouldPaintWithFilters())
             diff = std::max(diff, StyleDifference::RepaintLayer);
         else
             diff = std::max(diff, StyleDifference::RecompositeLayer);
diff --git a/Source/WebCore/rendering/RenderLayer.cpp b/Source/WebCore/rendering/RenderLayer.cpp
index efc5d8b741fc..fd640c71b0f5 100644
--- a/Source/WebCore/rendering/RenderLayer.cpp
+++ b/Source/WebCore/rendering/RenderLayer.cpp
@@ -1007,7 +1007,7 @@ bool RenderLayer::canRender3DTransforms() const
     return compositor().canRender3DTransforms();
 }
 
-bool RenderLayer::paintsWithFilters() const
+bool RenderLayer::shouldPaintWithFilters(OptionSet<PaintBehavior> paintBehavior) const
 {
     const auto& filter = renderer().style().filter();
     if (filter.isEmpty())
@@ -1019,15 +1019,18 @@ bool RenderLayer::paintsWithFilters() const
     if (RenderLayerFilters::isIdentity(renderer()))
         return false;
 
-    if (!isComposited())
+    if (paintBehavior & PaintBehavior::FlattenCompositingLayers)
         return true;
 
-    return !m_backing->canCompositeFilters();
+    if (isComposited() && m_backing->canCompositeFilters())
+        return false;
+
+    return true;
 }
 
 bool RenderLayer::requiresFullLayerImageForFilters() const
 {
-    if (!paintsWithFilters())
+    if (!shouldPaintWithFilters())
         return false;
 
     return m_filters && m_filters->hasFilterThatMovesPixels();
@@ -2383,7 +2386,7 @@ bool RenderLayer::shouldRepaintAfterLayout() const
     // SVG container sizes/positions are only ever determined by their children, so they will
     // change as a reaction on a re-position/re-sizing of the children - which already properly
     // trigger repaints.
-    if (is<RenderSVGContainer>(renderer()) && !paintsWithFilters())
+    if (is<RenderSVGContainer>(renderer()) && !shouldPaintWithFilters())
         return false;
 
     if (m_repaintStatus == RepaintStatus::NeedsNormalRepaint || m_repaintStatus == RepaintStatus::NeedsFullRepaint)
@@ -2516,7 +2519,7 @@ void RenderLayer::setFilterBackendNeedsRepaintingInRect(const LayoutRect& rect)
         parentLayerRect = renderer().localToContainerQuad(repaintQuad, &parentLayer->renderer()).enclosingBoundingBox();
     }
 
-    if (parentLayer->paintsWithFilters()) {
+    if (parentLayer->shouldPaintWithFilters()) {
         parentLayer->setFilterBackendNeedsRepaintingInRect(parentLayerRect);
         return;        
     }
@@ -3569,26 +3572,31 @@ void RenderLayer::clearLayerClipPath()
         svgClipper->removeClientFromCache(renderer());
 }
 
-RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags) const
+bool RenderLayer::shouldHaveFiltersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior) const
 {
     if (context.paintingDisabled())
-        return nullptr;
+        return false;
 
     if (paintFlags & PaintLayerFlag::PaintingOverlayScrollbars)
-        return nullptr;
+        return false;
 
-    if (!paintsWithFilters())
-        return nullptr;
+    if (!shouldPaintWithFilters(paintBehavior))
+        return false;
 
-    if (m_filters)
-        return m_filters.get();
+    return true;
+}
 
-    return nullptr;
+RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior)
+{
+    if (!shouldHaveFiltersForPainting(context, paintFlags, paintBehavior))
+        return nullptr;
+
+    return &ensureLayerFilters();
 }
 
 GraphicsContext* RenderLayer::setupFilters(GraphicsContext& destinationContext, LayerPaintingInfo& paintingInfo, OptionSet<PaintLayerFlag> paintFlags, const LayoutSize& offsetFromRoot, const ClipRect& backgroundRect)
 {
-    auto* paintingFilters = filtersForPainting(destinationContext, paintFlags);
+    auto* paintingFilters = filtersForPainting(destinationContext, paintFlags, paintingInfo.paintBehavior);
     if (!paintingFilters)
         return nullptr;
 
@@ -3751,7 +3759,7 @@ void RenderLayer::paintLayerContents(GraphicsContext& context, const LayerPainti
 
     // Apply clip-path to context.
     LayoutSize columnAwareOffsetFromRoot = offsetFromRoot;
-    if (renderer().enclosingFragmentedFlow() && (renderer().hasClipPath() || filtersForPainting(context, paintFlags)))
+    if (renderer().enclosingFragmentedFlow() && (renderer().hasClipPath() || shouldHaveFiltersForPainting(context, paintFlags, paintingInfo.paintBehavior)))
         columnAwareOffsetFromRoot = toLayoutSize(convertToLayerCoords(paintingInfo.rootLayer, LayoutPoint(), AdjustForColumns));
 
     GraphicsContextStateSaver stateSaver(context, false);
@@ -3809,7 +3817,7 @@ void RenderLayer::paintLayerContents(GraphicsContext& context, const LayerPainti
     { // Scope for filter-related state changes.
         ClipRect backgroundRect;
 
-        if (filtersForPainting(context, paintFlags)) {
+        if (shouldHaveFiltersForPainting(context, paintFlags, paintBehavior)) {
             // When we called collectFragments() last time, paintDirtyRect was reset to represent the filter bounds.
             // Now we need to compute the backgroundRect uncontaminated by filters, in order to clip the filtered result.
             // Note that we also use paintingInfo here, not localPaintingInfo which filters also contaminated.
@@ -3898,8 +3906,12 @@ void RenderLayer::paintLayerContents(GraphicsContext& context, const LayerPainti
                 paintOverflowControlsForFragments(layerFragments, currentContext, localPaintingInfo);
         }
 
-        if (filterContext)
+        if (filterContext) {
             applyFilters(context, paintingInfo, paintBehavior, backgroundRect);
+            // Painting a snapshot might have temporarily overriden the filter painting strategy,
+            // make sure it gets reset.
+            updateFilterPaintingStrategy();
+        }
     }
     
     if (shouldPaintContent && !(selectionOnly || selectionAndBackgroundsOnly)) {
@@ -5642,7 +5654,7 @@ LayoutRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, c
     for (auto* childLayer : normalFlowLayers())
         computeLayersUnion(*childLayer);
 
-    if (flags.contains(IncludeFilterOutsets) || (flags.contains(IncludePaintedFilterOutsets) && paintsWithFilters()))
+    if (flags.contains(IncludeFilterOutsets) || (flags.contains(IncludePaintedFilterOutsets) && shouldPaintWithFilters(flags & IncludeCompositedDescendants ? PaintBehavior::FlattenCompositingLayers : OptionSet<PaintBehavior>())))
         unionBounds.expand(toLayoutBoxExtent(filterOutsets()));
 
     if ((flags & IncludeSelfTransform) && paintsWithTransform(PaintBehavior::Normal)) {
@@ -5757,7 +5769,7 @@ bool RenderLayer::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect)
     if (renderer().style().usedVisibility() != Visibility::Visible)
         return false;
 
-    if (paintsWithFilters() && renderer().style().filter().hasFilterThatAffectsOpacity())
+    if (shouldPaintWithFilters() && renderer().style().filter().hasFilterThatAffectsOpacity())
         return false;
 
     // FIXME: Handle simple transforms.
@@ -6274,12 +6286,15 @@ RenderStyle RenderLayer::createReflectionStyle()
     return newStyle;
 }
 
-void RenderLayer::ensureLayerFilters()
+RenderLayerFilters& RenderLayer::ensureLayerFilters()
 {
     if (m_filters)
-        return;
+        return *m_filters;
     
     m_filters = makeUnique<RenderLayerFilters>(*this);
+    m_filters->setPreferredFilterRenderingModes(renderer().page().preferredFilterRenderingModes());
+    m_filters->setFilterScale({ page().deviceScaleFactor(), page().deviceScaleFactor() });
+    return *m_filters;
 }
 
 void RenderLayer::clearLayerFilters()
@@ -6314,10 +6329,9 @@ void RenderLayer::clearLayerScrollableArea()
 
 void RenderLayer::updateFiltersAfterStyleChange(StyleDifference diff, const RenderStyle* oldStyle)
 {
-    if (renderer().style().filter().hasReferenceFilter()) {
-        ensureLayerFilters();
-        m_filters->updateReferenceFilterClients(renderer().style().filter());
-    } else if (!paintsWithFilters())
+    if (renderer().style().filter().hasReferenceFilter())
+        ensureLayerFilters().updateReferenceFilterClients(renderer().style().filter());
+    else if (!shouldPaintWithFilters())
         clearLayerFilters();
     else if (m_filters)
         m_filters->removeReferenceFilterClients();
@@ -6367,7 +6381,7 @@ void RenderLayer::updateFilterPaintingStrategy()
     // RenderLayerFilters is only used to render the filters in software mode,
     // so we always need to run updateFilterPaintingStrategy() after the composited
     // mode might have changed for this layer.
-    if (!paintsWithFilters()) {
+    if (!shouldPaintWithFilters()) {
         // Don't delete the whole filter info here, because we might use it
         // for loading SVG reference filter files.
         if (m_filters)
@@ -6381,8 +6395,6 @@ void RenderLayer::updateFilterPaintingStrategy()
     }
 
     ensureLayerFilters();
-    m_filters->setPreferredFilterRenderingModes(renderer().page().preferredFilterRenderingModes());
-    m_filters->setFilterScale({ page().deviceScaleFactor(), page().deviceScaleFactor() });
 }
 
 IntOutsets RenderLayer::filterOutsets() const
diff --git a/Source/WebCore/rendering/RenderLayer.h b/Source/WebCore/rendering/RenderLayer.h
index fc50819ee176..528b23f62af6 100644
--- a/Source/WebCore/rendering/RenderLayer.h
+++ b/Source/WebCore/rendering/RenderLayer.h
@@ -905,7 +905,7 @@ class RenderLayer final : public CanMakeSingleThreadWeakPtr<RenderLayer>, public
     // The query rect is given in local coordinates.
     bool backgroundIsKnownToBeOpaqueInRect(const LayoutRect&) const;
 
-    bool paintsWithFilters() const;
+    bool shouldPaintWithFilters(OptionSet<PaintBehavior> = { }) const;
     bool requiresFullLayerImageForFilters() const;
 
     Element* enclosingElement() const;
@@ -1172,13 +1172,14 @@ class RenderLayer final : public CanMakeSingleThreadWeakPtr<RenderLayer>, public
     void setupClipPath(GraphicsContext&, GraphicsContextStateSaver&, RegionContextStateSaver&, const LayerPaintingInfo&, OptionSet<PaintLayerFlag>&, const LayoutSize& offsetFromRoot);
     void clearLayerClipPath();
 
-    void ensureLayerFilters();
+    RenderLayerFilters& ensureLayerFilters();
     void clearLayerFilters();
 
     void updateLayerScrollableArea();
     void clearLayerScrollableArea();
 
-    RenderLayerFilters* filtersForPainting(GraphicsContext&, OptionSet<PaintLayerFlag>) const;
+    bool shouldHaveFiltersForPainting(GraphicsContext&, OptionSet<PaintLayerFlag>, OptionSet<PaintBehavior>) const;
+    RenderLayerFilters* filtersForPainting(GraphicsContext&, OptionSet<PaintLayerFlag>, OptionSet<PaintBehavior>);
     GraphicsContext* setupFilters(GraphicsContext& destinationContext, LayerPaintingInfo&, OptionSet<PaintLayerFlag>, const LayoutSize& offsetFromRoot, const ClipRect& backgroundRect);
     void applyFilters(GraphicsContext& originalContext, const LayerPaintingInfo&, OptionSet<PaintBehavior>, const ClipRect& backgroundRect);
 
diff --git a/Source/WebCore/rendering/RenderLayerBacking.cpp b/Source/WebCore/rendering/RenderLayerBacking.cpp
index 482b9a267c6c..79af84eae423 100644
--- a/Source/WebCore/rendering/RenderLayerBacking.cpp
+++ b/Source/WebCore/rendering/RenderLayerBacking.cpp
@@ -3322,7 +3322,7 @@ bool RenderLayerBacking::containsPaintedContent(PaintedContentsInfo& contentsInf
 // that require painting. Direct compositing saves backing store.
 bool RenderLayerBacking::isDirectlyCompositedImage() const
 {
-    if (m_owningLayer.hasVisibleBoxDecorationsOrBackground() || m_owningLayer.paintsWithFilters() || renderer().hasClip())
+    if (m_owningLayer.hasVisibleBoxDecorationsOrBackground() || m_owningLayer.shouldPaintWithFilters() || renderer().hasClip())
         return false;
 
     // Fixed layers that allow detaching won't have a backing store,


Reviews:
user: login: shallawa
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T17:25:21Z
user: login: mattwoodrow
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T23:46:54Z
user: login: shallawa
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T01:35:25Z
user: login: mattwoodrow
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T01:37:28Z
user: login: mattwoodrow
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T02:15:06Z

Review comments:
diff_hunk: @@ -3569,17 +3572,27 @@ void RenderLayer::clearLayerClipPath()
         svgClipper->removeClientFromCache(renderer());
 }
 
-RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags) const
+bool RenderLayer::hasFiltersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior) const
 {
     if (context.paintingDisabled())
-        return nullptr;
+        return false;
 
     if (paintFlags & PaintLayerFlag::PaintingOverlayScrollbars)
-        return nullptr;
+        return false;
 
-    if (!paintsWithFilters())
+    if (!paintsWithFilters(paintBehavior))
+        return false;
+
+    return true;
+}
+
+RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior)
+{
+    if (!hasFiltersForPainting(context, paintFlags, paintBehavior))
         return nullptr;
 
+    updateFilterPaintingStrategy(paintBehavior);
path: Source/WebCore/rendering/RenderLayer.cpp
user: login: shallawa
body: `updateFilterPaintingStrategy()` will call `paintsWithFilters(paintBehavior)` which will return true because `hasFiltersForPainting ()` already returns true. Can't we refactor `updateFilterPaintingStrategy()` such that we do not call `paintsWithFilters(paintBehavior)` twice?
created_at: 2025-05-22T15:26:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3569,17 +3572,27 @@ void RenderLayer::clearLayerClipPath()
         svgClipper->removeClientFromCache(renderer());
 }
 
-RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags) const
+bool RenderLayer::hasFiltersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior) const
path: Source/WebCore/rendering/RenderLayer.cpp
user: login: shallawa
body: Now we have two functions answering almost the same question and the difference between them is very subtle: `hasFiltersForPainting()` and `paintsWithFilters()`. Can we rename them such that the names reflect exactly what they return?
created_at: 2025-05-22T15:36:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1022,6 +1022,9 @@ bool RenderLayer::paintsWithFilters() const
     if (!isComposited())
         return true;
 
+    if (paintBehavior & PaintBehavior::FlattenCompositingLayers)
+        return true;
+
     return !m_backing->canCompositeFilters();
path: Source/WebCore/rendering/RenderLayer.cpp
user: login: shallawa
body: The original code was a little bit hard to understand and this new condition makes it even harder. Does `paintBehavior` have to have `PaintBehavior::FlattenCompositingLayers` in addition to `isComposited()` true? Before your change I think the following code should be clearer:

```
if (isComposited() && m_backing->canCompositeFilters())
	return false;

return true;
```
Can your new condition fit with change?
created_at: 2025-05-22T16:10:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -5639,7 +5656,7 @@ LayoutRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, c
     for (auto* childLayer : normalFlowLayers())
         computeLayersUnion(*childLayer);
 
-    if (flags.contains(IncludeFilterOutsets) || (flags.contains(IncludePaintedFilterOutsets) && paintsWithFilters()))
+    if (flags.contains(IncludeFilterOutsets) || (flags.contains(IncludePaintedFilterOutsets) && (paintsWithFilters() || (flags & IncludeCompositedDescendants))))
path: Source/WebCore/rendering/RenderLayer.cpp
user: login: shallawa
body: ```
... paintsWithFilters(flags & IncludeCompositedDescendants ? PaintBehavior::FlattenCompositingLayers : OptionSet<PaintBehavior>()))
```
created_at: 2025-05-22T16:52:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3569,17 +3572,27 @@ void RenderLayer::clearLayerClipPath()
         svgClipper->removeClientFromCache(renderer());
 }
 
-RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags) const
+bool RenderLayer::hasFiltersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior) const
path: Source/WebCore/rendering/RenderLayer.cpp
user: login: shallawa
body: How about these names?
```
RenderLayer::shouldHaveFilters(OptionSet<PaintBehavior> paintBehavior)
RenderLayer::shouldHaveFiltersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior)
```
created_at: 2025-05-22T17:05:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3569,17 +3572,27 @@ void RenderLayer::clearLayerClipPath()
         svgClipper->removeClientFromCache(renderer());
 }
 
-RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags) const
+bool RenderLayer::hasFiltersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior) const
 {
     if (context.paintingDisabled())
-        return nullptr;
+        return false;
 
     if (paintFlags & PaintLayerFlag::PaintingOverlayScrollbars)
-        return nullptr;
+        return false;
 
-    if (!paintsWithFilters())
+    if (!paintsWithFilters(paintBehavior))
+        return false;
+
+    return true;
+}
+
+RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior)
+{
+    if (!hasFiltersForPainting(context, paintFlags, paintBehavior))
         return nullptr;
 
+    updateFilterPaintingStrategy(paintBehavior);
path: Source/WebCore/rendering/RenderLayer.cpp
user: login: shallawa
body: I would suggest moving these two lines to `ensureLayerFilters()`
```
    m_filters->setPreferredFilterRenderingModes(renderer().page().preferredFilterRenderingModes());
    m_filters->setFilterScale({ page().deviceScaleFactor(), page().deviceScaleFactor() });
```
So this function can call `ensureLayerFilters()` instead of calling `updateFilterPaintingStrategy()`.
created_at: 2025-05-22T17:12:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3569,17 +3572,27 @@ void RenderLayer::clearLayerClipPath()
         svgClipper->removeClientFromCache(renderer());
 }
 
-RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags) const
+bool RenderLayer::hasFiltersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior) const
 {
     if (context.paintingDisabled())
-        return nullptr;
+        return false;
 
     if (paintFlags & PaintLayerFlag::PaintingOverlayScrollbars)
-        return nullptr;
+        return false;
 
-    if (!paintsWithFilters())
+    if (!paintsWithFilters(paintBehavior))
+        return false;
+
+    return true;
+}
+
+RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior)
+{
+    if (!hasFiltersForPainting(context, paintFlags, paintBehavior))
         return nullptr;
 
+    updateFilterPaintingStrategy(paintBehavior);
+
     if (m_filters)
         return m_filters.get();
 
path: Source/WebCore/rendering/RenderLayer.cpp
user: login: shallawa
body: Not new but 

```
    if (m_filters)
        return m_filters.get();
    return nullptr;
````
can be replaced by
```
    return m_filters.get();
```
created_at: 2025-05-22T17:18:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3569,17 +3572,27 @@ void RenderLayer::clearLayerClipPath()
         svgClipper->removeClientFromCache(renderer());
 }
 
-RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags) const
+bool RenderLayer::hasFiltersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior) const
 {
     if (context.paintingDisabled())
-        return nullptr;
+        return false;
 
     if (paintFlags & PaintLayerFlag::PaintingOverlayScrollbars)
-        return nullptr;
+        return false;
 
-    if (!paintsWithFilters())
+    if (!paintsWithFilters(paintBehavior))
+        return false;
+
+    return true;
+}
+
+RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior)
+{
+    if (!hasFiltersForPainting(context, paintFlags, paintBehavior))
         return nullptr;
 
+    updateFilterPaintingStrategy(paintBehavior);
+
     if (m_filters)
         return m_filters.get();
 
path: Source/WebCore/rendering/RenderLayer.cpp
user: login: shallawa
body: Another suggestion:

```
RenderLayerFilters& RenderLayer::ensureLayerFilters()
{
    if (m_filters)
        return *m_filters;
    
    m_filters = makeUnique<RenderLayerFilters>(*this);
    m_filters->setPreferredFilterRenderingModes(renderer().page().preferredFilterRenderingModes());
    m_filters->setFilterScale({ page().deviceScaleFactor(), page().deviceScaleFactor() });
    return *m_filters;
}

RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior)
{
    if (!shouldHaveFiltersForPainting(context, paintFlags, paintBehavior))
        return nullptr;

    return &ensureLayerFilters();
}
````
created_at: 2025-05-22T17:25:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3569,17 +3572,27 @@ void RenderLayer::clearLayerClipPath()
         svgClipper->removeClientFromCache(renderer());
 }
 
-RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags) const
+bool RenderLayer::hasFiltersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior) const
path: Source/WebCore/rendering/RenderLayer.cpp
user: login: mattwoodrow
body: I think keeping the 'painting' bit of the name is important, since is distinct from having filters but using them as a property on a compositing layer.
created_at: 2025-05-22T23:46:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3569,26 +3572,31 @@ void RenderLayer::clearLayerClipPath()
         svgClipper->removeClientFromCache(renderer());
 }
 
-RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags) const
+bool RenderLayer::hasFiltersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior) const
 {
     if (context.paintingDisabled())
-        return nullptr;
+        return false;
 
     if (paintFlags & PaintLayerFlag::PaintingOverlayScrollbars)
-        return nullptr;
+        return false;
 
-    if (!paintsWithFilters())
-        return nullptr;
+    if (!shouldPaintWithFilters(paintBehavior))
+        return false;
 
-    if (m_filters)
-        return m_filters.get();
+    return true;
+}
 
-    return nullptr;
+RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior)
+{
+    if (!hasFiltersForPainting(context, paintFlags, paintBehavior))
path: Source/WebCore/rendering/RenderLayer.cpp
user: login: shallawa
body: What is the question here:
1. Does `RenderLayer` have `RenderLayerFilters` for painting? If yes then `hasFiltersForPainting()` is fine name
2. Should `RenderLayer` have `RenderLayerFilters` for painting? If yes then `hasFiltersForPainting()` is not a matching name

Given that we are calling `ensureLayerFilters()` below, I think `hasFiltersForPainting()` is not a matching name.
created_at: 2025-05-23T00:00:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<title>View transitions: inline child with filter</title>
+<link rel="help" href="https://www.w3.org/TR/css-view-transitions-1/">
+<link rel="author" href="mailto:vmpstr@chromium.org">
path: LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/inline-child-with-composited-filter.html
user: login: shallawa
body: Is this a new test or we just importing it? Do we need to add it to w3c-import.log?
created_at: 2025-05-23T01:35:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<title>View transitions: inline child with filter</title>
+<link rel="help" href="https://www.w3.org/TR/css-view-transitions-1/">
+<link rel="author" href="mailto:vmpstr@chromium.org">
path: LayoutTests/imported/w3c/web-platform-tests/css/css-view-transitions/inline-child-with-composited-filter.html
user: login: mattwoodrow
body: It's a new test, but I left the original author there since it's mostly a duplicate of an existing test (with the addition of forcing compositing the filtered element).
created_at: 2025-05-23T01:37:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3569,26 +3572,31 @@ void RenderLayer::clearLayerClipPath()
         svgClipper->removeClientFromCache(renderer());
 }
 
-RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags) const
+bool RenderLayer::hasFiltersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior) const
 {
     if (context.paintingDisabled())
-        return nullptr;
+        return false;
 
     if (paintFlags & PaintLayerFlag::PaintingOverlayScrollbars)
-        return nullptr;
+        return false;
 
-    if (!paintsWithFilters())
-        return nullptr;
+    if (!shouldPaintWithFilters(paintBehavior))
+        return false;
 
-    if (m_filters)
-        return m_filters.get();
+    return true;
+}
 
-    return nullptr;
+RenderLayerFilters* RenderLayer::filtersForPainting(GraphicsContext& context, OptionSet<PaintLayerFlag> paintFlags, OptionSet<PaintBehavior> paintBehavior)
+{
+    if (!hasFiltersForPainting(context, paintFlags, paintBehavior))
path: Source/WebCore/rendering/RenderLayer.cpp
user: login: mattwoodrow
body: That's fair, changing it to `shouldHaveFiltersForPainting`.
created_at: 2025-05-23T02:15:06Z
author_association: CONTRIBUTOR

PR comments:
user: login: webkit-early-warning-system
created_at: 2025-05-21T23:00:19Z
body: EWS run on previous version of this PR (hash https://github.com/WebKit/WebKit/commit/c1a53f645a23815af72f6ab2eb00027bcb17ec12)<details>

| Misc | iOS, visionOS, tvOS & watchOS  | macOS  | Linux |  Windows |
| ----- | ---------------------- | ------- |  ----- |  --------- |
| [✅ 🧪 style](https://ews-build.webkit.org/#/builders/38/builds/104533 "Passed style check") | [✅ 🛠 ios](https://ews-build.webkit.org/#/builders/131/builds/24243 "Built successfully") | [✅ 🛠 mac](https://ews-build.webkit.org/#/builders/138/builds/14661 "Built successfully") | [✅ 🛠 wpe](https://ews-build.webkit.org/#/builders/5/builds/109741 "Built successfully") | [✅ 🛠 win](https://ews-build.webkit.org/#/builders/59/builds/55204 "Built successfully") 
| [✅ 🧪 bindings](https://ews-build.webkit.org/#/builders/9/builds/106573 "Passed tests") | [✅ 🛠 ios-sim](https://ews-build.webkit.org/#/builders/130/builds/24627 "Built successfully") | [✅ 🛠 mac-AS-debug](https://ews-build.webkit.org/#/builders/123/builds/32787 "Built successfully") | [✅ 🧪 wpe-wk2](https://ews-build.webkit.org/#/builders/34/builds/79364 "Passed tests") | [⏳ 🧪 win-tests](https://ews-build.webkit.org/#/builders/Win-Tests-EWS "Waiting to run tests") 
| [✅ 🧪 webkitperl](https://ews-build.webkit.org/#/builders/11/builds/107539 "Passed tests") | [✅ 🧪 ios-wk2](https://ews-build.webkit.org/#/builders/132/builds/19155 "Passed tests") | [✅ 🧪 api-mac](https://ews-build.webkit.org/#/builders/18/builds/94348 "Passed tests") | [✅ 🧪 api-wpe](https://ews-build.webkit.org/#/builders/41/builds/59690 "Passed tests") | 
| | [💥 🧪 ios-wk2-wpt](https://ews-build.webkit.org/#/builders/133/builds/18931 "An unexpected error occured. Recent messages:Printed configuration") | [✅ 🧪 mac-wk1](https://ews-build.webkit.org/#/builders/135/builds/12420 "Passed tests") | [✅ 🛠 wpe-cairo](https://ews-build.webkit.org/#/builders/65/builds/54575 "Built successfully") | 
| | [✅ 🧪 api-ios](https://ews-build.webkit.org/#/builders/13/builds/88637 "Passed tests") | [✅ 🧪 mac-wk2](https://ews-build.webkit.org/#/builders/136/builds/12475 "Passed tests") | [✅ 🛠 gtk](https://ews-build.webkit.org/#/builders/2/builds/112135 "Built successfully") | 
| | [✅ 🛠 vision](https://ews-build.webkit.org/#/builders/128/builds/31694 "Built successfully") | [✅ 🧪 mac-AS-debug-wk2](https://ews-build.webkit.org/#/builders/122/builds/23337 "Passed tests") | [❌ 🧪 gtk-wk2](https://ews-build.webkit.org/#/builders/1/builds/88412 "Found 1 new test failure: fast/css/view-transitions-nested-transparency-layers.html (failure)") | 
| | [✅ 🛠 vision-sim](https://ews-build.webkit.org/#/builders/121/builds/32058 "Built successfully") | [✅ 🧪 mac-wk2-stress](https://ews-build.webkit.org/#/builders/8/builds/90579 "Passed tests") | [✅ 🧪 api-gtk](https://ews-build.webkit.org/#/builders/21/builds/88082 "Passed tests") | 
| | [✅ 🧪 vision-wk2](https://ews-build.webkit.org/#/builders/126/builds/32975 "Passed tests") | [✅ 🧪 mac-intel-wk2](https://ews-build.webkit.org/#/builders/137/builds/10751 "Passed tests") | [✅ 🛠 playstation](https://ews-build.webkit.org/#/builders/134/builds/27011 "Built successfully") | 
| | [✅ 🛠 tv](https://ews-build.webkit.org/#/builders/127/builds/31621 "Built successfully") | [✅ 🛠 mac-safer-cpp](https://ews-build.webkit.org/#/builders/120/builds/36961 "Built successfully") | | 
| | [✅ 🛠 tv-sim](https://ews-build.webkit.org/#/builders/125/builds/31413 "Built successfully") | | | 
| | [✅ 🛠 watch](https://ews-build.webkit.org/#/builders/129/builds/34752 "Built successfully") | | | 
| | [✅ 🛠 watch-sim](https://ews-build.webkit.org/#/builders/124/builds/32973 "Built successfully") | | | </details>
author_association: COLLABORATOR
user: login: webkit-early-warning-system
created_at: 2025-05-22T23:42:18Z
body: EWS run on previous version of this PR (hash https://github.com/WebKit/WebKit/commit/ce47451c9285beefff14eab27b53d35d276c7170)<details>

| Misc | iOS, visionOS, tvOS & watchOS  | macOS  | Linux |  Windows |
| ----- | ---------------------- | ------- |  ----- |  --------- |
| [✅ 🧪 style](https://ews-build.webkit.org/#/builders/38/builds/104748 "Passed style check") | [✅ 🛠 ios](https://ews-build.webkit.org/#/builders/131/builds/24461 "Built successfully") | [✅ 🛠 mac](https://ews-build.webkit.org/#/builders/138/builds/14883 "Built successfully") | [✅ 🛠 wpe](https://ews-build.webkit.org/#/builders/5/builds/109962 "Built successfully") | [  ~~🛠 win~~](https://ews-build.webkit.org/#/builders/59/builds/55422 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") 
| [✅ 🧪 bindings](https://ews-build.webkit.org/#/builders/9/builds/106788 "Passed tests") | [✅ 🛠 ios-sim](https://ews-build.webkit.org/#/builders/130/builds/24852 "Built successfully") | [✅ 🛠 mac-AS-debug](https://ews-build.webkit.org/#/builders/123/builds/33007 "Built successfully") | [  ~~🧪 wpe-wk2~~](https://ews-build.webkit.org/#/builders/34/builds/79534 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🧪 win-tests~~](https://ews-build.webkit.org/#/builders/59/builds/55422 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") 
| [✅ 🧪 webkitperl](https://ews-build.webkit.org/#/builders/11/builds/107754 "Passed tests") | [  ~~🧪 ios-wk2~~](https://ews-build.webkit.org/#/builders/132/builds/19321 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [✅ 🧪 api-mac](https://ews-build.webkit.org/#/builders/18/builds/94532 "Passed tests") | [  ~~🧪 api-wpe~~](https://ews-build.webkit.org/#/builders/41/builds/59841 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | 
| | [⏳ 🧪 ios-wk2-wpt](https://ews-build.webkit.org/#/builders/iOS-18-Simulator-WPT-WK2-Tests-EWS "Waiting to run tests") | [✅ 🧪 mac-wk1](https://ews-build.webkit.org/#/builders/135/builds/12609 "Passed tests") | [✅ 🛠 wpe-cairo](https://ews-build.webkit.org/#/builders/65/builds/54801 "Built successfully") | 
| | [⏳ 🧪 api-ios](https://ews-build.webkit.org/#/builders/API-Tests-iOS-Simulator-EWS "Waiting to run tests") | [✅ 🧪 mac-wk2](https://ews-build.webkit.org/#/builders/136/builds/12656 "Passed tests") | [✅ 🛠 gtk](https://ews-build.webkit.org/#/builders/2/builds/112362 "Built successfully") | 
| | [✅ 🛠 vision](https://ews-build.webkit.org/#/builders/128/builds/31914 "Built successfully") | [⏳ 🧪 mac-AS-debug-wk2](https://ews-build.webkit.org/#/builders/macOS-Sequoia-Debug-WK2-Tests-EWS "Waiting to run tests") | [  ~~🧪 gtk-wk2~~](https://ews-build.webkit.org/#/builders/1/builds/88614 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | 
| | [✅ 🛠 vision-sim](https://ews-build.webkit.org/#/builders/121/builds/32277 "Built successfully") | [✅ 🧪 mac-wk2-stress](https://ews-build.webkit.org/#/builders/8/builds/90758 "Passed tests") | [✅ 🧪 api-gtk](https://ews-build.webkit.org/#/builders/21/builds/88237 "Passed tests") | 
| | [✅ 🧪 vision-wk2](https://ews-build.webkit.org/#/builders/126/builds/33125 "Passed tests") | [⏳ 🧪 mac-intel-wk2](https://ews-build.webkit.org/#/builders/macOS-Sonoma-Release-WK2-Intel-Tests-EWS "Waiting to run tests") | [  ~~🛠 playstation~~](https://ews-build.webkit.org/#/builders/134/builds/27227 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | 
| | [✅ 🛠 tv](https://ews-build.webkit.org/#/builders/127/builds/31839 "Built successfully") | [  ~~🛠 mac-safer-cpp~~](https://ews-build.webkit.org/#/builders/120/builds/37191 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | | 
| | [✅ 🛠 tv-sim](https://ews-build.webkit.org/#/builders/125/builds/31631 "Built successfully") | | | 
| | [  ~~🛠 watch~~](https://ews-build.webkit.org/#/builders/129/builds/34972 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | | | 
| | [✅ 🛠 watch-sim](https://ews-build.webkit.org/#/builders/124/builds/33190 "Built successfully") | | | </details>
author_association: COLLABORATOR
user: login: webkit-early-warning-system
created_at: 2025-05-23T02:11:47Z
body: EWS run on previous version of this PR (hash https://github.com/WebKit/WebKit/commit/c3063b5b370d58e65540c591ad182a49580dab45)<details>

| Misc | iOS, visionOS, tvOS & watchOS  | macOS  | Linux |  Windows |
| ----- | ---------------------- | ------- |  ----- |  --------- |
| [✅ 🧪 style](https://ews-build.webkit.org/#/builders/38/builds/104773 "Passed style check") | [✅ 🛠 ios](https://ews-build.webkit.org/#/builders/131/builds/24485 "Built successfully") | [✅ 🛠 mac](https://ews-build.webkit.org/#/builders/138/builds/14907 "Built successfully") | [  ~~🛠 wpe~~](https://ews-build.webkit.org/#/builders/5/builds/109987 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🛠 win~~](https://ews-build.webkit.org/#/builders/59/builds/55447 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") 
| [✅ 🧪 bindings](https://ews-build.webkit.org/#/builders/9/builds/106813 "Passed tests") | [  ~~🛠 ios-sim~~](https://ews-build.webkit.org/#/builders/130/builds/24876 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🛠 mac-AS-debug~~](https://ews-build.webkit.org/#/builders/123/builds/33031 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🧪 wpe-wk2~~](https://ews-build.webkit.org/#/builders/5/builds/109987 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🧪 win-tests~~](https://ews-build.webkit.org/#/builders/59/builds/55447 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") 
| [✅ 🧪 webkitperl](https://ews-build.webkit.org/#/builders/11/builds/107779 "Passed tests") | [  ~~🧪 ios-wk2~~](https://ews-build.webkit.org/#/builders/130/builds/24876 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🧪 api-mac~~](https://ews-build.webkit.org/#/builders/18/builds/94553 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🧪 api-wpe~~](https://ews-build.webkit.org/#/builders/5/builds/109987 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | 
| | [  ~~🧪 ios-wk2-wpt~~](https://ews-build.webkit.org/#/builders/130/builds/24876 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🧪 mac-wk1~~](https://ews-build.webkit.org/#/builders/135/builds/12630 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🛠 wpe-cairo~~](https://ews-build.webkit.org/#/builders/65/builds/54829 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | 
| | [  ~~🧪 api-ios~~](https://ews-build.webkit.org/#/builders/130/builds/24876 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🧪 mac-wk2~~](https://ews-build.webkit.org/#/builders/136/builds/12677 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🛠 gtk~~](https://ews-build.webkit.org/#/builders/2/builds/112389 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | 
| | [  ~~🛠 vision~~](https://ews-build.webkit.org/#/builders/128/builds/31938 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🧪 mac-AS-debug-wk2~~](https://ews-build.webkit.org/#/builders/123/builds/33031 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🧪 gtk-wk2~~](https://ews-build.webkit.org/#/builders/2/builds/112389 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | 
| | [  ~~🛠 vision-sim~~](https://ews-build.webkit.org/#/builders/121/builds/32302 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🧪 mac-wk2-stress~~](https://ews-build.webkit.org/#/builders/8/builds/90779 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🧪 api-gtk~~](https://ews-build.webkit.org/#/builders/2/builds/112389 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | 
| | [⏳ 🧪 vision-wk2 ](https://ews-build.webkit.org/#/builders/visionOS-2-Simulator-WK2-Tests-EWS "Waiting in queue, processing has not started yet") | [⏳ 🧪 mac-intel-wk2](https://ews-build.webkit.org/#/builders/macOS-Sonoma-Release-WK2-Intel-Tests-EWS "Waiting to run tests") | [  ~~🛠 playstation~~](https://ews-build.webkit.org/#/builders/134/builds/27251 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | 
| | [  ~~🛠 tv~~](https://ews-build.webkit.org/#/builders/127/builds/31863 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | [  ~~🛠 mac-safer-cpp~~](https://ews-build.webkit.org/#/builders/120/builds/37217 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | | 
| | [  ~~🛠 tv-sim~~](https://ews-build.webkit.org/#/builders/125/builds/31655 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | | | 
| | [  ~~🛠 watch~~](https://ews-build.webkit.org/#/builders/129/builds/34996 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | | | 
| | [  ~~🛠 watch-sim~~](https://ews-build.webkit.org/#/builders/124/builds/33214 "The change is no longer eligible for processing. Commit was outdated when EWS attempted to process it.") | | | </details>
author_association: COLLABORATOR
user: login: webkit-early-warning-system
created_at: 2025-05-23T02:13:49Z
body: EWS run on current version of this PR (hash https://github.com/WebKit/WebKit/commit/79280ecb44eb7d4ce0920cb448e65d974adee71f)<details>

| Misc | iOS, visionOS, tvOS & watchOS  | macOS  | Linux |  Windows |
| ----- | ---------------------- | ------- |  ----- |  --------- |
| [✅ 🧪 style](https://ews-build.webkit.org/#/builders/38/builds/104774 "Passed style check") | [✅ 🛠 ios](https://ews-build.webkit.org/#/builders/131/builds/24486 "Built successfully") | [✅ 🛠 mac](https://ews-build.webkit.org/#/builders/138/builds/14908 "Built successfully") | [✅ 🛠 wpe](https://ews-build.webkit.org/#/builders/5/builds/109988 "Built successfully") | [✅ 🛠 win](https://ews-build.webkit.org/#/builders/59/builds/55448 "Built successfully") 
| [✅ 🧪 bindings](https://ews-build.webkit.org/#/builders/9/builds/106814 "Passed tests") | [✅ 🛠 ios-sim](https://ews-build.webkit.org/#/builders/130/builds/24877 "Built successfully") | [✅ 🛠 mac-AS-debug](https://ews-build.webkit.org/#/builders/123/builds/33032 "Built successfully") | [✅ 🧪 wpe-wk2](https://ews-build.webkit.org/#/builders/34/builds/79555 "Passed tests") | [⏳ 🧪 win-tests](https://ews-build.webkit.org/#/builders/Win-Tests-EWS "Waiting to run tests") 
| [✅ 🧪 webkitperl](https://ews-build.webkit.org/#/builders/11/builds/107780 "Passed tests") | [✅ 🧪 ios-wk2](https://ews-build.webkit.org/#/builders/132/builds/19340 "Passed tests") | [✅ 🧪 api-mac](https://ews-build.webkit.org/#/builders/18/builds/94555 "Passed tests") | [✅ 🧪 api-wpe](https://ews-build.webkit.org/#/builders/41/builds/59862 "Passed tests") | 
| | [✅ 🧪 ios-wk2-wpt](https://ews-build.webkit.org/#/builders/133/builds/19099 "Passed tests") | [✅ 🧪 mac-wk1](https://ews-build.webkit.org/#/builders/135/builds/12632 "Passed tests") | [✅ 🛠 wpe-cairo](https://ews-build.webkit.org/#/builders/65/builds/54830 "Built successfully") | 
| | [✅ 🧪 api-ios](https://ews-build.webkit.org/#/builders/13/builds/88796 "Passed tests") | [✅ 🧪 mac-wk2](https://ews-build.webkit.org/#/builders/136/builds/12679 "Passed tests") | [✅ 🛠 gtk](https://ews-build.webkit.org/#/builders/2/builds/112390 "Built successfully") | 
| | [✅ 🛠 vision](https://ews-build.webkit.org/#/builders/128/builds/31939 "Built successfully") | [✅ 🧪 mac-AS-debug-wk2](https://ews-build.webkit.org/#/builders/122/builds/23485 "Passed tests") | [✅ 🧪 gtk-wk2](https://ews-build.webkit.org/#/builders/1/builds/88634 "Passed tests") | 
| | [✅ 🛠 vision-sim](https://ews-build.webkit.org/#/builders/121/builds/32303 "Built successfully") | [✅ 🧪 mac-wk2-stress](https://ews-build.webkit.org/#/builders/8/builds/90781 "Passed tests") | [✅ 🧪 api-gtk](https://ews-build.webkit.org/#/builders/21/builds/88258 "Passed tests") | 
| [✅ 🛠 🧪 merge](https://ews-build.webkit.org/#/builders/19/builds/22510 "Built successfully and passed tests") | [✅ 🧪 vision-wk2](https://ews-build.webkit.org/#/builders/126/builds/33150 "Passed tests") | [✅ 🧪 mac-intel-wk2](https://ews-build.webkit.org/#/builders/137/builds/10919 "Passed tests") | [✅ 🛠 playstation](https://ews-build.webkit.org/#/builders/134/builds/27252 "Built successfully") | 
| | [✅ 🛠 tv](https://ews-build.webkit.org/#/builders/127/builds/31864 "Built successfully") | [✅ 🛠 mac-safer-cpp](https://ews-build.webkit.org/#/builders/120/builds/37218 "Built successfully") | | 
| | [✅ 🛠 tv-sim](https://ews-build.webkit.org/#/builders/125/builds/31656 "Built successfully") | | | 
| | [✅ 🛠 watch](https://ews-build.webkit.org/#/builders/129/builds/34997 "Built successfully") | | | 
| | [✅ 🛠 watch-sim](https://ews-build.webkit.org/#/builders/124/builds/33215 "Built successfully") | | | </details>
author_association: COLLABORATOR
user: login: webkit-commit-queue
created_at: 2025-05-23T19:48:46Z
body: Committed 295357@main (a73751a1a8cb): <https://commits.webkit.org/295357@main>

Reviewed commits have been landed. Closing PR #45730 and removing active labels.
author_association: COLLABORATOR