SVG animations are not paused when inserted into a hidden page
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 23 Mar 2017 22:40:50 +0000 (22:40 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 23 Mar 2017 22:40:50 +0000 (22:40 +0000)
https://bugs.webkit.org/show_bug.cgi?id=170026
<rdar://problem/31228704>

Reviewed by Andreas Kling.

Source/WebCore:

SVG animations were not paused when inserted into a hidden page. We would pause
animations in a page when the page becomes hidden. However, new animations
inserted in the page after this point would start, despite the page being
hidden.

Tests:
- svg/animations/animations-paused-when-inserted-in-hidden-document.html
- svg/animations/animations-paused-when-inserted-in-hidden-document2.html

* dom/Document.cpp:
(WebCore::Document::accessSVGExtensions):
* svg/SVGDocumentExtensions.cpp:
(WebCore::SVGDocumentExtensions::SVGDocumentExtensions):
(WebCore::SVGDocumentExtensions::addTimeContainer):
(WebCore::reportMessage):
* svg/SVGDocumentExtensions.h:
* testing/Internals.cpp:
(WebCore::Internals::areSVGAnimationsPaused):
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

Add layout test coverage.

* svg/animations/animations-paused-when-inserted-in-hidden-document-expected.txt: Added.
* svg/animations/animations-paused-when-inserted-in-hidden-document.html: Added.
* svg/animations/animations-paused-when-inserted-in-hidden-document2-expected.txt: Added.
* svg/animations/animations-paused-when-inserted-in-hidden-document2.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@214322 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
LayoutTests/ChangeLog
LayoutTests/svg/animations/animations-paused-when-inserted-in-hidden-document-expected.txt [new file with mode: 0644]
LayoutTests/svg/animations/animations-paused-when-inserted-in-hidden-document.html [new file with mode: 0644]
LayoutTests/svg/animations/animations-paused-when-inserted-in-hidden-document2-expected.txt [new file with mode: 0644]
LayoutTests/svg/animations/animations-paused-when-inserted-in-hidden-document2.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/svg/SVGDocumentExtensions.cpp
Source/WebCore/svg/SVGDocumentExtensions.h
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl

index 53ccc93..6b191d5 100644 (file)
@@ -1 +1,16 @@
+2017-03-23  Chris Dumez  <cdumez@apple.com>
+
+        SVG animations are not paused when inserted into a hidden page
+        https://bugs.webkit.org/show_bug.cgi?id=170026
+        <rdar://problem/31228704>
+
+        Reviewed by Andreas Kling.
+
+        Add layout test coverage.
+
+        * svg/animations/animations-paused-when-inserted-in-hidden-document-expected.txt: Added.
+        * svg/animations/animations-paused-when-inserted-in-hidden-document.html: Added.
+        * svg/animations/animations-paused-when-inserted-in-hidden-document2-expected.txt: Added.
+        * svg/animations/animations-paused-when-inserted-in-hidden-document2.html: Added.
+
 == Rolled over to ChangeLog-2017-03-23 ==
diff --git a/LayoutTests/svg/animations/animations-paused-when-inserted-in-hidden-document-expected.txt b/LayoutTests/svg/animations/animations-paused-when-inserted-in-hidden-document-expected.txt
new file mode 100644 (file)
index 0000000..032380a
--- /dev/null
@@ -0,0 +1,20 @@
+Tests that SVG animations are properly paused in iframes that are inserted into hidden pages.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Setting page visibility to hidden
+Inserting frame in document
+PASS frame.contentWindow.internals.areSVGAnimationsPaused is true
+PASS frame.contentDocument.getElementsByTagName('svg')[0].animationsPaused() is true
+PASS grandChildFrame.contentWindow.internals.areSVGAnimationsPaused is true
+PASS grandChildFrame.contentDocument.getElementsByTagName('svg')[0].animationsPaused() is true
+Setting page visibility to visible
+PASS frame.contentWindow.internals.areSVGAnimationsPaused is false
+PASS frame.contentDocument.getElementsByTagName('svg')[0].animationsPaused() is false
+PASS grandChildFrame.contentWindow.internals.areSVGAnimationsPaused is false
+PASS grandChildFrame.contentDocument.getElementsByTagName('svg')[0].animationsPaused() is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/svg/animations/animations-paused-when-inserted-in-hidden-document.html b/LayoutTests/svg/animations/animations-paused-when-inserted-in-hidden-document.html
new file mode 100644 (file)
index 0000000..1f2709d
--- /dev/null
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/js-test-pre.js"></script>
+<script>
+description("Tests that SVG animations are properly paused in iframes that are inserted into hidden pages.");
+jsTestIsAsync = true;
+
+document.addEventListener("visibilitychange", function() {
+    if (document.hidden) {
+        frame = document.createElement("iframe");
+        frame.onload = function() {
+            setTimeout(function() {
+                shouldBeTrue("frame.contentWindow.internals.areSVGAnimationsPaused");
+                shouldBeTrue("frame.contentDocument.getElementsByTagName('svg')[0].animationsPaused()");
+                grandChildFrame = frame.contentDocument.getElementById('grandChildFrame');
+                shouldBeTrue("grandChildFrame.contentWindow.internals.areSVGAnimationsPaused");
+                shouldBeTrue("grandChildFrame.contentDocument.getElementsByTagName('svg')[0].animationsPaused()");
+
+                debug("Setting page visibility to visible"); 
+                if (window.testRunner)
+                    testRunner.setPageVisibility("visible");
+            }, 0);
+        }
+        frame.src = "resources/frame-with-svg-animation.html";
+        debug("Inserting frame in document");
+        document.body.appendChild(frame);
+    } else {
+        shouldBeFalse("frame.contentWindow.internals.areSVGAnimationsPaused");
+        shouldBeFalse("frame.contentDocument.getElementsByTagName('svg')[0].animationsPaused()");
+        grandChildFrame = frame.contentDocument.getElementById('grandChildFrame');
+        shouldBeFalse("grandChildFrame.contentWindow.internals.areSVGAnimationsPaused");
+        shouldBeFalse("grandChildFrame.contentDocument.getElementsByTagName('svg')[0].animationsPaused()");
+        finishJSTest();
+    }
+});
+
+window.onload = function() {
+    debug("Setting page visibility to hidden");
+    if (window.testRunner)
+        testRunner.setPageVisibility("hidden");
+}
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/svg/animations/animations-paused-when-inserted-in-hidden-document2-expected.txt b/LayoutTests/svg/animations/animations-paused-when-inserted-in-hidden-document2-expected.txt
new file mode 100644 (file)
index 0000000..bf353b3
--- /dev/null
@@ -0,0 +1,16 @@
+Tests that SVG animations are properly paused in iframes that are inserted into hidden pages.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Setting page visibility to hidden
+Inserting SVG animation in the document
+PASS internals.areSVGAnimationsPaused is true
+PASS svgElement.animationsPaused() is true
+Setting page visibility to visible
+PASS internals.areSVGAnimationsPaused is false
+PASS svgElement.animationsPaused() is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/svg/animations/animations-paused-when-inserted-in-hidden-document2.html b/LayoutTests/svg/animations/animations-paused-when-inserted-in-hidden-document2.html
new file mode 100644 (file)
index 0000000..377e026
--- /dev/null
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/js-test-pre.js"></script>
+<script>
+description("Tests that SVG animations are properly paused in iframes that are inserted into hidden pages.");
+jsTestIsAsync = true;
+
+var animationAsText = '<svg width="300px" height="100px"><rect x="0" y="0" width="300" height="100" stroke="black" stroke-width="1" /><circle cx="0" cy="50" r="15" fill="blue" stroke="black" stroke-width="1"><animate attributeName="cx" from="0" to="100" dur="5s" repeatCount="indefinite" /></circle></svg>';
+
+document.addEventListener("visibilitychange", function() {
+    if (document.hidden) {
+        debug("Inserting SVG animation in the document");
+        testDiv.innerHTML = animationAsText;
+        svgElement = document.getElementsByTagName("svg")[0];
+        
+        setTimeout(function() {
+            shouldBeTrue("internals.areSVGAnimationsPaused");
+            shouldBeTrue("svgElement.animationsPaused()");
+
+            debug("Setting page visibility to visible"); 
+            if (window.testRunner)
+                testRunner.setPageVisibility("visible");
+            }, 0);
+    } else {
+        shouldBeFalse("internals.areSVGAnimationsPaused");
+        shouldBeFalse("svgElement.animationsPaused()");
+        finishJSTest();
+    }
+});
+
+window.onload = function() {
+    debug("Setting page visibility to hidden");
+    if (window.testRunner)
+        testRunner.setPageVisibility("hidden");
+}
+</script>
+<div id="testDiv"></div>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 53ccc93..ea326f1 100644 (file)
@@ -1 +1,30 @@
+2017-03-23  Chris Dumez  <cdumez@apple.com>
+
+        SVG animations are not paused when inserted into a hidden page
+        https://bugs.webkit.org/show_bug.cgi?id=170026
+        <rdar://problem/31228704>
+
+        Reviewed by Andreas Kling.
+
+        SVG animations were not paused when inserted into a hidden page. We would pause
+        animations in a page when the page becomes hidden. However, new animations
+        inserted in the page after this point would start, despite the page being
+        hidden.
+
+        Tests:
+        - svg/animations/animations-paused-when-inserted-in-hidden-document.html
+        - svg/animations/animations-paused-when-inserted-in-hidden-document2.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::accessSVGExtensions):
+        * svg/SVGDocumentExtensions.cpp:
+        (WebCore::SVGDocumentExtensions::SVGDocumentExtensions):
+        (WebCore::SVGDocumentExtensions::addTimeContainer):
+        (WebCore::reportMessage):
+        * svg/SVGDocumentExtensions.h:
+        * testing/Internals.cpp:
+        (WebCore::Internals::areSVGAnimationsPaused):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 == Rolled over to ChangeLog-2017-03-23 ==
index acd7f30..38485f4 100644 (file)
@@ -4873,7 +4873,7 @@ const SVGDocumentExtensions* Document::svgExtensions()
 SVGDocumentExtensions& Document::accessSVGExtensions()
 {
     if (!m_svgExtensions)
-        m_svgExtensions = std::make_unique<SVGDocumentExtensions>(this);
+        m_svgExtensions = std::make_unique<SVGDocumentExtensions>(*this);
     return *m_svgExtensions;
 }
 
