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();