Diff
Modified: trunk/LayoutTests/ChangeLog (277111 => 277112)
--- trunk/LayoutTests/ChangeLog 2021-05-06 19:41:53 UTC (rev 277111)
+++ trunk/LayoutTests/ChangeLog 2021-05-06 20:11:19 UTC (rev 277112)
@@ -1,3 +1,18 @@
+2021-05-06 Antoine Quint <[email protected]>
+
+ CSS custom properties on pseudo elements background gradients causes infinite layout and high CPU load
+ https://bugs.webkit.org/show_bug.cgi?id=194332
+ <rdar://problem/47873895>
+
+ Reviewed by Simon Fraser.
+
+ Add a test where an element with a background-image set to a CSS gradient using a custom property as a color
+ stop changes another property targeted by a transition to check that there is no background-image transition
+ generated.
+
+ * webanimations/css-transition-element-with-gradient-background-image-and-css-custom-property-expected.txt: Added.
+ * webanimations/css-transition-element-with-gradient-background-image-and-css-custom-property.html: Added.
+
2021-05-06 Diego Pino Garcia <[email protected]>
[GLIB] Unreviewed test gardening. Update expectations after WPT re-import in r277073 and r277077.
Added: trunk/LayoutTests/webanimations/css-transition-element-with-gradient-background-image-and-css-custom-property-expected.txt (0 => 277112)
--- trunk/LayoutTests/webanimations/css-transition-element-with-gradient-background-image-and-css-custom-property-expected.txt (rev 0)
+++ trunk/LayoutTests/webanimations/css-transition-element-with-gradient-background-image-and-css-custom-property-expected.txt 2021-05-06 20:11:19 UTC (rev 277112)
@@ -0,0 +1,3 @@
+
+PASS An element with a background-image set to a CSS gradient with a stop color set by a custom property does not yield a background-image transition when another CSS property changes.
+
Added: trunk/LayoutTests/webanimations/css-transition-element-with-gradient-background-image-and-css-custom-property.html (0 => 277112)
--- trunk/LayoutTests/webanimations/css-transition-element-with-gradient-background-image-and-css-custom-property.html (rev 0)
+++ trunk/LayoutTests/webanimations/css-transition-element-with-gradient-background-image-and-css-custom-property.html 2021-05-06 20:11:19 UTC (rev 277112)
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+
+:root {
+ --custom-color: blue;
+}
+
+.target {
+ width: 100px;
+ height: 100px;
+ background: linear-gradient(var(--custom-color), green);
+ transition-duration: 1s;
+}
+
+.target.transition {
+ margin-left: 100px;
+}
+
+</style>
+<body>
+<script src=""
+<script src=""
+<script>
+
+'use strict';
+
+promise_test(async test => {
+ const target = document.body.appendChild(document.createElement("div"));
+ target.classList.add("target");
+
+ await new Promise(requestAnimationFrame);
+ target.classList.add("transition");
+
+ const animations = target.getAnimations();
+ assert_equals(animations.length, 1, "There is only one animation applied to the target.");
+ assert_true(animations[0] instanceof CSSTransition, "The single animation applied to the target is a CSS transition.");
+ assert_equals(animations[0].transitionProperty, "margin-left", "The single CSS transition applied to the target is for the margin-left property.");
+}, `An element with a background-image set to a CSS gradient with a stop color set by a custom property does not yield a background-image transition when another CSS property changes.`);
+
+</script>
+</body>
\ No newline at end of file
Modified: trunk/Source/WebCore/ChangeLog (277111 => 277112)
--- trunk/Source/WebCore/ChangeLog 2021-05-06 19:41:53 UTC (rev 277111)
+++ trunk/Source/WebCore/ChangeLog 2021-05-06 20:11:19 UTC (rev 277112)
@@ -1,3 +1,44 @@
+2021-05-06 Antoine Quint <[email protected]>
+
+ CSS custom properties on pseudo elements background gradients causes infinite layout and high CPU load
+ https://bugs.webkit.org/show_bug.cgi?id=194332
+ <rdar://problem/47873895>
+
+ Reviewed by Simon Fraser.
+
+ When a background-image uses a CSS custom property the resulting StyleGeneratedImage may not be the same
+ object as during prior style updates. This caused transitions to be triggered for all style updates for
+ such background-image values. To fix this, we modify the == operator for StyleGeneratedImage to use
+ arePointingToEqualData() with the CSSImageGeneratorValue member and added an == operator for the
+ CSSImageGeneratorValue class to call into the existing equals() methods. These equals() methods
+ are now overrides of the virtual CSSImageGeneratorValue method.
+
+ This change in behavior required a change in RenderElement::updateFillImages() to not only check whether
+ the images were identical, but to also check whether the renderer was registered as a client on the new
+ images. To do this, we add a new virtual hasClient() method on StyleImage.
+
+ Test: webanimations/css-transition-element-with-gradient-background-image-and-css-custom-property.html
+
+ * css/CSSImageGeneratorValue.cpp:
+ (WebCore::CSSImageGeneratorValue::operator== const):
+ * css/CSSImageGeneratorValue.h:
+ * rendering/RenderElement.cpp:
+ (WebCore::RenderElement::updateFillImages):
+ * rendering/style/FillLayer.cpp:
+ (WebCore::FillLayer::imagesIdentical): Deleted.
+ * rendering/style/FillLayer.h:
+ * rendering/style/StyleCachedImage.cpp:
+ (WebCore::StyleCachedImage::hasClient const):
+ * rendering/style/StyleCachedImage.h:
+ * rendering/style/StyleGeneratedImage.cpp:
+ (WebCore::StyleGeneratedImage::operator== const):
+ (WebCore::StyleGeneratedImage::hasClient const):
+ * rendering/style/StyleGeneratedImage.h:
+ * rendering/style/StyleImage.h:
+ * rendering/style/StyleMultiImage.cpp:
+ (WebCore::StyleMultiImage::hasClient const):
+ * rendering/style/StyleMultiImage.h:
+
2021-05-06 Philippe Normand <[email protected]>
[WebAudio][GStreamer] socketpair leaks
Modified: trunk/Source/WebCore/css/CSSImageGeneratorValue.cpp (277111 => 277112)
--- trunk/Source/WebCore/css/CSSImageGeneratorValue.cpp 2021-05-06 19:41:53 UTC (rev 277111)
+++ trunk/Source/WebCore/css/CSSImageGeneratorValue.cpp 2021-05-06 20:11:19 UTC (rev 277112)
@@ -298,6 +298,35 @@
}
}
+bool CSSImageGeneratorValue::operator==(const CSSImageGeneratorValue& other) const
+{
+ if (classType() != other.classType())
+ return false;
+
+ switch (classType()) {
+ case CrossfadeClass:
+ return downcast<CSSCrossfadeValue>(*this).equals(downcast<CSSCrossfadeValue>(other));
+ case CanvasClass:
+ return downcast<CSSCanvasValue>(*this).equals(downcast<CSSCanvasValue>(other));
+ case FilterImageClass:
+ return downcast<CSSFilterImageValue>(*this).equals(downcast<CSSFilterImageValue>(other));
+ case LinearGradientClass:
+ return downcast<CSSLinearGradientValue>(*this).equals(downcast<CSSLinearGradientValue>(other));
+ case RadialGradientClass:
+ return downcast<CSSRadialGradientValue>(*this).equals(downcast<CSSRadialGradientValue>(other));
+ case ConicGradientClass:
+ return downcast<CSSConicGradientValue>(*this).equals(downcast<CSSConicGradientValue>(other));
+#if ENABLE(CSS_PAINTING_API)
+ case PaintImageClass:
+ return downcast<CSSPaintImageValue>(*this).equals(downcast<CSSPaintImageValue>(other));
+#endif
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ return false;
+}
+
bool CSSImageGeneratorValue::subimageIsPending(const CSSValue& value)
{
if (is<CSSImageValue>(value))
Modified: trunk/Source/WebCore/css/CSSImageGeneratorValue.h (277111 => 277112)
--- trunk/Source/WebCore/css/CSSImageGeneratorValue.h 2021-05-06 19:41:53 UTC (rev 277111)
+++ trunk/Source/WebCore/css/CSSImageGeneratorValue.h 2021-05-06 20:11:19 UTC (rev 277112)
@@ -57,6 +57,8 @@
void loadSubimages(CachedResourceLoader&, const ResourceLoaderOptions&);
+ bool operator==(const CSSImageGeneratorValue& other) const;
+
protected:
CSSImageGeneratorValue(ClassType);
Modified: trunk/Source/WebCore/rendering/RenderElement.cpp (277111 => 277112)
--- trunk/Source/WebCore/rendering/RenderElement.cpp 2021-05-06 19:41:53 UTC (rev 277111)
+++ trunk/Source/WebCore/rendering/RenderElement.cpp 2021-05-06 20:11:19 UTC (rev 277112)
@@ -353,10 +353,31 @@
void RenderElement::updateFillImages(const FillLayer* oldLayers, const FillLayer& newLayers)
{
- // Optimize the common case.
- if (FillLayer::imagesIdentical(oldLayers, &newLayers))
+ auto fillImagesAreIdentical = [](const FillLayer* layer1, const FillLayer* layer2) -> bool {
+ if (layer1 == layer2)
+ return true;
+
+ for (; layer1 && layer2; layer1 = layer1->next(), layer2 = layer2->next()) {
+ if (!arePointingToEqualData(layer1->image(), layer2->image()))
+ return false;
+ }
+
+ return !layer1 && !layer2;
+ };
+
+ auto isRegisteredWithNewFillImages = [&]() -> bool {
+ for (auto* layer = &newLayers; layer; layer = layer->next()) {
+ if (layer->image() && !layer->image()->hasClient(*this))
+ return false;
+ }
+ return true;
+ };
+
+ // If images have the same characteristics and this element is already registered as a
+ // client to the new images, there is nothing to do.
+ if (fillImagesAreIdentical(oldLayers, &newLayers) && isRegisteredWithNewFillImages())
return;
-
+
// Add before removing, to avoid removing all clients of an image that is in both sets.
for (auto* layer = &newLayers; layer; layer = layer->next()) {
if (layer->image())
Modified: trunk/Source/WebCore/rendering/style/FillLayer.cpp (277111 => 277112)
--- trunk/Source/WebCore/rendering/style/FillLayer.cpp 2021-05-06 19:41:53 UTC (rev 277111)
+++ trunk/Source/WebCore/rendering/style/FillLayer.cpp 2021-05-06 20:11:19 UTC (rev 277112)
@@ -396,19 +396,6 @@
return false;
}
-bool FillLayer::imagesIdentical(const FillLayer* layer1, const FillLayer* layer2)
-{
- if (layer1 == layer2)
- return true;
-
- for (; layer1 && layer2; layer1 = layer1->next(), layer2 = layer2->next()) {
- if (!arePointingToEqualData(layer1->image(), layer2->image()))
- return false;
- }
-
- return !layer1 && !layer2;
-}
-
TextStream& operator<<(TextStream& ts, FillSize fillSize)
{
return ts << fillSize.type << " " << fillSize.size;
Modified: trunk/Source/WebCore/rendering/style/FillLayer.h (277111 => 277112)
--- trunk/Source/WebCore/rendering/style/FillLayer.h 2021-05-06 19:41:53 UTC (rev 277111)
+++ trunk/Source/WebCore/rendering/style/FillLayer.h 2021-05-06 20:11:19 UTC (rev 277112)
@@ -159,8 +159,6 @@
void fillUnsetProperties();
void cullEmptyLayers();
- static bool imagesIdentical(const FillLayer*, const FillLayer*);
-
static FillAttachment initialFillAttachment(FillLayerType) { return FillAttachment::ScrollBackground; }
static FillBox initialFillClip(FillLayerType) { return FillBox::Border; }
static FillBox initialFillOrigin(FillLayerType type) { return type == FillLayerType::Background ? FillBox::Padding : FillBox::Border; }
Modified: trunk/Source/WebCore/rendering/style/StyleCachedImage.cpp (277111 => 277112)
--- trunk/Source/WebCore/rendering/style/StyleCachedImage.cpp 2021-05-06 19:41:53 UTC (rev 277111)
+++ trunk/Source/WebCore/rendering/style/StyleCachedImage.cpp 2021-05-06 20:11:19 UTC (rev 277112)
@@ -172,6 +172,14 @@
m_cachedImage->removeClient(renderer);
}
+bool StyleCachedImage::hasClient(RenderElement& renderer) const
+{
+ ASSERT(!m_isPending);
+ if (!m_cachedImage)
+ return false;
+ return m_cachedImage->hasClient(renderer);
+}
+
RefPtr<Image> StyleCachedImage::image(RenderElement* renderer, const FloatSize&) const
{
ASSERT(!m_isPending);
Modified: trunk/Source/WebCore/rendering/style/StyleCachedImage.h (277111 => 277112)
--- trunk/Source/WebCore/rendering/style/StyleCachedImage.h 2021-05-06 19:41:53 UTC (rev 277111)
+++ trunk/Source/WebCore/rendering/style/StyleCachedImage.h 2021-05-06 20:11:19 UTC (rev 277112)
@@ -60,6 +60,7 @@
void setContainerContextForRenderer(const RenderElement&, const FloatSize&, float) final;
void addClient(RenderElement&) final;
void removeClient(RenderElement&) final;
+ bool hasClient(RenderElement&) const final;
RefPtr<Image> image(RenderElement*, const FloatSize&) const final;
float imageScaleFactor() const final;
bool knownToBeOpaque(const RenderElement&) const final;
Modified: trunk/Source/WebCore/rendering/style/StyleGeneratedImage.cpp (277111 => 277112)
--- trunk/Source/WebCore/rendering/style/StyleGeneratedImage.cpp 2021-05-06 19:41:53 UTC (rev 277111)
+++ trunk/Source/WebCore/rendering/style/StyleGeneratedImage.cpp 2021-05-06 20:11:19 UTC (rev 277112)
@@ -37,6 +37,13 @@
m_isGeneratedImage = true;
}
+bool StyleGeneratedImage::operator==(const StyleImage& other) const
+{
+ if (is<StyleGeneratedImage>(other))
+ return arePointingToEqualData(m_imageGeneratorValue.ptr(), downcast<StyleGeneratedImage>(other).m_imageGeneratorValue.ptr());
+ return false;
+}
+
Ref<CSSValue> StyleGeneratedImage::cssValue() const
{
return m_imageGeneratorValue.copyRef();
@@ -96,6 +103,11 @@
m_imageGeneratorValue->removeClient(renderer);
}
+bool StyleGeneratedImage::hasClient(RenderElement& renderer) const
+{
+ return m_imageGeneratorValue->clients().contains(&renderer);
+}
+
RefPtr<Image> StyleGeneratedImage::image(RenderElement* renderer, const FloatSize& size) const
{
return renderer ? m_imageGeneratorValue->image(*renderer, size) : &Image::nullImage();
Modified: trunk/Source/WebCore/rendering/style/StyleGeneratedImage.h (277111 => 277112)
--- trunk/Source/WebCore/rendering/style/StyleGeneratedImage.h 2021-05-06 19:41:53 UTC (rev 277111)
+++ trunk/Source/WebCore/rendering/style/StyleGeneratedImage.h 2021-05-06 20:11:19 UTC (rev 277112)
@@ -40,7 +40,7 @@
CSSImageGeneratorValue& imageValue() { return m_imageGeneratorValue; }
private:
- bool operator==(const StyleImage& other) const final { return data() == other.data(); }
+ bool operator==(const StyleImage& other) const final;
WrappedImagePtr data() const final { return m_imageGeneratorValue.ptr(); }
@@ -56,6 +56,7 @@
void setContainerContextForRenderer(const RenderElement&, const FloatSize& containerSize, float) final { m_containerSize = containerSize; }
void addClient(RenderElement&) final;
void removeClient(RenderElement&) final;
+ bool hasClient(RenderElement&) const final;
RefPtr<Image> image(RenderElement*, const FloatSize&) const final;
bool knownToBeOpaque(const RenderElement&) const final;
Modified: trunk/Source/WebCore/rendering/style/StyleImage.h (277111 => 277112)
--- trunk/Source/WebCore/rendering/style/StyleImage.h 2021-05-06 19:41:53 UTC (rev 277111)
+++ trunk/Source/WebCore/rendering/style/StyleImage.h 2021-05-06 20:11:19 UTC (rev 277112)
@@ -62,6 +62,7 @@
virtual void setContainerContextForRenderer(const RenderElement&, const FloatSize&, float) = 0;
virtual void addClient(RenderElement&) = 0;
virtual void removeClient(RenderElement&) = 0;
+ virtual bool hasClient(RenderElement&) const = 0;
virtual RefPtr<Image> image(RenderElement*, const FloatSize&) const = 0;
virtual WrappedImagePtr data() const = 0;
virtual float imageScaleFactor() const { return 1; }
Modified: trunk/Source/WebCore/rendering/style/StyleMultiImage.cpp (277111 => 277112)
--- trunk/Source/WebCore/rendering/style/StyleMultiImage.cpp 2021-05-06 19:41:53 UTC (rev 277111)
+++ trunk/Source/WebCore/rendering/style/StyleMultiImage.cpp 2021-05-06 20:11:19 UTC (rev 277112)
@@ -148,6 +148,13 @@
m_selectedImage->removeClient(renderer);
}
+bool StyleMultiImage::hasClient(RenderElement& renderer) const
+{
+ if (!m_selectedImage)
+ return false;
+ return m_selectedImage->hasClient(renderer);
+}
+
RefPtr<Image> StyleMultiImage::image(RenderElement* renderer, const FloatSize& size) const
{
if (!m_selectedImage)
Modified: trunk/Source/WebCore/rendering/style/StyleMultiImage.h (277111 => 277112)
--- trunk/Source/WebCore/rendering/style/StyleMultiImage.h 2021-05-06 19:41:53 UTC (rev 277111)
+++ trunk/Source/WebCore/rendering/style/StyleMultiImage.h 2021-05-06 20:11:19 UTC (rev 277112)
@@ -59,6 +59,7 @@
void setContainerContextForRenderer(const RenderElement&, const FloatSize&, float);
void addClient(RenderElement&) final;
void removeClient(RenderElement&) final;
+ bool hasClient(RenderElement&) const final;
RefPtr<Image> image(RenderElement*, const FloatSize&) const final;
float imageScaleFactor() const final;
bool knownToBeOpaque(const RenderElement&) const final;