Title: [185012] trunk
Revision
185012
Author
[email protected]
Date
2015-05-29 16:02:36 -0700 (Fri, 29 May 2015)

Log Message

Consider throttling DOM timers in iframes outside the viewport
https://bugs.webkit.org/show_bug.cgi?id=145465
<rdar://problem/20768957>

Reviewed by Darin Adler.

Source/WebCore:

Throttle DOM timers in iframes that are outside the viewport to decrease
CPU usage, improve performance and reduce power use.

The approach is similar to what we already did for requestAnimationFrame
in r183998.

We already has support for throttling DOM timers at:
- Page level: for backgound pages
- DOM timer level: for timers changing the style of an element outside
  the viewport or drawing on a canvas outside the viewport.

This patch adds support for throttling DOM timers at Document level so
we can throttle all timers inside a specific iframe / Document. It relies
on the same timerAlignmentInterval that is used for throttling at Page
level with tweaks so that different Documents inside the same Page can
have a different timerAlignmentInterval.

Test: fast/dom/timer-throttling-subframe.html

* dom/Document.cpp:
(WebCore::Document::setTimerThrottlingEnabled):
(WebCore::Document::timerAlignmentInterval):
* dom/Document.h:
(WebCore::Document::isTimerThrottlingEnabled):
* page/DOMTimer.cpp:
(WebCore::DOMTimer::alignedFireTime):
The previous code was not throttling the timer if its fireTime was in
the past. This was causing some aggressive timers on mashable.com to
not be throttled so I got rid of this behavior. I don't see any reason
why we would not throttle a timer simply because it is supposed to have
fired already.

* page/FrameView.cpp:
(WebCore::FrameView::viewportContentsChanged):
(WebCore::FrameView::updateScriptedAnimationsAndTimersThrottlingState):
* page/FrameView.h:

* testing/Internals.cpp:
(WebCore::Internals::areTimersThrottled):
* testing/Internals.h:
* testing/Internals.idl:
Add API to facilitate layout testing of this functionality.

LayoutTests:

Add a layout test to check that DOM timers in iframes outside the
viewport get throttled.

* fast/dom/resources/timer-frame-2.html: Added.
* fast/dom/resources/timer-frame.html: Added.
* fast/dom/timer-throttling-subframe-expected.txt: Added.
* fast/dom/timer-throttling-subframe.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (185011 => 185012)


--- trunk/LayoutTests/ChangeLog	2015-05-29 23:01:37 UTC (rev 185011)
+++ trunk/LayoutTests/ChangeLog	2015-05-29 23:02:36 UTC (rev 185012)
@@ -1,3 +1,19 @@
+2015-05-29  Chris Dumez  <[email protected]>
+
+        Consider throttling DOM timers in iframes outside the viewport
+        https://bugs.webkit.org/show_bug.cgi?id=145465
+        <rdar://problem/20768957>
+
+        Reviewed by Darin Adler.
+
+        Add a layout test to check that DOM timers in iframes outside the
+        viewport get throttled.
+
+        * fast/dom/resources/timer-frame-2.html: Added.
+        * fast/dom/resources/timer-frame.html: Added.
+        * fast/dom/timer-throttling-subframe-expected.txt: Added.
+        * fast/dom/timer-throttling-subframe.html: Added.
+
 2015-05-28  Filip Pizlo  <[email protected]>
 
         Non-speculative Branch should be fast in the FTL

Added: trunk/LayoutTests/fast/dom/resources/timer-frame-2.html (0 => 185012)


--- trunk/LayoutTests/fast/dom/resources/timer-frame-2.html	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/resources/timer-frame-2.html	2015-05-29 23:02:36 UTC (rev 185012)
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script>
+var i = 0;
+setInterval(function() {
+  i++;
+}, 100);
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/fast/dom/resources/timer-frame.html (0 => 185012)


--- trunk/LayoutTests/fast/dom/resources/timer-frame.html	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/resources/timer-frame.html	2015-05-29 23:02:36 UTC (rev 185012)
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<body>
+<iframe id="grandChildFrame" src=""
+<script>
+var i = 0;
+setInterval(function() {
+  i++;
+}, 100);
+</script>
+</body>
+</html>

Added: trunk/LayoutTests/fast/dom/timer-throttling-subframe-expected.txt (0 => 185012)