index cad83c9..eb8154c 100644 (file)
@@ -27,6 +27,7 @@
 #include "EventListener.h"
 #include "Frame.h"
 #include "FrameLoader.h"
+#include "Page.h"
 #include "SMILTimeContainer.h"
 #include "SVGElement.h"
 #include "SVGResourcesCache.h"
 
 namespace WebCore {
 
-SVGDocumentExtensions::SVGDocumentExtensions(Document* document)
+SVGDocumentExtensions::SVGDocumentExtensions(Document& document)
     : m_document(document)
     , m_resourcesCache(std::make_unique<SVGResourcesCache>())
+    , m_areAnimationsPaused(!document.page() || !document.page()->isVisible())
 {
 }
 
@@ -52,6 +54,8 @@ SVGDocumentExtensions::~SVGDocumentExtensions()
 void SVGDocumentExtensions::addTimeContainer(SVGSVGElement* element)
 {
     m_timeContainers.add(element);
+    if (m_areAnimationsPaused)
+        element->pauseAnimations();
 }
 
 void SVGDocumentExtensions::removeTimeContainer(SVGSVGElement* element)
@@ -124,10 +128,10 @@ void SVGDocumentExtensions::dispatchSVGLoadEventToOutermostSVGElements()
     }
 }
 
-static void reportMessage(Document* document, MessageLevel level, const String& message)
+static void reportMessage(Document& document, MessageLevel level, const String& message)
 {
-    if (document->frame())
-        document->addConsoleMessage(MessageSource::Rendering, level, message);
+    if (document.frame())
+        document.addConsoleMessage(MessageSource::Rendering, level, message);
 }
 
 void SVGDocumentExtensions::reportWarning(const String& message)
