<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[213169] trunk</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/213169">213169</a></dd>
<dt>Author</dt> <dd>cdumez@apple.com</dd>
<dt>Date</dt> <dd>2017-02-28 13:26:27 -0800 (Tue, 28 Feb 2017)</dd>
</dl>
<h3>Log Message</h3>
<pre>[iOS] Throttle requestAnimationFrame to 30fps in low power mode
https://bugs.webkit.org/show_bug.cgi?id=168837
<rdar://problem/30700929>
Reviewed by Simon Fraser.
Source/WebCore:
Throttle requestAnimationFrame to 30fps in low power mode on iOS to save battery.
ScriptedAnimationController now maintains an OptionSet of throttling reasons.
Throttling reasons for now are: OutsideViewport, VisuallyIdle, and LowPowerMode.
The requestAnimationFrame interval is then determined based on those throttling
reasons:
- OutsideViewport or VisuallyIdle: 10 seconds (very aggressive throttling)
- LowPowerMode: 30fps
- No reasons: 60fps
The Page now keeps track of low power mode state using a LowPowerModeNotifier.
Whenever low power mode changes, it updates the throttling reasons in all the
documents' ScriptedAnimationControllers in the frame tree.
Tests: fast/animation/request-animation-frame-throttling-detached-iframe.html
fast/animation/request-animation-frame-throttling-lowPowerMode.html
* dom/Document.cpp:
(WebCore::Document::requestAnimationFrame):
* dom/Document.h:
* dom/ScriptedAnimationController.cpp:
(WebCore::ScriptedAnimationController::ScriptedAnimationController):
(WebCore::throttlingReasonToString):
(WebCore::throttlingReasonsToString):
(WebCore::ScriptedAnimationController::addThrottlingReason):
(WebCore::ScriptedAnimationController::removeThrottlingReason):
(WebCore::ScriptedAnimationController::isThrottled):
(WebCore::ScriptedAnimationController::interval):
(WebCore::ScriptedAnimationController::page):
(WebCore::ScriptedAnimationController::scheduleAnimation):
* dom/ScriptedAnimationController.h:
(WebCore::ScriptedAnimationController::create):
* page/FrameView.cpp:
(WebCore::FrameView::updateScriptedAnimationsAndTimersThrottlingState):
* page/Page.cpp:
(WebCore::Page::Page):
(WebCore::Page::isLowPowerModeEnabled):
(WebCore::Page::setLowPowerModeEnabledOverrideForTesting):
(WebCore::updateScriptedAnimationsThrottlingReason):
(WebCore::Page::setIsVisuallyIdleInternal):
(WebCore::Page::handleLowModePowerChange):
* page/Page.h:
* testing/Internals.cpp:
(WebCore::Internals::resetToConsistentState):
(WebCore::Internals::requestAnimationFrameInterval):
(WebCore::Internals::setLowPowerModeEnabled):
* testing/Internals.h:
* testing/Internals.idl:
Source/WTF:
Add support for operator -= on WTF::OptionSet for convenience:
set -= Enum::A;
looks much better than:
set = set - Enum::A;
* wtf/OptionSet.h:
(WTF::OptionSet::operator-=):
Tools:
Add unit test for -= operator on WTF::OptionSet.
* TestWebKitAPI/Tests/WTF/OptionSet.cpp:
(TestWebKitAPI::TEST):
LayoutTests:
Add layout test coverage.
* fast/animation/request-animation-frame-throttling-detached-iframe-expected.txt: Added.
* fast/animation/request-animation-frame-throttling-detached-iframe.html: Added.
* fast/animation/request-animation-frame-throttling-lowPowerMode-expected.txt: Added.
* fast/animation/request-animation-frame-throttling-lowPowerMode.html: Added.
* fast/animation/resources/frame-with-animation.html: Added.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWTFChangeLog">trunk/Source/WTF/ChangeLog</a></li>
<li><a href="#trunkSourceWTFwtfOptionSeth">trunk/Source/WTF/wtf/OptionSet.h</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoredomDocumentcpp">trunk/Source/WebCore/dom/Document.cpp</a></li>
<li><a href="#trunkSourceWebCoredomDocumenth">trunk/Source/WebCore/dom/Document.h</a></li>
<li><a href="#trunkSourceWebCoredomScriptedAnimationControllercpp">trunk/Source/WebCore/dom/ScriptedAnimationController.cpp</a></li>
<li><a href="#trunkSourceWebCoredomScriptedAnimationControllerh">trunk/Source/WebCore/dom/ScriptedAnimationController.h</a></li>
<li><a href="#trunkSourceWebCorepageFrameViewcpp">trunk/Source/WebCore/page/FrameView.cpp</a></li>
<li><a href="#trunkSourceWebCorepagePagecpp">trunk/Source/WebCore/page/Page.cpp</a></li>
<li><a href="#trunkSourceWebCorepagePageh">trunk/Source/WebCore/page/Page.h</a></li>
<li><a href="#trunkSourceWebCoretestingInternalscpp">trunk/Source/WebCore/testing/Internals.cpp</a></li>
<li><a href="#trunkSourceWebCoretestingInternalsh">trunk/Source/WebCore/testing/Internals.h</a></li>
<li><a href="#trunkSourceWebCoretestingInternalsidl">trunk/Source/WebCore/testing/Internals.idl</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWTFOptionSetcpp">trunk/Tools/TestWebKitAPI/Tests/WTF/OptionSet.cpp</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastanimationrequestanimationframethrottlingdetachediframeexpectedtxt">trunk/LayoutTests/fast/animation/request-animation-frame-throttling-detached-iframe-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastanimationrequestanimationframethrottlingdetachediframehtml">trunk/LayoutTests/fast/animation/request-animation-frame-throttling-detached-iframe.html</a></li>
<li><a href="#trunkLayoutTestsfastanimationrequestanimationframethrottlinglowPowerModeexpectedtxt">trunk/LayoutTests/fast/animation/request-animation-frame-throttling-lowPowerMode-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastanimationrequestanimationframethrottlinglowPowerModehtml">trunk/LayoutTests/fast/animation/request-animation-frame-throttling-lowPowerMode.html</a></li>
<li><a href="#trunkLayoutTestsfastanimationresourcesframewithanimationhtml">trunk/LayoutTests/fast/animation/resources/frame-with-animation.html</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/LayoutTests/ChangeLog        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -1,3 +1,19 @@
</span><ins>+2017-02-28 Chris Dumez <cdumez@apple.com>
+
+ [iOS] Throttle requestAnimationFrame to 30fps in low power mode
+ https://bugs.webkit.org/show_bug.cgi?id=168837
+ <rdar://problem/30700929>
+
+ Reviewed by Simon Fraser.
+
+ Add layout test coverage.
+
+ * fast/animation/request-animation-frame-throttling-detached-iframe-expected.txt: Added.
+ * fast/animation/request-animation-frame-throttling-detached-iframe.html: Added.
+ * fast/animation/request-animation-frame-throttling-lowPowerMode-expected.txt: Added.
+ * fast/animation/request-animation-frame-throttling-lowPowerMode.html: Added.
+ * fast/animation/resources/frame-with-animation.html: Added.
+
</ins><span class="cx"> 2017-02-28 Myles C. Maxfield <mmaxfield@apple.com>
</span><span class="cx">
</span><span class="cx"> [macOS] Migrate off of CTFontCreateForCSS
</span></span></pre></div>
<a id="trunkLayoutTestsfastanimationrequestanimationframethrottlingdetachediframeexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/animation/request-animation-frame-throttling-detached-iframe-expected.txt (0 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/animation/request-animation-frame-throttling-detached-iframe-expected.txt         (rev 0)
+++ trunk/LayoutTests/fast/animation/request-animation-frame-throttling-detached-iframe-expected.txt        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -0,0 +1,31 @@
</span><ins>+Test that requestAnimationFrame gets the right throttling in an iframe when inserted into a document.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS internals.isRequestAnimationFrameThrottled() is false
+PASS internals.requestAnimationFrameInterval is 0.015
+PASS frame.contentWindow.internals.isRequestAnimationFrameThrottled() is false
+PASS frame.contentWindow.internals.requestAnimationFrameInterval is 0.015
+internals.setLowPowerModeEnabled(true);
+PASS internals.isRequestAnimationFrameThrottled() is true
+PASS internals.requestAnimationFrameInterval is 0.030
+PASS frame.contentWindow.internals.isRequestAnimationFrameThrottled() is true
+PASS frame.contentWindow.internals.requestAnimationFrameInterval is 0.030
+frame.remove()
+document.body.appendChild(frame)
+PASS internals.isRequestAnimationFrameThrottled() is true
+PASS internals.requestAnimationFrameInterval is 0.030
+PASS frame.contentWindow.internals.isRequestAnimationFrameThrottled() is true
+PASS frame.contentWindow.internals.requestAnimationFrameInterval is 0.030
+frame.remove()
+internals.setLowPowerModeEnabled(false);
+PASS internals.isRequestAnimationFrameThrottled() is false
+PASS internals.requestAnimationFrameInterval is 0.015
+document.body.appendChild(frame)
+PASS frame.contentWindow.internals.isRequestAnimationFrameThrottled() is false
+PASS frame.contentWindow.internals.requestAnimationFrameInterval is 0.015
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastanimationrequestanimationframethrottlingdetachediframehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/animation/request-animation-frame-throttling-detached-iframe.html (0 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/animation/request-animation-frame-throttling-detached-iframe.html         (rev 0)
+++ trunk/LayoutTests/fast/animation/request-animation-frame-throttling-detached-iframe.html        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -0,0 +1,57 @@
</span><ins>+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/js-test-pre.js"></script>
+<script>
+description("Test that requestAnimationFrame gets the right throttling in an iframe when inserted into a document.");
+jsTestIsAsync = true;
+
+let i = 0;
+function doWork()
+{
+ i++;
+ requestAnimationFrame(doWork);
+}
+
+requestAnimationFrame(doWork);
+
+const frame = document.createElement("iframe");
+frame.src = "resources/frame-with-animation.html";
+frame.onload = function() {
+ shouldBeFalse("internals.isRequestAnimationFrameThrottled()");
+ shouldBe("internals.requestAnimationFrameInterval", "0.015");
+ shouldBeFalse("frame.contentWindow.internals.isRequestAnimationFrameThrottled()");
+ shouldBe("frame.contentWindow.internals.requestAnimationFrameInterval", "0.015");
+
+ evalAndLog("internals.setLowPowerModeEnabled(true);");
+ shouldBeTrue("internals.isRequestAnimationFrameThrottled()");
+ shouldBe("internals.requestAnimationFrameInterval", "0.030");
+ shouldBeTrue("frame.contentWindow.internals.isRequestAnimationFrameThrottled()");
+ shouldBe("frame.contentWindow.internals.requestAnimationFrameInterval", "0.030");
+ evalAndLog("frame.remove()");
+
+ evalAndLog("document.body.appendChild(frame)");
+ frame.onload = function() {
+ shouldBeTrue("internals.isRequestAnimationFrameThrottled()");
+ shouldBe("internals.requestAnimationFrameInterval", "0.030");
+ shouldBeTrue("frame.contentWindow.internals.isRequestAnimationFrameThrottled()");
+ shouldBe("frame.contentWindow.internals.requestAnimationFrameInterval", "0.030");
+
+ evalAndLog("frame.remove()");
+ evalAndLog("internals.setLowPowerModeEnabled(false);");
+ shouldBeFalse("internals.isRequestAnimationFrameThrottled()");
+ shouldBe("internals.requestAnimationFrameInterval", "0.015");
+
+ evalAndLog("document.body.appendChild(frame)");
+ frame.onload = function() {
+ shouldBeFalse("frame.contentWindow.internals.isRequestAnimationFrameThrottled()");
+ shouldBe("frame.contentWindow.internals.requestAnimationFrameInterval", "0.015");
+ finishJSTest();
+ }
+ }
+};
+document.body.appendChild(frame);
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestsfastanimationrequestanimationframethrottlinglowPowerModeexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/animation/request-animation-frame-throttling-lowPowerMode-expected.txt (0 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/animation/request-animation-frame-throttling-lowPowerMode-expected.txt         (rev 0)
+++ trunk/LayoutTests/fast/animation/request-animation-frame-throttling-lowPowerMode-expected.txt        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -0,0 +1,26 @@
</span><ins>+Test that requestAnimationFrame gets throttled in low power mode.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS internals.isRequestAnimationFrameThrottled() is false
+PASS internals.requestAnimationFrameInterval is Infinity
+rAFHandle = requestAnimationFrame(doWork);
+PASS internals.isRequestAnimationFrameThrottled() is false
+PASS internals.requestAnimationFrameInterval is 0.015
+internals.setLowPowerModeEnabled(true);
+PASS internals.isRequestAnimationFrameThrottled() is true
+PASS internals.requestAnimationFrameInterval is 0.030
+cancelAnimationFrame(rAFHandle);
+PASS internals.isRequestAnimationFrameThrottled() is true
+PASS internals.requestAnimationFrameInterval is 0.030
+rAFHandle = requestAnimationFrame(doWork);
+PASS internals.isRequestAnimationFrameThrottled() is true
+PASS internals.requestAnimationFrameInterval is 0.030
+internals.setLowPowerModeEnabled(false);
+PASS internals.isRequestAnimationFrameThrottled() is false
+PASS internals.requestAnimationFrameInterval is 0.015
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastanimationrequestanimationframethrottlinglowPowerModehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/animation/request-animation-frame-throttling-lowPowerMode.html (0 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/animation/request-animation-frame-throttling-lowPowerMode.html         (rev 0)
+++ trunk/LayoutTests/fast/animation/request-animation-frame-throttling-lowPowerMode.html        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -0,0 +1,36 @@
</span><ins>+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/js-test-pre.js"></script>
+<script>
+description("Test that requestAnimationFrame gets throttled in low power mode.");
+
+let rAFHandle;
+let i = 0;
+function doWork()
+{
+ i++;
+ rAFHandle = requestAnimationFrame(doWork);
+}
+
+shouldBeFalse("internals.isRequestAnimationFrameThrottled()");
+shouldBe("internals.requestAnimationFrameInterval", "Infinity");
+evalAndLog("rAFHandle = requestAnimationFrame(doWork);");
+shouldBeFalse("internals.isRequestAnimationFrameThrottled()");
+shouldBe("internals.requestAnimationFrameInterval", "0.015");
+evalAndLog("internals.setLowPowerModeEnabled(true);");
+shouldBeTrue("internals.isRequestAnimationFrameThrottled()");
+shouldBe("internals.requestAnimationFrameInterval", "0.030");
+evalAndLog("cancelAnimationFrame(rAFHandle);");
+shouldBeTrue("internals.isRequestAnimationFrameThrottled()");
+shouldBe("internals.requestAnimationFrameInterval", "0.030");
+evalAndLog("rAFHandle = requestAnimationFrame(doWork);");
+shouldBeTrue("internals.isRequestAnimationFrameThrottled()");
+shouldBe("internals.requestAnimationFrameInterval", "0.030");
+evalAndLog("internals.setLowPowerModeEnabled(false);");
+shouldBeFalse("internals.isRequestAnimationFrameThrottled()");
+shouldBe("internals.requestAnimationFrameInterval", "0.015");
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkLayoutTestsfastanimationresourcesframewithanimationhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/animation/resources/frame-with-animation.html (0 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/animation/resources/frame-with-animation.html         (rev 0)
+++ trunk/LayoutTests/fast/animation/resources/frame-with-animation.html        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -0,0 +1,10 @@
</span><ins>+<script>
+let i = 0;
+function doWork()
+{
+ i++;
+ requestAnimationFrame(doWork);
+}
+
+requestAnimationFrame(doWork);
+</script>
</ins></span></pre></div>
<a id="trunkSourceWTFChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/ChangeLog (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/ChangeLog        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Source/WTF/ChangeLog        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -1,3 +1,19 @@
</span><ins>+2017-02-28 Chris Dumez <cdumez@apple.com>
+
+ [iOS] Throttle requestAnimationFrame to 30fps in low power mode
+ https://bugs.webkit.org/show_bug.cgi?id=168837
+ <rdar://problem/30700929>
+
+ Reviewed by Simon Fraser.
+
+ Add support for operator -= on WTF::OptionSet for convenience:
+ set -= Enum::A;
+ looks much better than:
+ set = set - Enum::A;
+
+ * wtf/OptionSet.h:
+ (WTF::OptionSet::operator-=):
+
</ins><span class="cx"> 2017-02-28 Michael Saboff <msaboff@apple.com>
</span><span class="cx">
</span><span class="cx"> Add ability to configure JSC options from a file
</span></span></pre></div>
<a id="trunkSourceWTFwtfOptionSeth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/OptionSet.h (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/OptionSet.h        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Source/WTF/wtf/OptionSet.h        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -125,6 +125,13 @@
</span><span class="cx"> return lhs;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ friend OptionSet& operator-=(OptionSet& lhs, OptionSet rhs)
+ {
+ lhs.m_storage &= ~rhs.m_storage;
+
+ return lhs;
+ }
+
</ins><span class="cx"> constexpr friend OptionSet operator-(OptionSet lhs, OptionSet rhs)
</span><span class="cx"> {
</span><span class="cx"> return OptionSet::fromRaw(lhs.m_storage & ~rhs.m_storage);
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Source/WebCore/ChangeLog        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -1,3 +1,60 @@
</span><ins>+2017-02-28 Chris Dumez <cdumez@apple.com>
+
+ [iOS] Throttle requestAnimationFrame to 30fps in low power mode
+ https://bugs.webkit.org/show_bug.cgi?id=168837
+ <rdar://problem/30700929>
+
+ Reviewed by Simon Fraser.
+
+ Throttle requestAnimationFrame to 30fps in low power mode on iOS to save battery.
+
+ ScriptedAnimationController now maintains an OptionSet of throttling reasons.
+ Throttling reasons for now are: OutsideViewport, VisuallyIdle, and LowPowerMode.
+ The requestAnimationFrame interval is then determined based on those throttling
+ reasons:
+ - OutsideViewport or VisuallyIdle: 10 seconds (very aggressive throttling)
+ - LowPowerMode: 30fps
+ - No reasons: 60fps
+
+ The Page now keeps track of low power mode state using a LowPowerModeNotifier.
+ Whenever low power mode changes, it updates the throttling reasons in all the
+ documents' ScriptedAnimationControllers in the frame tree.
+
+ Tests: fast/animation/request-animation-frame-throttling-detached-iframe.html
+ fast/animation/request-animation-frame-throttling-lowPowerMode.html
+
+ * dom/Document.cpp:
+ (WebCore::Document::requestAnimationFrame):
+ * dom/Document.h:
+ * dom/ScriptedAnimationController.cpp:
+ (WebCore::ScriptedAnimationController::ScriptedAnimationController):
+ (WebCore::throttlingReasonToString):
+ (WebCore::throttlingReasonsToString):
+ (WebCore::ScriptedAnimationController::addThrottlingReason):
+ (WebCore::ScriptedAnimationController::removeThrottlingReason):
+ (WebCore::ScriptedAnimationController::isThrottled):
+ (WebCore::ScriptedAnimationController::interval):
+ (WebCore::ScriptedAnimationController::page):
+ (WebCore::ScriptedAnimationController::scheduleAnimation):
+ * dom/ScriptedAnimationController.h:
+ (WebCore::ScriptedAnimationController::create):
+ * page/FrameView.cpp:
+ (WebCore::FrameView::updateScriptedAnimationsAndTimersThrottlingState):
+ * page/Page.cpp:
+ (WebCore::Page::Page):
+ (WebCore::Page::isLowPowerModeEnabled):
+ (WebCore::Page::setLowPowerModeEnabledOverrideForTesting):
+ (WebCore::updateScriptedAnimationsThrottlingReason):
+ (WebCore::Page::setIsVisuallyIdleInternal):
+ (WebCore::Page::handleLowModePowerChange):
+ * page/Page.h:
+ * testing/Internals.cpp:
+ (WebCore::Internals::resetToConsistentState):
+ (WebCore::Internals::requestAnimationFrameInterval):
+ (WebCore::Internals::setLowPowerModeEnabled):
+ * testing/Internals.h:
+ * testing/Internals.idl:
+
</ins><span class="cx"> 2017-02-28 Youenn Fablet <youenn@apple.com>
</span><span class="cx">
</span><span class="cx"> [WebRTC] Limit libwebrtc logging in Debug build
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumentcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.cpp (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.cpp        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Source/WebCore/dom/Document.cpp        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -5481,12 +5481,6 @@
</span><span class="cx"> m_scriptedAnimationController->resume();
</span><span class="cx"> }
</span><span class="cx">
</span><del>-void Document::scriptedAnimationControllerSetThrottled(bool isThrottled)
-{
- if (m_scriptedAnimationController)
- m_scriptedAnimationController->setThrottled(isThrottled);
-}
-
</del><span class="cx"> void Document::windowScreenDidChange(PlatformDisplayID displayID)
</span><span class="cx"> {
</span><span class="cx"> if (m_scriptedAnimationController)
</span><span class="lines">@@ -6058,9 +6052,9 @@
</span><span class="cx"> {
</span><span class="cx"> if (!m_scriptedAnimationController) {
</span><span class="cx"> #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
</span><del>- m_scriptedAnimationController = ScriptedAnimationController::create(this, page() ? page()->chrome().displayID() : 0);
</del><ins>+ m_scriptedAnimationController = ScriptedAnimationController::create(*this, page() ? page()->chrome().displayID() : 0);
</ins><span class="cx"> #else
</span><del>- m_scriptedAnimationController = ScriptedAnimationController::create(this, 0);
</del><ins>+ m_scriptedAnimationController = ScriptedAnimationController::create(*this, 0);
</ins><span class="cx"> #endif
</span><span class="cx"> // It's possible that the Page may have suspended scripted animations before
</span><span class="cx"> // we were created. We need to make sure that we don't start up the animation
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumenth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.h (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.h        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Source/WebCore/dom/Document.h        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -958,7 +958,6 @@
</span><span class="cx"> ScriptedAnimationController* scriptedAnimationController() { return m_scriptedAnimationController.get(); }
</span><span class="cx"> void suspendScriptedAnimationControllerCallbacks();
</span><span class="cx"> void resumeScriptedAnimationControllerCallbacks();
</span><del>- void scriptedAnimationControllerSetThrottled(bool);
</del><span class="cx">
</span><span class="cx"> void windowScreenDidChange(PlatformDisplayID);
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCoredomScriptedAnimationControllercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/ScriptedAnimationController.cpp (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/ScriptedAnimationController.cpp        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Source/WebCore/dom/ScriptedAnimationController.cpp        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -40,6 +40,7 @@
</span><span class="cx"> #include "Settings.h"
</span><span class="cx"> #include <wtf/Ref.h>
</span><span class="cx"> #include <wtf/SystemTracing.h>
</span><ins>+#include <wtf/text/StringBuilder.h>
</ins><span class="cx">
</span><span class="cx"> #if USE(REQUEST_ANIMATION_FRAME_TIMER)
</span><span class="cx"> #include <algorithm>
</span><span class="lines">@@ -46,19 +47,27 @@
</span><span class="cx"> #include <wtf/CurrentTime.h>
</span><span class="cx">
</span><span class="cx"> // Allow a little more than 60fps to make sure we can at least hit that frame rate.
</span><del>-#define MinimumAnimationInterval 0.015
-#define MinimumThrottledAnimationInterval 10
</del><ins>+static const Seconds fullSpeedAnimationInterval { 0.015 };
+// Allow a little more than 30fps to make sure we can at least hit that frame rate.
+static const Seconds halfSpeedThrottlingAnimationInterval { 0.030 };
+static const Seconds aggressiveThrottlingAnimationInterval { 10 };
</ins><span class="cx"> #endif
</span><span class="cx">
</span><ins>+#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(page() && page()->isAlwaysOnLoggingAllowed(), PerformanceLogging, "%p - ScriptedAnimationController::" fmt, this, ##__VA_ARGS__)
+
</ins><span class="cx"> namespace WebCore {
</span><span class="cx">
</span><del>-ScriptedAnimationController::ScriptedAnimationController(Document* document, PlatformDisplayID displayID)
- : m_document(document)
</del><ins>+ScriptedAnimationController::ScriptedAnimationController(Document& document, PlatformDisplayID displayID)
+ : m_document(&document)
</ins><span class="cx"> #if USE(REQUEST_ANIMATION_FRAME_TIMER)
</span><span class="cx"> , m_animationTimer(*this, &ScriptedAnimationController::animationTimerFired)
</span><span class="cx"> #endif
</span><span class="cx"> {
</span><span class="cx"> windowScreenDidChange(displayID);
</span><ins>+
+ auto* page = document.page();
+ if (page && page->isLowPowerModeEnabled())
+ addThrottlingReason(ThrottlingReason::LowPowerMode);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> ScriptedAnimationController::~ScriptedAnimationController()
</span><span class="lines">@@ -86,28 +95,78 @@
</span><span class="cx"> scheduleAnimation();
</span><span class="cx"> }
</span><span class="cx">
</span><del>-void ScriptedAnimationController::setThrottled(bool isThrottled)
</del><ins>+#if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) && !RELEASE_LOG_DISABLED
+
+static const char* throttlingReasonToString(ScriptedAnimationController::ThrottlingReason reason)
</ins><span class="cx"> {
</span><ins>+ switch (reason) {
+ case ScriptedAnimationController::ThrottlingReason::VisuallyIdle:
+ return "VisuallyIdle";
+ case ScriptedAnimationController::ThrottlingReason::OutsideViewport:
+ return "OutsideViewport";
+ case ScriptedAnimationController::ThrottlingReason::LowPowerMode:
+ return "LowPowerMode";
+ }
+}
+
+static String throttlingReasonsToString(OptionSet<ScriptedAnimationController::ThrottlingReason> reasons)
+{
+ if (reasons.isEmpty())
+ return ASCIILiteral("[Unthrottled]");
+
+ StringBuilder builder;
+ for (auto reason : reasons) {
+ if (!builder.isEmpty())
+ builder.append('|');
+ builder.append(throttlingReasonToString(reason));
+ }
+ return builder.toString();
+}
+
+#endif
+
+void ScriptedAnimationController::addThrottlingReason(ThrottlingReason reason)
+{
</ins><span class="cx"> #if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
</span><del>- if (m_isThrottled == isThrottled)
</del><ins>+ if (m_throttlingReasons.contains(reason))
</ins><span class="cx"> return;
</span><span class="cx">
</span><del>- LOG(Animations, "%p - Setting RequestAnimationFrame throttling state to %d in frame %p (isMainFrame: %d)", this, isThrottled, m_document->frame(), m_document->frame() ? m_document->frame()->isMainFrame() : 0);
</del><ins>+ m_throttlingReasons |= reason;
</ins><span class="cx">
</span><del>- m_isThrottled = isThrottled;
</del><ins>+ RELEASE_LOG_IF_ALLOWED("addThrottlingReason(%s) -> %s", throttlingReasonToString(reason), throttlingReasonsToString(m_throttlingReasons).utf8().data());
+
</ins><span class="cx"> if (m_animationTimer.isActive()) {
</span><span class="cx"> m_animationTimer.stop();
</span><span class="cx"> scheduleAnimation();
</span><span class="cx"> }
</span><span class="cx"> #else
</span><del>- UNUSED_PARAM(isThrottled);
</del><ins>+ UNUSED_PARAM(reason);
</ins><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+void ScriptedAnimationController::removeThrottlingReason(ThrottlingReason reason)
+{
+#if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+ if (!m_throttlingReasons.contains(reason))
+ return;
+
+ m_throttlingReasons -= reason;
+
+ RELEASE_LOG_IF_ALLOWED("removeThrottlingReason(%s) -> %s", throttlingReasonToString(reason), throttlingReasonsToString(m_throttlingReasons).utf8().data());
+
+ if (m_animationTimer.isActive()) {
+ m_animationTimer.stop();
+ scheduleAnimation();
+ }
+#else
+ UNUSED_PARAM(reason);
+#endif
+}
+
</ins><span class="cx"> bool ScriptedAnimationController::isThrottled() const
</span><span class="cx"> {
</span><span class="cx"> #if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
</span><del>- return m_isThrottled;
</del><ins>+ return !m_throttlingReasons.isEmpty();
</ins><span class="cx"> #else
</span><span class="cx"> return false;
</span><span class="cx"> #endif
</span><span class="lines">@@ -192,6 +251,23 @@
</span><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+Seconds ScriptedAnimationController::interval() const
+{
+#if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+ if (m_throttlingReasons.contains(ThrottlingReason::VisuallyIdle) || m_throttlingReasons.contains(ThrottlingReason::OutsideViewport))
+ return aggressiveThrottlingAnimationInterval;
+ if (m_throttlingReasons.contains(ThrottlingReason::LowPowerMode))
+ return halfSpeedThrottlingAnimationInterval;
+ ASSERT(m_throttlingReasons.isEmpty());
+#endif
+ return fullSpeedAnimationInterval;
+}
+
+Page* ScriptedAnimationController::page() const
+{
+ return m_document ? m_document->page() : nullptr;
+}
+
</ins><span class="cx"> void ScriptedAnimationController::scheduleAnimation()
</span><span class="cx"> {
</span><span class="cx"> if (!requestAnimationFrameEnabled())
</span><span class="lines">@@ -199,7 +275,7 @@
</span><span class="cx">
</span><span class="cx"> #if USE(REQUEST_ANIMATION_FRAME_TIMER)
</span><span class="cx"> #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
</span><del>- if (!m_isUsingTimer && !m_isThrottled) {
</del><ins>+ if (!m_isUsingTimer && !isThrottled()) {
</ins><span class="cx"> if (DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this))
</span><span class="cx"> return;
</span><span class="cx">
</span><span class="lines">@@ -209,13 +285,8 @@
</span><span class="cx"> if (m_animationTimer.isActive())
</span><span class="cx"> return;
</span><span class="cx">
</span><del>- double animationInterval = MinimumAnimationInterval;
-#if USE(REQUEST_ANIMATION_FRAME_TIMER) && USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
- if (m_isThrottled)
- animationInterval = MinimumThrottledAnimationInterval;
-#endif
-
- double scheduleDelay = std::max<double>(animationInterval - (m_document->domWindow()->nowTimestamp() - m_lastAnimationFrameTimestamp), 0);
</del><ins>+ Seconds animationInterval = interval();
+ double scheduleDelay = std::max<double>(animationInterval.value() - (m_document->domWindow()->nowTimestamp() - m_lastAnimationFrameTimestamp), 0);
</ins><span class="cx"> m_animationTimer.startOneShot(scheduleDelay);
</span><span class="cx"> #else
</span><span class="cx"> if (FrameView* frameView = m_document->view())
</span></span></pre></div>
<a id="trunkSourceWebCoredomScriptedAnimationControllerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/ScriptedAnimationController.h (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/ScriptedAnimationController.h        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Source/WebCore/dom/ScriptedAnimationController.h        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx">
</span><span class="cx"> #include "DOMTimeStamp.h"
</span><span class="cx"> #include "PlatformScreen.h"
</span><ins>+#include <wtf/OptionSet.h>
</ins><span class="cx"> #include <wtf/RefCounted.h>
</span><span class="cx"> #include <wtf/RefPtr.h>
</span><span class="cx"> #include <wtf/Vector.h>
</span><span class="lines">@@ -44,6 +45,7 @@
</span><span class="cx"> namespace WebCore {
</span><span class="cx">
</span><span class="cx"> class Document;
</span><ins>+class Page;
</ins><span class="cx"> class RequestAnimationFrameCallback;
</span><span class="cx">
</span><span class="cx"> class ScriptedAnimationController : public RefCounted<ScriptedAnimationController>
</span><span class="lines">@@ -52,7 +54,7 @@
</span><span class="cx"> #endif
</span><span class="cx"> {
</span><span class="cx"> public:
</span><del>- static Ref<ScriptedAnimationController> create(Document* document, PlatformDisplayID displayID)
</del><ins>+ static Ref<ScriptedAnimationController> create(Document& document, PlatformDisplayID displayID)
</ins><span class="cx"> {
</span><span class="cx"> return adoptRef(*new ScriptedAnimationController(document, displayID));
</span><span class="cx"> }
</span><span class="lines">@@ -68,14 +70,24 @@
</span><span class="cx">
</span><span class="cx"> void suspend();
</span><span class="cx"> void resume();
</span><del>- void setThrottled(bool);
</del><ins>+
+ enum class ThrottlingReason {
+ VisuallyIdle = 1 << 0,
+ OutsideViewport = 1 << 1,
+ LowPowerMode = 1 << 2,
+ };
+ void addThrottlingReason(ThrottlingReason);
+ void removeThrottlingReason(ThrottlingReason);
</ins><span class="cx"> WEBCORE_EXPORT bool isThrottled() const;
</span><ins>+ WEBCORE_EXPORT Seconds interval() const;
</ins><span class="cx">
</span><span class="cx"> void windowScreenDidChange(PlatformDisplayID);
</span><span class="cx">
</span><span class="cx"> private:
</span><del>- ScriptedAnimationController(Document*, PlatformDisplayID);
</del><ins>+ ScriptedAnimationController(Document&, PlatformDisplayID);
</ins><span class="cx">
</span><ins>+ Page* page() const;
+
</ins><span class="cx"> typedef Vector<RefPtr<RequestAnimationFrameCallback>> CallbackList;
</span><span class="cx"> CallbackList m_callbacks;
</span><span class="cx">
</span><span class="lines">@@ -97,7 +109,8 @@
</span><span class="cx"> RefPtr<DisplayRefreshMonitor> createDisplayRefreshMonitor(PlatformDisplayID) const override;
</span><span class="cx">
</span><span class="cx"> bool m_isUsingTimer { false };
</span><del>- bool m_isThrottled { false };
</del><ins>+
+ OptionSet<ThrottlingReason> m_throttlingReasons;
</ins><span class="cx"> #endif
</span><span class="cx"> };
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCorepageFrameViewcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/FrameView.cpp (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/FrameView.cpp        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Source/WebCore/page/FrameView.cpp        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -2563,8 +2563,12 @@
</span><span class="cx"> // We don't throttle zero-size or display:none frames because those are usually utility frames.
</span><span class="cx"> bool shouldThrottle = visibleRect.isEmpty() && !m_size.isEmpty() && frame().ownerRenderer();
</span><span class="cx">
</span><del>- if (auto* scriptedAnimationController = document->scriptedAnimationController())
- scriptedAnimationController->setThrottled(shouldThrottle);
</del><ins>+ if (auto* scriptedAnimationController = document->scriptedAnimationController()) {
+ if (shouldThrottle)
+ scriptedAnimationController->addThrottlingReason(ScriptedAnimationController::ThrottlingReason::OutsideViewport);
+ else
+ scriptedAnimationController->removeThrottlingReason(ScriptedAnimationController::ThrottlingReason::OutsideViewport);
+ }
</ins><span class="cx">
</span><span class="cx"> document->setTimerThrottlingEnabled(shouldThrottle);
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCorepagePagecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/Page.cpp (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/Page.cpp        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Source/WebCore/page/Page.cpp        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -57,6 +57,7 @@
</span><span class="cx"> #include "InspectorInstrumentation.h"
</span><span class="cx"> #include "LibWebRTCProvider.h"
</span><span class="cx"> #include "Logging.h"
</span><ins>+#include "LowPowerModeNotifier.h"
</ins><span class="cx"> #include "MainFrame.h"
</span><span class="cx"> #include "MediaCanStartListener.h"
</span><span class="cx"> #include "Navigator.h"
</span><span class="lines">@@ -84,6 +85,7 @@
</span><span class="cx"> #include "SVGDocumentExtensions.h"
</span><span class="cx"> #include "SchemeRegistry.h"
</span><span class="cx"> #include "ScriptController.h"
</span><ins>+#include "ScriptedAnimationController.h"
</ins><span class="cx"> #include "ScrollingCoordinator.h"
</span><span class="cx"> #include "Settings.h"
</span><span class="cx"> #include "SharedBuffer.h"
</span><span class="lines">@@ -263,6 +265,7 @@
</span><span class="cx"> , m_isClosing(false)
</span><span class="cx"> , m_isUtilityPage(isUtilityPageChromeClient(chrome().client()))
</span><span class="cx"> , m_performanceMonitor(isUtilityPage() ? nullptr : std::make_unique<PerformanceMonitor>(*this))
</span><ins>+ , m_lowPowerModeNotifier(std::make_unique<LowPowerModeNotifier>([this](bool isLowPowerModeEnabled) { handleLowModePowerChange(isLowPowerModeEnabled); }))
</ins><span class="cx"> {
</span><span class="cx"> updateTimerThrottlingState();
</span><span class="cx">
</span><span class="lines">@@ -954,6 +957,20 @@
</span><span class="cx"> return !isUtilityPage() && nonUtilityPageCount == 1;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+bool Page::isLowPowerModeEnabled() const
+{
+ if (m_lowPowerModeEnabledOverrideForTesting)
+ return m_lowPowerModeEnabledOverrideForTesting.value();
+
+ return m_lowPowerModeNotifier->isLowPowerModeEnabled();
+}
+
+void Page::setLowPowerModeEnabledOverrideForTesting(std::optional<bool> isEnabled)
+{
+ m_lowPowerModeEnabledOverrideForTesting = isEnabled;
+ handleLowModePowerChange(m_lowPowerModeEnabledOverrideForTesting.value_or(false));
+}
+
</ins><span class="cx"> void Page::setTopContentInset(float contentInset)
</span><span class="cx"> {
</span><span class="cx"> if (m_topContentInset == contentInset)
</span><span class="lines">@@ -1094,14 +1111,34 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><del>-void Page::setIsVisuallyIdleInternal(bool isVisuallyIdle)
</del><ins>+enum class ThrottlingReasonOperation { Add, Remove };
+static void updateScriptedAnimationsThrottlingReason(Page& page, ThrottlingReasonOperation operation, ScriptedAnimationController::ThrottlingReason reason)
</ins><span class="cx"> {
</span><del>- for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) {
- if (frame->document())
- frame->document()->scriptedAnimationControllerSetThrottled(isVisuallyIdle);
</del><ins>+ for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
+ auto* document = frame->document();
+ if (!document)
+ continue;
+ auto* scriptedAnimationController = document->scriptedAnimationController();
+ if (!scriptedAnimationController)
+ continue;
+
+ if (operation == ThrottlingReasonOperation::Add)
+ scriptedAnimationController->addThrottlingReason(reason);
+ else
+ scriptedAnimationController->removeThrottlingReason(reason);
</ins><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+void Page::setIsVisuallyIdleInternal(bool isVisuallyIdle)
+{
+ updateScriptedAnimationsThrottlingReason(*this, isVisuallyIdle ? ThrottlingReasonOperation::Add : ThrottlingReasonOperation::Remove, ScriptedAnimationController::ThrottlingReason::VisuallyIdle);
+}
+
+void Page::handleLowModePowerChange(bool isLowPowerModeEnabled)
+{
+ updateScriptedAnimationsThrottlingReason(*this, isLowPowerModeEnabled ? ThrottlingReasonOperation::Add : ThrottlingReasonOperation::Remove, ScriptedAnimationController::ThrottlingReason::LowPowerMode);
+}
+
</ins><span class="cx"> void Page::userStyleSheetLocationChanged()
</span><span class="cx"> {
</span><span class="cx"> // FIXME: Eventually we will move to a model of just being handed the sheet
</span></span></pre></div>
<a id="trunkSourceWebCorepagePageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/Page.h (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/Page.h        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Source/WebCore/page/Page.h        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -99,6 +99,7 @@
</span><span class="cx"> class InspectorClient;
</span><span class="cx"> class InspectorController;
</span><span class="cx"> class LibWebRTCProvider;
</span><ins>+class LowPowerModeNotifier;
</ins><span class="cx"> class MainFrame;
</span><span class="cx"> class MediaCanStartListener;
</span><span class="cx"> class MediaPlaybackTarget;
</span><span class="lines">@@ -576,6 +577,9 @@
</span><span class="cx"> WEBCORE_EXPORT bool hasSelectionAtPosition(const FloatPoint&) const;
</span><span class="cx"> #endif
</span><span class="cx">
</span><ins>+ bool isLowPowerModeEnabled() const;
+ WEBCORE_EXPORT void setLowPowerModeEnabledOverrideForTesting(std::optional<bool>);
+
</ins><span class="cx"> private:
</span><span class="cx"> WEBCORE_EXPORT void initGroup();
</span><span class="cx">
</span><span class="lines">@@ -598,6 +602,8 @@
</span><span class="cx">
</span><span class="cx"> Vector<Ref<PluginViewBase>> pluginViews();
</span><span class="cx">
</span><ins>+ void handleLowModePowerChange(bool);
+
</ins><span class="cx"> enum class TimerThrottlingState { Disabled, Enabled, EnabledIncreasing };
</span><span class="cx"> void hiddenPageDOMTimerThrottlingStateChanged();
</span><span class="cx"> void setTimerThrottlingState(TimerThrottlingState);
</span><span class="lines">@@ -780,6 +786,8 @@
</span><span class="cx"> std::optional<EventThrottlingBehavior> m_eventThrottlingBehaviorOverride;
</span><span class="cx">
</span><span class="cx"> std::unique_ptr<PerformanceMonitor> m_performanceMonitor;
</span><ins>+ std::unique_ptr<LowPowerModeNotifier> m_lowPowerModeNotifier;
+ std::optional<bool> m_lowPowerModeEnabledOverrideForTesting;
</ins><span class="cx">
</span><span class="cx"> bool m_isRunningUserScripts { false };
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.cpp (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.cpp        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Source/WebCore/testing/Internals.cpp        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -436,6 +436,7 @@
</span><span class="cx"> #endif
</span><span class="cx">
</span><span class="cx"> page.setShowAllPlugins(false);
</span><ins>+ page.setLowPowerModeEnabledOverrideForTesting(std::nullopt);
</ins><span class="cx">
</span><span class="cx"> #if USE(QUICK_LOOK)
</span><span class="cx"> MockQuickLookHandleClient::singleton().setPassword("");
</span><span class="lines">@@ -1019,6 +1020,14 @@
</span><span class="cx"> return scriptedAnimationController->isThrottled();
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+double Internals::requestAnimationFrameInterval() const
+{
+ auto* scriptedAnimationController = contextDocument()->scriptedAnimationController();
+ if (!scriptedAnimationController)
+ return INFINITY;
+ return scriptedAnimationController->interval().value();
+}
+
</ins><span class="cx"> bool Internals::areTimersThrottled() const
</span><span class="cx"> {
</span><span class="cx"> return contextDocument()->isTimerThrottlingEnabled();
</span><span class="lines">@@ -1290,6 +1299,19 @@
</span><span class="cx"> FontCache::singleton().invalidate();
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ExceptionOr<void> Internals::setLowPowerModeEnabled(bool isEnabled)
+{
+ auto* document = contextDocument();
+ if (!document)
+ return Exception { INVALID_ACCESS_ERR };
+ auto* page = document->page();
+ if (!page)
+ return Exception { INVALID_ACCESS_ERR };
+
+ page->setLowPowerModeEnabledOverrideForTesting(isEnabled);
+ return { };
+}
+
</ins><span class="cx"> ExceptionOr<void> Internals::setScrollViewPosition(int x, int y)
</span><span class="cx"> {
</span><span class="cx"> Document* document = contextDocument();
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.h (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.h        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Source/WebCore/testing/Internals.h        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -130,6 +130,7 @@
</span><span class="cx"> // DOMTimers throttling testing.
</span><span class="cx"> ExceptionOr<bool> isTimerThrottled(int timeoutId);
</span><span class="cx"> bool isRequestAnimationFrameThrottled() const;
</span><ins>+ double requestAnimationFrameInterval() const;
</ins><span class="cx"> bool areTimersThrottled() const;
</span><span class="cx">
</span><span class="cx"> enum EventThrottlingBehavior { Responsive, Unresponsive };
</span><span class="lines">@@ -174,6 +175,7 @@
</span><span class="cx"> ExceptionOr<void> setMarkedTextMatchesAreHighlighted(bool);
</span><span class="cx">
</span><span class="cx"> void invalidateFontCache();
</span><ins>+ ExceptionOr<void> setLowPowerModeEnabled(bool);
</ins><span class="cx">
</span><span class="cx"> ExceptionOr<void> setScrollViewPosition(int x, int y);
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalsidl"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.idl (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.idl        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Source/WebCore/testing/Internals.idl        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -350,6 +350,9 @@
</span><span class="cx"> boolean isRequestAnimationFrameThrottled();
</span><span class="cx"> boolean areTimersThrottled();
</span><span class="cx">
</span><ins>+ [MayThrowException] void setLowPowerModeEnabled(boolean enabled);
+ readonly attribute double requestAnimationFrameInterval;
+
</ins><span class="cx"> // Override the behavior of WebPage::eventThrottlingDelay(), which only affects iOS.
</span><span class="cx"> attribute EventThrottlingBehavior? eventThrottlingBehaviorOverride;
</span><span class="cx">
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Tools/ChangeLog        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2017-02-28 Chris Dumez <cdumez@apple.com>
+
+ [iOS] Throttle requestAnimationFrame to 30fps in low power mode
+ https://bugs.webkit.org/show_bug.cgi?id=168837
+ <rdar://problem/30700929>
+
+ Reviewed by Simon Fraser.
+
+ Add unit test for -= operator on WTF::OptionSet.
+
+ * TestWebKitAPI/Tests/WTF/OptionSet.cpp:
+ (TestWebKitAPI::TEST):
+
</ins><span class="cx"> 2017-02-28 Jonathan Bedard <jbedard@apple.com>
</span><span class="cx">
</span><span class="cx"> webkitpy: Regular expression for parsing simctl device information is wrong for iPad Pro
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWTFOptionSetcpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WTF/OptionSet.cpp (213168 => 213169)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WTF/OptionSet.cpp        2017-02-28 21:24:50 UTC (rev 213168)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/OptionSet.cpp        2017-02-28 21:26:27 UTC (rev 213169)
</span><span class="lines">@@ -86,6 +86,18 @@
</span><span class="cx"> EXPECT_TRUE((set - set).isEmpty());
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+TEST(WTF_OptionSet, MinusEqual)
+{
+ OptionSet<ExampleFlags> set { ExampleFlags::A, ExampleFlags::B, ExampleFlags::C };
+
+ EXPECT_TRUE(((set -= ExampleFlags::A) == OptionSet<ExampleFlags> { ExampleFlags::B, ExampleFlags::C }));
+ EXPECT_TRUE((set == OptionSet<ExampleFlags> { ExampleFlags::B, ExampleFlags::C }));
+ EXPECT_TRUE(((set -= ExampleFlags::D) == OptionSet<ExampleFlags> { ExampleFlags::B, ExampleFlags::C }));
+ EXPECT_TRUE((set == OptionSet<ExampleFlags> { ExampleFlags::B, ExampleFlags::C }));
+ EXPECT_TRUE((set -= set).isEmpty());
+ EXPECT_TRUE(set.isEmpty());
+}
+
</ins><span class="cx"> TEST(WTF_OptionSet, ContainsTwoFlags)
</span><span class="cx"> {
</span><span class="cx"> OptionSet<ExampleFlags> set { ExampleFlags::A, ExampleFlags::B };
</span></span></pre>
</div>
</div>
</body>
</html>