--- trunk/LayoutTests/fast/dom/timer-throttling-subframe-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/timer-throttling-subframe-expected.txt	2015-05-29 23:02:36 UTC (rev 185012)
@@ -0,0 +1,24 @@
+Tests that timers are throttled in subframes that are outside the viewport
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Frame is initially outside the viewport so timers should be throttled
+PASS testFrame.contentWindow.internals.areTimersThrottled() became true
+PASS internals.areTimersThrottled() is false
+PASS testFrame.contentWindow.internals.areTimersThrottled() is true
+PASS grandChildFrame.contentWindow.internals.areTimersThrottled() is true
+Scrolling frame into view.
+Timers should no longer be throttled
+PASS internals.areTimersThrottled() is false
+PASS testFrame.contentWindow.internals.areTimersThrottled() is false
+PASS grandChildFrame.contentWindow.internals.areTimersThrottled() is false
+Scrolling frame out of view again.
+PASS internals.areTimersThrottled() is false
+PASS testFrame.contentWindow.internals.areTimersThrottled() became true
+PASS testFrame.contentWindow.internals.areTimersThrottled() is true
+PASS grandChildFrame.contentWindow.internals.areTimersThrottled() is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/dom/timer-throttling-subframe.html (0 => 185012)


--- trunk/LayoutTests/fast/dom/timer-throttling-subframe.html	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/timer-throttling-subframe.html	2015-05-29 23:02:36 UTC (rev 185012)
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+description("Tests that timers are throttled in subframes that are outside the viewport");
+window.jsTestIsAsync = true;
+
+function checkSubframesThrottled()
+{
+    shouldBeTrue("testFrame.contentWindow.internals.areTimersThrottled()");
+    shouldBeTrue("grandChildFrame.contentWindow.internals.areTimersThrottled()");
+
+    finishJSTest();
+}
+
+function scrollFrameOutOfView()
+{
+    debug("Scrolling frame out of view again.");
+    window.scroll(0, 0);
+
+    shouldBeFalse("internals.areTimersThrottled()");
+    shouldBecomeEqual("testFrame.contentWindow.internals.areTimersThrottled()", "true", checkSubframesThrottled);
+}
+
+function scrollFrameIntoView()
+{
+    shouldBeFalse("internals.areTimersThrottled()");
+    shouldBeTrue("testFrame.contentWindow.internals.areTimersThrottled()");
+    shouldBeTrue("grandChildFrame.contentWindow.internals.areTimersThrottled()");
+
+    debug("Scrolling frame into view.");
+    window.internals.scrollElementToRect(testFrame, 0, 0, 300, 300);
+
+    debug("Timers should no longer be throttled");
+    shouldBeFalse("internals.areTimersThrottled()");
+    shouldBeFalse("testFrame.contentWindow.internals.areTimersThrottled()");
+    shouldBeFalse("grandChildFrame.contentWindow.internals.areTimersThrottled()");
+
+    scrollFrameOutOfView();
+}
+
+function runTest()
+{
+    testFrame = document.getElementById("testFrame");
+    grandChildFrame = testFrame.contentDocument.getElementById("grandChildFrame");
+    debug("Frame is initially outside the viewport so timers should be throttled");
+    shouldBecomeEqual("testFrame.contentWindow.internals.areTimersThrottled()", "true", scrollFrameIntoView);
+}
+
+var i = 0;
+setInterval(function() {
+    i++;
+}, 100);
+</script>
+<div style="position: relative; width: 1600px; height: 2400px; background-color: green;">
+    <iframe id="testFrame" src="" style="position:absolute; left: 600px; top: 800px;" _onload_="runTest()"></iframe>
+</div>
+<script src=""
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (185011 => 185012)


--- trunk/Source/WebCore/ChangeLog	2015-05-29 23:01:37 UTC (rev 185011)
+++ trunk/Source/WebCore/ChangeLog	2015-05-29 23:02:36 UTC (rev 185012)
@@ -1,3 +1,54 @@
+2015-05-29  Chris Dumez  <[email protected]>
+
+        Consider throttling DOM timers in iframes outside the viewport
+        https://bugs.webkit.org/show_bug.cgi?id=145465
+        <rdar://problem/20768957>
+
+        Reviewed by Darin Adler.
+
+        Throttle DOM timers in iframes that are outside the viewport to decrease
+        CPU usage, improve performance and reduce power use.
+
+        The approach is similar to what we already did for requestAnimationFrame
+        in r183998.
+
+        We already has support for throttling DOM timers at:
+        - Page level: for backgound pages
+        - DOM timer level: for timers changing the style of an element outside
+          the viewport or drawing on a canvas outside the viewport.
+
+        This patch adds support for throttling DOM timers at Document level so
+        we can throttle all timers inside a specific iframe / Document. It relies
+        on the same timerAlignmentInterval that is used for throttling at Page
+        level with tweaks so that different Documents inside the same Page can
+        have a different timerAlignmentInterval.
+
+        Test: fast/dom/timer-throttling-subframe.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::setTimerThrottlingEnabled):
+        (WebCore::Document::timerAlignmentInterval):
+        * dom/Document.h:
+        (WebCore::Document::isTimerThrottlingEnabled):
+        * page/DOMTimer.cpp:
+        (WebCore::DOMTimer::alignedFireTime):
+        The previous code was not throttling the timer if its fireTime was in
+        the past. This was causing some aggressive timers on mashable.com to
+        not be throttled so I got rid of this behavior. I don't see any reason
+        why we would not throttle a timer simply because it is supposed to have
+        fired already.
+
+        * page/FrameView.cpp:
+        (WebCore::FrameView::viewportContentsChanged):
+        (WebCore::FrameView::updateScriptedAnimationsAndTimersThrottlingState):
+        * page/FrameView.h:
+
+        * testing/Internals.cpp:
+        (WebCore::Internals::areTimersThrottled):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+        Add API to facilitate layout testing of this functionality.
+
 2015-05-29  Brady Eidson  <[email protected]>
 
         NavigationAction constructor cleanup.

