Title: [214503] trunk
Revision
214503
Author
[email protected]
Date
2017-03-28 16:11:35 -0700 (Tue, 28 Mar 2017)

Log Message

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.

Source/WebCore:

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.

LayoutTests:

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.

Modified Paths

Added Paths

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;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to