Diff
Modified: trunk/LayoutTests/ChangeLog (214502 => 214503)
--- trunk/LayoutTests/ChangeLog 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/LayoutTests/ChangeLog 2017-03-28 23:11:35 UTC (rev 214503)
@@ -1,3 +1,20 @@
+2017-03-28 Chris Dumez <[email protected]>
+
+ Animated SVG images are not paused when outside viewport
+ https://bugs.webkit.org/show_bug.cgi?id=170155
+ <rdar://problem/31288893>
+
+ Reviewed by Antti Koivisto.
+
+ Add layout test coverage.
+
+ * platform/mac-wk1/TestExpectations:
+ * svg/animations/animated-svg-image-outside-viewport-paused-expected.txt: Added.
+ * svg/animations/animated-svg-image-outside-viewport-paused.html: Added.
+ * svg/animations/animated-svg-image-removed-from-document-paused-expected.txt: Added.
+ * svg/animations/animated-svg-image-removed-from-document-paused.html: Added.
+ * svg/animations/resources/smilAnimation.svg: Added.
+
2017-03-28 Antti Koivisto <[email protected]>
Missing render tree position invalidation when tearing down renderers for display:contents subtree
Modified: trunk/LayoutTests/platform/mac-wk1/TestExpectations (214502 => 214503)
--- trunk/LayoutTests/platform/mac-wk1/TestExpectations 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/LayoutTests/platform/mac-wk1/TestExpectations 2017-03-28 23:11:35 UTC (rev 214503)
@@ -123,6 +123,8 @@
fast/images/animated-gif-body-outside-viewport.html [ Skip ]
fast/images/animated-gif-window-resizing.html [ Skip ]
fast/images/animated-gif-zooming.html [ Skip ]
+svg/animations/animated-svg-image-outside-viewport-paused.html [ Skip ]
+svg/animations/animated-svg-image-removed-from-document-paused.html [ Skip ]
# WK1 uses the native scrollview for scrolling by page.
scrollbars/scrolling-backward-by-page-accounting-bottom-fixed-elements-on-keyboard-spacebar.html
Added: trunk/LayoutTests/svg/animations/animated-svg-image-outside-viewport-paused-expected.txt (0 => 214503)
--- trunk/LayoutTests/svg/animations/animated-svg-image-outside-viewport-paused-expected.txt (rev 0)
+++ trunk/LayoutTests/svg/animations/animated-svg-image-outside-viewport-paused-expected.txt 2017-03-28 23:11:35 UTC (rev 214503)
@@ -0,0 +1,15 @@
+Tests that animated SVG images are paused when outside the viewport.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Initially outside the viewport
+PASS internals.isImageAnimating(image) is false
+Scrolling animation into view
+PASS internals.isImageAnimating(image) became true
+Scrolling animation outside view again
+PASS internals.isImageAnimating(image) became false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/svg/animations/animated-svg-image-outside-viewport-paused.html (0 => 214503)
--- trunk/LayoutTests/svg/animations/animated-svg-image-outside-viewport-paused.html (rev 0)
+++ trunk/LayoutTests/svg/animations/animated-svg-image-outside-viewport-paused.html 2017-03-28 23:11:35 UTC (rev 214503)
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+description("Tests that animated SVG images are paused when outside the viewport.");
+jsTestIsAsync = true;
+
+_onload_ = function() {
+ image = document.querySelector("img");
+
+ setTimeout(function() {
+ debug("Initially outside the viewport");
+ shouldBeFalse("internals.isImageAnimating(image)");
+
+ debug("Scrolling animation into view");
+ internals.scrollElementToRect(image, 0, 0, 300, 300);
+ shouldBecomeEqual("internals.isImageAnimating(image)", "true", function() {
+ debug("Scrolling animation outside view again");
+ scroll(0, 0);
+ shouldBecomeEqual("internals.isImageAnimating(image)", "false", finishJSTest);
+ });
+ }, 30);
+}
+</script>
+<div style="position: relative; width: 1600px; height: 2400px;">
+<img src="" style="position:absolute; left: 600px; top: 800px;">
+</div>
+<script src=""
+</body>
+</html>
Added: trunk/LayoutTests/svg/animations/animated-svg-image-removed-from-document-paused-expected.txt (0 => 214503)
--- trunk/LayoutTests/svg/animations/animated-svg-image-removed-from-document-paused-expected.txt (rev 0)
+++ trunk/LayoutTests/svg/animations/animated-svg-image-removed-from-document-paused-expected.txt 2017-03-28 23:11:35 UTC (rev 214503)
@@ -0,0 +1,21 @@
+Tests that animated SVG images are paused when removed from the document.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS internals.isImageAnimating(imageA) is true
+PASS internals.isImageAnimating(imageB) is true
+imageA.remove()
+PASS internals.isImageAnimating(imageB) is true
+PASS internals.isImageAnimating(imageB) is true
+imageB.remove()
+PASS internals.isImageAnimating(imageA) is false
+PASS internals.isImageAnimating(imageB) is false
+document.body.appendChild(imageA)
+PASS internals.isImageAnimating(imageA) is true
+document.body.appendChild(imageB)
+PASS internals.isImageAnimating(imageB) is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/svg/animations/animated-svg-image-removed-from-document-paused.html (0 => 214503)
--- trunk/LayoutTests/svg/animations/animated-svg-image-removed-from-document-paused.html (rev 0)
+++ trunk/LayoutTests/svg/animations/animated-svg-image-removed-from-document-paused.html 2017-03-28 23:11:35 UTC (rev 214503)
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<img id="a" src=""
+<img id="b" src=""
+<script>
+description("Tests that animated SVG images are paused when removed from the document.");
+jsTestIsAsync = true;
+
+// Both images will use the same underlying SVGImage.
+const imageA = document.getElementById("a");
+const imageB = document.getElementById("b");
+
+_onload_ = function() {
+ shouldBeTrue("internals.isImageAnimating(imageA)");
+ shouldBeTrue("internals.isImageAnimating(imageB)");
+
+ setTimeout(function() {
+ evalAndLog("imageA.remove()");
+ shouldBeTrue("internals.isImageAnimating(imageB)");
+
+ setTimeout(function() {
+ shouldBeTrue("internals.isImageAnimating(imageB)");
+ evalAndLog("imageB.remove()");
+ setTimeout(function() {
+ shouldBeFalse("internals.isImageAnimating(imageA)");
+ shouldBeFalse("internals.isImageAnimating(imageB)");
+
+ evalAndLog("document.body.appendChild(imageA)");
+ document.body.offsetWidth; // Force layout.
+ shouldBeTrue("internals.isImageAnimating(imageA)");
+ evalAndLog("document.body.appendChild(imageB)");
+ document.body.offsetWidth; // Force layout.
+ shouldBeTrue("internals.isImageAnimating(imageB)");
+
+ finishJSTest();
+ }, 30);
+ }, 30);
+ }, 30);
+}
+</script>
+<script src=""
+</body>
+</html>
Added: trunk/LayoutTests/svg/animations/resources/smilAnimation.svg (0 => 214503)
--- trunk/LayoutTests/svg/animations/resources/smilAnimation.svg (rev 0)
+++ trunk/LayoutTests/svg/animations/resources/smilAnimation.svg 2017-03-28 23:11:35 UTC (rev 214503)
@@ -0,0 +1,5 @@
+<svg width="200" height="100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <rect width="100" height="100" fill="green">
+ <animate attributeName="x" values="0; 100; 0" dur="1s" repeatCount="indefinite"></animate>
+ </rect>
+</svg>
Modified: trunk/Source/WebCore/ChangeLog (214502 => 214503)
--- trunk/Source/WebCore/ChangeLog 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/ChangeLog 2017-03-28 23:11:35 UTC (rev 214503)
@@ -1,3 +1,93 @@
+2017-03-28 Chris Dumez <[email protected]>
+
+ Animated SVG images are not paused when outside viewport
+ https://bugs.webkit.org/show_bug.cgi?id=170155
+ <rdar://problem/31288893>
+
+ Reviewed by Antti Koivisto.
+
+ Make sure animated SVG images get paused when outside the viewport,
+ similarly to what was already done for animated GIF images. Also
+ make sure they are paused when they no longer have any renderers
+ using them.
+
+ Tests: svg/animations/animated-svg-image-outside-viewport-paused.html
+ svg/animations/animated-svg-image-removed-from-document-paused.html
+
+ * loader/cache/CachedImage.cpp:
+ (WebCore::CachedImage::didAddClient):
+ Restart the animation whenever a new CachedImage client is added. This
+ will cause us the re-evaluate if the animation should run. The animation
+ will pause again if the new renderer is not inside the viewport.
+
+ (WebCore::CachedImage::animationAdvanced):
+ Add a flag to newImageAnimationFrameAvailable() so that the renderers can
+ let us know if we can pause the animation. Pause the animation if all no
+ renderer requires it (i.e. they are all outside the viewport, or there
+ are no renderers).
+
+ * loader/cache/CachedImageClient.h:
+ (WebCore::CachedImageClient::newImageAnimationFrameAvailable):
+ By default, the CachedImageClients allow pausing. Only renderer will
+ potentially prevent pausing if they are inside the viewport.
+
+ * platform/graphics/BitmapImage.cpp:
+ (WebCore::BitmapImage::isAnimating):
+ * platform/graphics/BitmapImage.h:
+ * platform/graphics/Image.h:
+ (WebCore::Image::isAnimating):
+ Add isAnimating() flag on Image for layout testing purposes.
+
+ * rendering/RenderElement.cpp:
+ (WebCore::RenderElement::newImageAnimationFrameAvailable):
+ Set canPause flag to true if the renderer is not inside the viewport.
+
+ (WebCore::RenderElement::repaintForPausedImageAnimationsIfNeeded):
+ Call startAnimation() if the renderer is now visible to resume SVG
+ animations. Repainting is enough for GIF animations but not for SVG
+ animations, we have to explicitly resume them.
+
+ * rendering/RenderElement.h:
+ * rendering/RenderView.cpp:
+ (WebCore::RenderView::addRendererWithPausedImageAnimations):
+ (WebCore::RenderView::removeRendererWithPausedImageAnimations):
+ (WebCore::RenderView::resumePausedImageAnimationsIfNeeded):
+ * rendering/RenderView.h:
+ Store CachedImages with the renderers that have paused animations.
+ This is required for SVG where we need to explicitly resume the
+ animation on the CachedImage when the renderer becomes visible
+ again. Having access to the Image will also allow us to do smarter
+ visibility checks in RenderElement's shouldRepaintForImageAnimation(),
+ in the future.
+
+ * svg/SVGSVGElement.cpp:
+ (WebCore::SVGSVGElement::hasActiveAnimation):
+ * svg/SVGSVGElement.h:
+ Add hasActiveAnimation() method.
+
+ * svg/graphics/SVGImage.cpp:
+ (WebCore::SVGImage::startAnimation):
+ Check that animations are paused before starting them. This avoid
+ jumping due to unnecessary calls to rootElement->setCurrentTime(0).
+
+ (WebCore::SVGImage::isAnimating):
+ Add isAnimating() method for layout tests purposes.
+
+ * svg/graphics/SVGImage.h:
+ * svg/graphics/SVGImageClients.h:
+ Call animationAdvanced() on the observer instead of the generic
+ changedInRect() when the SVGImage is animating. This way, we go
+ through the same code path as GIF animations and we end up calling
+ CachedImage::animationAdvanced() which calls newImageAnimationFrameAvailable()
+ on RenderElement, which determines if the animation should keep
+ running or not.
+
+ * testing/Internals.cpp:
+ (WebCore::Internals::isImageAnimating):
+ * testing/Internals.h:
+ * testing/Internals.idl:
+ Add layout testing infrastructure.
+
2017-03-28 Antti Koivisto <[email protected]>
Missing render tree position invalidation when tearing down renderers for display:contents subtree
Modified: trunk/Source/WebCore/loader/cache/CachedImage.cpp (214502 => 214503)
--- trunk/Source/WebCore/loader/cache/CachedImage.cpp 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/loader/cache/CachedImage.cpp 2017-03-28 23:11:35 UTC (rev 214503)
@@ -117,6 +117,9 @@
if (m_image && !m_image->isNull())
static_cast<CachedImageClient&>(client).imageChanged(this);
+ if (m_image)
+ m_image->startAnimation();
+
CachedResource::didAddClient(client);
}
@@ -514,9 +517,19 @@
{
if (!image || image != m_image)
return;
+
+ bool shouldPauseAnimation = true;
+
CachedResourceClientWalker<CachedImageClient> clientWalker(m_clients);
- while (CachedImageClient* client = clientWalker.next())
- client->newImageAnimationFrameAvailable(*this);
+ while (CachedImageClient* client = clientWalker.next()) {
+ bool canPause = false;
+ client->newImageAnimationFrameAvailable(*this, canPause);
+ if (!canPause)
+ shouldPauseAnimation = false;
+ }
+
+ if (shouldPauseAnimation)
+ m_image->stopAnimation();
}
void CachedImage::changedInRect(const Image* image, const IntRect* rect)
Modified: trunk/Source/WebCore/loader/cache/CachedImageClient.h (214502 => 214503)
--- trunk/Source/WebCore/loader/cache/CachedImageClient.h 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/loader/cache/CachedImageClient.h 2017-03-28 23:11:35 UTC (rev 214503)
@@ -40,7 +40,7 @@
virtual void imageChanged(CachedImage*, const IntRect* = nullptr) { }
// Called when GIF animation progresses.
- virtual void newImageAnimationFrameAvailable(CachedImage& image) { imageChanged(&image); }
+ virtual void newImageAnimationFrameAvailable(CachedImage& image, bool& canPause) { imageChanged(&image); canPause = true; }
};
} // namespace WebCore
Modified: trunk/Source/WebCore/platform/graphics/BitmapImage.cpp (214502 => 214503)
--- trunk/Source/WebCore/platform/graphics/BitmapImage.cpp 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/platform/graphics/BitmapImage.cpp 2017-03-28 23:11:35 UTC (rev 214503)
@@ -406,6 +406,11 @@
LOG(Images, "BitmapImage::%s - %p - url: %s [m_currentFrame = %ld]", __FUNCTION__, this, sourceURL().utf8().data(), m_currentFrame);
}
+bool BitmapImage::isAnimating() const
+{
+ return !!m_frameTimer;
+}
+
void BitmapImage::stopAnimation()
{
// This timer is used to animate all occurrences of this image. Don't invalidate
Modified: trunk/Source/WebCore/platform/graphics/BitmapImage.h (214502 => 214503)
--- trunk/Source/WebCore/platform/graphics/BitmapImage.h 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/platform/graphics/BitmapImage.h 2017-03-28 23:11:35 UTC (rev 214503)
@@ -169,6 +169,7 @@
StartAnimationStatus internalStartAnimation();
void advanceAnimation();
void internalAdvanceAnimation();
+ bool isAnimating() const final;
// It may look unusual that there is no start animation call as public API. This is because
// we start and stop animating lazily. Animation begins whenever someone draws the image. It will
Modified: trunk/Source/WebCore/platform/graphics/Image.h (214502 => 214503)
--- trunk/Source/WebCore/platform/graphics/Image.h 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/platform/graphics/Image.h 2017-03-28 23:11:35 UTC (rev 214503)
@@ -131,6 +131,7 @@
virtual void stopAnimation() {}
virtual void resetAnimation() {}
virtual void newFrameNativeImageAvailableAtIndex(size_t) { }
+ virtual bool isAnimating() const { return false; }
// Typically the CachedImage that owns us.
ImageObserver* imageObserver() const { return m_imageObserver; }
Modified: trunk/Source/WebCore/rendering/RenderElement.cpp (214502 => 214503)
--- trunk/Source/WebCore/rendering/RenderElement.cpp 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/rendering/RenderElement.cpp 2017-03-28 23:11:35 UTC (rev 214503)
@@ -1494,21 +1494,19 @@
ASSERT_NOT_REACHED();
}
-void RenderElement::newImageAnimationFrameAvailable(CachedImage& image)
+void RenderElement::newImageAnimationFrameAvailable(CachedImage& image, bool& canPause)
{
auto& frameView = view().frameView();
auto visibleRect = frameView.windowToContents(frameView.windowClipRect());
if (!shouldRepaintForImageAnimation(*this, visibleRect)) {
- // FIXME: It would be better to pass the image along with the renderer
- // so that we can be smarter about detecting if the image is inside the
- // viewport in repaintForPausedImageAnimationsIfNeeded().
- view().addRendererWithPausedImageAnimations(*this);
+ view().addRendererWithPausedImageAnimations(*this, image);
+ canPause = true;
return;
}
imageChanged(&image);
}
-bool RenderElement::repaintForPausedImageAnimationsIfNeeded(const IntRect& visibleRect)
+bool RenderElement::repaintForPausedImageAnimationsIfNeeded(const IntRect& visibleRect, CachedImage& cachedImage)
{
ASSERT(m_hasPausedImageAnimations);
if (!shouldRepaintForImageAnimation(*this, visibleRect))
@@ -1516,6 +1514,9 @@
repaint();
+ if (auto* image = cachedImage.image())
+ image->startAnimation();
+
// For directly-composited animated GIFs it does not suffice to call repaint() to resume animation. We need to mark the image as changed.
if (is<RenderBoxModelObject>(*this))
downcast<RenderBoxModelObject>(*this).contentChanged(ImageChanged);
Modified: trunk/Source/WebCore/rendering/RenderElement.h (214502 => 214503)
--- trunk/Source/WebCore/rendering/RenderElement.h 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/rendering/RenderElement.h 2017-03-28 23:11:35 UTC (rev 214503)
@@ -195,7 +195,7 @@
void setVisibleInViewportState(VisibleInViewportState);
virtual void visibleInViewportStateChanged();
- bool repaintForPausedImageAnimationsIfNeeded(const IntRect& visibleRect);
+ bool repaintForPausedImageAnimationsIfNeeded(const IntRect& visibleRect, CachedImage&);
bool hasPausedImageAnimations() const { return m_hasPausedImageAnimations; }
void setHasPausedImageAnimations(bool b) { m_hasPausedImageAnimations = b; }
@@ -317,7 +317,7 @@
std::unique_ptr<RenderStyle> computeFirstLineStyle() const;
void invalidateCachedFirstLineStyle();
- void newImageAnimationFrameAvailable(CachedImage&) final;
+ void newImageAnimationFrameAvailable(CachedImage&, bool& canPause) final;
bool getLeadingCorner(FloatPoint& output, bool& insideFixed) const;
bool getTrailingCorner(FloatPoint& output, bool& insideFixed) const;
Modified: trunk/Source/WebCore/rendering/RenderView.cpp (214502 => 214503)
--- trunk/Source/WebCore/rendering/RenderView.cpp 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/rendering/RenderView.cpp 2017-03-28 23:11:35 UTC (rev 214503)
@@ -1395,14 +1395,16 @@
}
}
-void RenderView::addRendererWithPausedImageAnimations(RenderElement& renderer)
+void RenderView::addRendererWithPausedImageAnimations(RenderElement& renderer, CachedImage& image)
{
- if (renderer.hasPausedImageAnimations()) {
- ASSERT(m_renderersWithPausedImageAnimation.contains(&renderer));
- return;
- }
+ ASSERT(!renderer.hasPausedImageAnimations() || m_renderersWithPausedImageAnimation.contains(&renderer));
+
renderer.setHasPausedImageAnimations(true);
- m_renderersWithPausedImageAnimation.add(&renderer);
+ auto& images = m_renderersWithPausedImageAnimation.ensure(&renderer, [] {
+ return Vector<CachedImage*>();
+ }).iterator->value;
+ if (!images.contains(&image))
+ images.append(&image);
}
void RenderView::removeRendererWithPausedImageAnimations(RenderElement& renderer)
@@ -1414,15 +1416,35 @@
m_renderersWithPausedImageAnimation.remove(&renderer);
}
+void RenderView::removeRendererWithPausedImageAnimations(RenderElement& renderer, CachedImage& image)
+{
+ ASSERT(renderer.hasPausedImageAnimations());
+
+ auto it = m_renderersWithPausedImageAnimation.find(&renderer);
+ ASSERT(it != m_renderersWithPausedImageAnimation.end());
+
+ auto& images = it->value;
+ if (!images.contains(&image))
+ return;
+
+ if (images.size() == 1)
+ removeRendererWithPausedImageAnimations(renderer);
+ else
+ images.removeFirst(&image);
+}
+
void RenderView::resumePausedImageAnimationsIfNeeded(IntRect visibleRect)
{
- Vector<RenderElement*, 10> toRemove;
- for (auto* renderer : m_renderersWithPausedImageAnimation) {
- if (renderer->repaintForPausedImageAnimationsIfNeeded(visibleRect))
- toRemove.append(renderer);
+ Vector<std::pair<RenderElement*, CachedImage*>, 10> toRemove;
+ for (auto& it : m_renderersWithPausedImageAnimation) {
+ auto* renderer = it.key;
+ for (auto* image : it.value) {
+ if (renderer->repaintForPausedImageAnimationsIfNeeded(visibleRect, *image))
+ toRemove.append(std::make_pair(renderer, image));
+ }
}
- for (auto& renderer : toRemove)
- removeRendererWithPausedImageAnimations(*renderer);
+ for (auto& pair : toRemove)
+ removeRendererWithPausedImageAnimations(*pair.first, *pair.second);
}
RenderView::RepaintRegionAccumulator::RepaintRegionAccumulator(RenderView* view)
Modified: trunk/Source/WebCore/rendering/RenderView.h (214502 => 214503)
--- trunk/Source/WebCore/rendering/RenderView.h 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/rendering/RenderView.h 2017-03-28 23:11:35 UTC (rev 214503)
@@ -229,8 +229,9 @@
void registerForVisibleInViewportCallback(RenderElement&);
void unregisterForVisibleInViewportCallback(RenderElement&);
void resumePausedImageAnimationsIfNeeded(IntRect visibleRect);
- void addRendererWithPausedImageAnimations(RenderElement&);
+ void addRendererWithPausedImageAnimations(RenderElement&, CachedImage&);
void removeRendererWithPausedImageAnimations(RenderElement&);
+ void removeRendererWithPausedImageAnimations(RenderElement&, CachedImage&);
class RepaintRegionAccumulator {
WTF_MAKE_NONCOPYABLE(RepaintRegionAccumulator);
@@ -389,7 +390,7 @@
bool m_inHitTesting { false };
#endif
- HashSet<RenderElement*> m_renderersWithPausedImageAnimation;
+ HashMap<RenderElement*, Vector<CachedImage*>> m_renderersWithPausedImageAnimation;
HashSet<RenderElement*> m_visibleInViewportRenderers;
Vector<RefPtr<RenderWidget>> m_protectedRenderWidgets;
Modified: trunk/Source/WebCore/rendering/style/StyleCachedImage.cpp (214502 => 214503)
--- trunk/Source/WebCore/rendering/style/StyleCachedImage.cpp 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/rendering/style/StyleCachedImage.cpp 2017-03-28 23:11:35 UTC (rev 214503)
@@ -29,6 +29,7 @@
#include "CSSImageValue.h"
#include "CachedImage.h"
#include "RenderElement.h"
+#include "RenderView.h"
namespace WebCore {
@@ -186,6 +187,10 @@
if (!m_cachedImage)
return;
ASSERT(renderer);
+
+ if (renderer->hasPausedImageAnimations())
+ renderer->view().removeRendererWithPausedImageAnimations(*renderer, *m_cachedImage);
+
m_cachedImage->removeClient(*renderer);
}
Modified: trunk/Source/WebCore/svg/SVGSVGElement.cpp (214502 => 214503)
--- trunk/Source/WebCore/svg/SVGSVGElement.cpp 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/svg/SVGSVGElement.cpp 2017-03-28 23:11:35 UTC (rev 214503)
@@ -507,6 +507,11 @@
return m_timeContainer->isPaused();
}
+bool SVGSVGElement::hasActiveAnimation() const
+{
+ return m_timeContainer->isActive();
+}
+
float SVGSVGElement::getCurrentTime() const
{
return narrowPrecisionToFloat(m_timeContainer->elapsed().value());
Modified: trunk/Source/WebCore/svg/SVGSVGElement.h (214502 => 214503)
--- trunk/Source/WebCore/svg/SVGSVGElement.h 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/svg/SVGSVGElement.h 2017-03-28 23:11:35 UTC (rev 214503)
@@ -85,6 +85,7 @@
void pauseAnimations();
void unpauseAnimations();
bool animationsPaused() const;
+ bool hasActiveAnimation() const;
float getCurrentTime() const;
void setCurrentTime(float);
Modified: trunk/Source/WebCore/svg/graphics/SVGImage.cpp (214502 => 214503)
--- trunk/Source/WebCore/svg/graphics/SVGImage.cpp 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/svg/graphics/SVGImage.cpp 2017-03-28 23:11:35 UTC (rev 214503)
@@ -374,7 +374,7 @@
void SVGImage::startAnimation()
{
SVGSVGElement* rootElement = this->rootElement();
- if (!rootElement)
+ if (!rootElement || !rootElement->animationsPaused())
return;
rootElement->unpauseAnimations();
rootElement->setCurrentTime(0);
@@ -393,6 +393,14 @@
stopAnimation();
}
+bool SVGImage::isAnimating() const
+{
+ SVGSVGElement* rootElement = this->rootElement();
+ if (!rootElement)
+ return false;
+ return rootElement->hasActiveAnimation();
+}
+
void SVGImage::reportApproximateMemoryCost() const
{
Document* document = m_page->mainFrame().document();
Modified: trunk/Source/WebCore/svg/graphics/SVGImage.h (214502 => 214503)
--- trunk/Source/WebCore/svg/graphics/SVGImage.h 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/svg/graphics/SVGImage.h 2017-03-28 23:11:35 UTC (rev 214503)
@@ -63,6 +63,7 @@
void startAnimation() final;
void stopAnimation() final;
void resetAnimation() final;
+ bool isAnimating() const final;
#if USE(CAIRO)
NativeImagePtr nativeImageForCurrentFrame(const GraphicsContext* = nullptr) final;
Modified: trunk/Source/WebCore/svg/graphics/SVGImageClients.h (214502 => 214503)
--- trunk/Source/WebCore/svg/graphics/SVGImageClients.h 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/svg/graphics/SVGImageClients.h 2017-03-28 23:11:35 UTC (rev 214503)
@@ -29,6 +29,7 @@
#pragma once
#include "EmptyClients.h"
+#include "SVGImage.h"
namespace WebCore {
@@ -52,8 +53,17 @@
void invalidateContentsAndRootView(const IntRect& r) final
{
// If m_image->m_page is null, we're being destructed, don't fire changedInRect() in that case.
- if (m_image && m_image->imageObserver() && m_image->m_page)
- m_image->imageObserver()->changedInRect(m_image, &r);
+ if (!m_image || !m_image->m_page)
+ return;
+
+ auto* imageObserver = m_image->imageObserver();
+ if (!imageObserver)
+ return;
+
+ if (m_image->isAnimating())
+ imageObserver->animationAdvanced(m_image);
+ else
+ imageObserver->changedInRect(m_image, &r);
}
SVGImage* m_image;
Modified: trunk/Source/WebCore/testing/Internals.cpp (214502 => 214503)
--- trunk/Source/WebCore/testing/Internals.cpp 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/testing/Internals.cpp 2017-03-28 23:11:35 UTC (rev 214503)
@@ -744,6 +744,19 @@
image->resetAnimation();
}
+bool Internals::isImageAnimating(HTMLImageElement& element)
+{
+ auto* cachedImage = element.cachedImage();
+ if (!cachedImage)
+ return false;
+
+ auto* image = cachedImage->image();
+ if (!image)
+ return false;
+
+ return image->isAnimating();
+}
+
void Internals::setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement& element, bool value)
{
auto* cachedImage = element.cachedImage();
Modified: trunk/Source/WebCore/testing/Internals.h (214502 => 214503)
--- trunk/Source/WebCore/testing/Internals.h 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/testing/Internals.h 2017-03-28 23:11:35 UTC (rev 214503)
@@ -115,6 +115,7 @@
unsigned imageFrameIndex(HTMLImageElement&);
void setImageFrameDecodingDuration(HTMLImageElement&, float duration);
void resetImageAnimation(HTMLImageElement&);
+ bool isImageAnimating(HTMLImageElement&);
void setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement&, bool);
void clearPageCache();
Modified: trunk/Source/WebCore/testing/Internals.idl (214502 => 214503)
--- trunk/Source/WebCore/testing/Internals.idl 2017-03-28 23:00:36 UTC (rev 214502)
+++ trunk/Source/WebCore/testing/Internals.idl 2017-03-28 23:11:35 UTC (rev 214503)
@@ -244,6 +244,7 @@
unsigned long imageFrameIndex(HTMLImageElement element);
void setImageFrameDecodingDuration(HTMLImageElement element, unrestricted float duration);
void resetImageAnimation(HTMLImageElement element);
+ boolean isImageAnimating(HTMLImageElement element);
void setClearDecoderAfterAsyncFrameRequestForTesting(HTMLImageElement element, boolean value);
readonly attribute InternalSettings settings;