Modified: trunk/Source/WebCore/dom/Document.cpp (185011 => 185012)


--- trunk/Source/WebCore/dom/Document.cpp	2015-05-29 23:01:37 UTC (rev 185011)
+++ trunk/Source/WebCore/dom/Document.cpp	2015-05-29 23:02:36 UTC (rev 185012)
@@ -2750,8 +2750,24 @@
     return page->settings().minimumDOMTimerInterval();
 }
 
+void Document::setTimerThrottlingEnabled(bool shouldThrottle)
+{
+    if (m_isTimerThrottlingEnabled == shouldThrottle)
+        return;
+
+    double previousInterval = timerAlignmentInterval();
+
+    m_isTimerThrottlingEnabled = shouldThrottle;
+
+    if (previousInterval != timerAlignmentInterval())
+        didChangeTimerAlignmentInterval();
+}
+
 double Document::timerAlignmentInterval() const
 {
+    if (m_isTimerThrottlingEnabled)
+        return DOMTimer::hiddenPageAlignmentInterval();
+
     Page* page = this->page();
     if (!page)
         return ScriptExecutionContext::timerAlignmentInterval();

Modified: trunk/Source/WebCore/dom/Document.h (185011 => 185012)


--- trunk/Source/WebCore/dom/Document.h	2015-05-29 23:01:37 UTC (rev 185011)
+++ trunk/Source/WebCore/dom/Document.h	2015-05-29 23:02:36 UTC (rev 185012)
@@ -431,6 +431,9 @@
     String visibilityState() const;
     bool hidden() const;
 
+    void setTimerThrottlingEnabled(bool);
+    bool isTimerThrottlingEnabled() const { return m_isTimerThrottlingEnabled; }
+
 #if ENABLE(CSP_NEXT)
     DOMSecurityPolicy& securityPolicy();
 #endif
@@ -1688,6 +1691,7 @@
     bool m_hasPreparedForDestruction;
 
     bool m_hasStyleWithViewportUnits;
+    bool m_isTimerThrottlingEnabled { false };
 
     HashSet<MediaProducer*> m_audioProducers;
     MediaProducer::MediaStateFlags m_mediaState { MediaProducer::IsNotPlaying };

Modified: trunk/Source/WebCore/page/DOMTimer.cpp (185011 => 185012)


--- trunk/Source/WebCore/page/DOMTimer.cpp	2015-05-29 23:01:37 UTC (rev 185011)
+++ trunk/Source/WebCore/page/DOMTimer.cpp	2015-05-29 23:02:36 UTC (rev 185012)
@@ -507,16 +507,9 @@
 
 double DOMTimer::alignedFireTime(double fireTime) const
 {
-    double alignmentInterval = scriptExecutionContext()->timerAlignmentInterval();
-    if (alignmentInterval) {
-        double currentTime = monotonicallyIncreasingTime();
-        if (fireTime <= currentTime)
-            return fireTime;
+    if (double alignmentInterval = scriptExecutionContext()->timerAlignmentInterval())
+        return ceil(fireTime / alignmentInterval) * alignmentInterval;
 
-        double alignedTime = ceil(fireTime / alignmentInterval) * alignmentInterval;
-        return alignedTime;
-    }
-
     return fireTime;
 }
 

Modified: trunk/Source/WebCore/page/FrameView.cpp (185011 => 185012)


--- trunk/Source/WebCore/page/FrameView.cpp	2015-05-29 23:01:37 UTC (rev 185011)
+++ trunk/Source/WebCore/page/FrameView.cpp	2015-05-29 23:02:36 UTC (rev 185012)
@@ -1807,7 +1807,7 @@
     applyRecursivelyWithVisibleRect([] (FrameView& frameView, const IntRect& visibleRect) {
         frameView.resumeVisibleImageAnimations(visibleRect);
         frameView.updateThrottledDOMTimersState(visibleRect);
-        frameView.updateScriptedAnimationsThrottlingState(visibleRect);
+        frameView.updateScriptedAnimationsAndTimersThrottlingState(visibleRect);
     });
 }
 