index 058f20d..6a52770 100644 (file)
@@ -40,7 +40,7 @@ class SVGDocumentExtensions {
     WTF_MAKE_NONCOPYABLE(SVGDocumentExtensions); WTF_MAKE_FAST_ALLOCATED;
 public:
     typedef HashSet<Element*> PendingElements;
-    explicit SVGDocumentExtensions(Document*);
+    explicit SVGDocumentExtensions(Document&);
     ~SVGDocumentExtensions();
     
     void addTimeContainer(SVGSVGElement*);
@@ -77,7 +77,7 @@ public:
 #endif
 
 private:
-    Document* m_document; // weak reference
+    Document& m_document;
     HashSet<SVGSVGElement*> m_timeContainers; // For SVG 1.2 support this will need to be made more general.
 #if ENABLE(SVG_FONTS)
     HashSet<SVGFontFaceElement*> m_svgFontFaceElements;
@@ -89,7 +89,7 @@ private:
     std::unique_ptr<SVGResourcesCache> m_resourcesCache;
 
     Vector<SVGElement*> m_rebuildElements;
-    bool m_areAnimationsPaused { false }; // For testing.
+    bool m_areAnimationsPaused;
 
 public:
     // This HashMap contains a list of pending resources. Pending resources, are such
index 4330d24..9d390f9 100644 (file)
@@ -517,14 +517,14 @@ unsigned Internals::workerThreadCount() const
     return WorkerThread::workerThreadCount();
 }
 
-bool Internals::areSVGAnimationsPaused() const
+ExceptionOr<bool> Internals::areSVGAnimationsPaused() const
 {
     auto* document = contextDocument();
     if (!document)
-        return false;
+        return Exception { INVALID_ACCESS_ERR, ASCIILiteral("No context document") };
 
     if (!document->svgExtensions())
-        return false;
+        return Exception { NOT_FOUND_ERR, ASCIILiteral("No SVG animations") };
 
     return document->accessSVGExtensions().areAnimationsPaused();
 }
index 52c4d59..383d05c 100644 (file)
@@ -266,7 +266,7 @@ public:
 
     InternalSettings* settings() const;
     unsigned workerThreadCount() const;
-    bool areSVGAnimationsPaused() const;
+    ExceptionOr<bool> areSVGAnimationsPaused() const;
     ExceptionOr<double> svgAnimationsInterval(SVGSVGElement&) const;
 
     ExceptionOr<void> setDeviceProximity(const String& eventType, double value, double min, double max);
index a65c384..722507c 100644 (file)
@@ -249,7 +249,7 @@ enum EventThrottlingBehavior {
     readonly attribute InternalSettings settings;
     readonly attribute unsigned long workerThreadCount;
 
-    readonly attribute boolean areSVGAnimationsPaused;
+    [MayThrowException] readonly attribute boolean areSVGAnimationsPaused;
     [MayThrowException] double svgAnimationsInterval(SVGSVGElement element);
 
     // Flags for layerTreeAsText.