@@ -2168,9 +2168,8 @@
         renderView->resumePausedImageAnimationsIfNeeded(visibleRect);
 }
 
-void FrameView::updateScriptedAnimationsThrottlingState(const IntRect& visibleRect)
+void FrameView::updateScriptedAnimationsAndTimersThrottlingState(const IntRect& visibleRect)
 {
-#if ENABLE(REQUEST_ANIMATION_FRAME)
     if (frame().isMainFrame())
         return;
 
@@ -2178,17 +2177,16 @@
     if (!document)
         return;
 
-    auto* scriptedAnimationController = document->scriptedAnimationController();
-    if (!scriptedAnimationController)
-        return;
-
     // FIXME: This doesn't work for subframes of a "display: none" frame because
     // they have a non-null ownerRenderer.
     bool shouldThrottle = !frame().ownerRenderer() || visibleRect.isEmpty();
-    scriptedAnimationController->setThrottled(shouldThrottle);
-#else
-    UNUSED_PARAM(visibleRect);
+
+#if ENABLE(REQUEST_ANIMATION_FRAME)
+    if (auto* scriptedAnimationController = document->scriptedAnimationController())
+        scriptedAnimationController->setThrottled(shouldThrottle);
 #endif
+
+    document->setTimerThrottlingEnabled(shouldThrottle);
 }
 
 

Modified: trunk/Source/WebCore/page/FrameView.h (185011 => 185012)


--- trunk/Source/WebCore/page/FrameView.h	2015-05-29 23:01:37 UTC (rev 185011)
+++ trunk/Source/WebCore/page/FrameView.h	2015-05-29 23:02:36 UTC (rev 185012)
@@ -606,7 +606,7 @@
     void applyRecursivelyWithVisibleRect(const std::function<void (FrameView& frameView, const IntRect& visibleRect)>&);
     void updateThrottledDOMTimersState(const IntRect& visibleRect);
     void resumeVisibleImageAnimations(const IntRect& visibleRect);
-    void updateScriptedAnimationsThrottlingState(const IntRect& visibleRect);
+    void updateScriptedAnimationsAndTimersThrottlingState(const IntRect& visibleRect);
 
     void updateLayerFlushThrottling();
     WEBCORE_EXPORT void adjustTiledBackingCoverage();

Modified: trunk/Source/WebCore/testing/Internals.cpp (185011 => 185012)


--- trunk/Source/WebCore/testing/Internals.cpp	2015-05-29 23:01:37 UTC (rev 185011)
+++ trunk/Source/WebCore/testing/Internals.cpp	2015-05-29 23:02:36 UTC (rev 185012)
@@ -799,6 +799,11 @@
 #endif
 }
 
+bool Internals::areTimersThrottled() const
+{
+    return contextDocument()->isTimerThrottlingEnabled();
+}
+
 String Internals::visiblePlaceholder(Element* element)
 {
     if (is<HTMLTextFormControlElement>(element)) {

Modified: trunk/Source/WebCore/testing/Internals.h (185011 => 185012)


--- trunk/Source/WebCore/testing/Internals.h	2015-05-29 23:01:37 UTC (rev 185011)
+++ trunk/Source/WebCore/testing/Internals.h	2015-05-29 23:02:36 UTC (rev 185012)
@@ -116,6 +116,7 @@
     // DOMTimers throttling testing.
     bool isTimerThrottled(int timeoutId, ExceptionCode&);
     bool isRequestAnimationFrameThrottled() const;
+    bool areTimersThrottled() const;
 
     // Spatial Navigation testing.
     unsigned lastSpatialNavigationCandidateCount(ExceptionCode&) const;

Modified: trunk/Source/WebCore/testing/Internals.idl (185011 => 185012)


--- trunk/Source/WebCore/testing/Internals.idl	2015-05-29 23:01:37 UTC (rev 185011)
+++ trunk/Source/WebCore/testing/Internals.idl	2015-05-29 23:02:36 UTC (rev 185012)
@@ -285,6 +285,7 @@
     [RaisesException] boolean isTimerThrottled(long timerHandle);
 
     boolean isRequestAnimationFrameThrottled();
+    boolean areTimersThrottled();
 
     [RaisesException] void startTrackingStyleRecalcs();
     [RaisesException] unsigned long styleRecalcCount();
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to