<!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
&lt;rdar://problem/30700929&gt;

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  &lt;cdumez@apple.com&gt;
+
+        [iOS] Throttle requestAnimationFrame to 30fps in low power mode
+        https://bugs.webkit.org/show_bug.cgi?id=168837
+        &lt;rdar://problem/30700929&gt;
+
+        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  &lt;mmaxfield@apple.com&gt;
</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 &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+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>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;body&gt;
+&lt;script src=&quot;../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+description(&quot;Test that requestAnimationFrame gets the right throttling in an iframe when inserted into a document.&quot;);
+jsTestIsAsync = true;
+
+let i = 0;
+function doWork()
+{
+    i++;
+    requestAnimationFrame(doWork);
+}
+
+requestAnimationFrame(doWork);
+
+const frame = document.createElement(&quot;iframe&quot;);
+frame.src = &quot;resources/frame-with-animation.html&quot;;
+frame.onload = function() {
+    shouldBeFalse(&quot;internals.isRequestAnimationFrameThrottled()&quot;);
+    shouldBe(&quot;internals.requestAnimationFrameInterval&quot;, &quot;0.015&quot;);
+    shouldBeFalse(&quot;frame.contentWindow.internals.isRequestAnimationFrameThrottled()&quot;);
+    shouldBe(&quot;frame.contentWindow.internals.requestAnimationFrameInterval&quot;, &quot;0.015&quot;);
+
+    evalAndLog(&quot;internals.setLowPowerModeEnabled(true);&quot;);
+    shouldBeTrue(&quot;internals.isRequestAnimationFrameThrottled()&quot;);
+    shouldBe(&quot;internals.requestAnimationFrameInterval&quot;, &quot;0.030&quot;);
+    shouldBeTrue(&quot;frame.contentWindow.internals.isRequestAnimationFrameThrottled()&quot;);
+    shouldBe(&quot;frame.contentWindow.internals.requestAnimationFrameInterval&quot;, &quot;0.030&quot;);
+    evalAndLog(&quot;frame.remove()&quot;);
+
+    evalAndLog(&quot;document.body.appendChild(frame)&quot;);
+    frame.onload = function() {
+        shouldBeTrue(&quot;internals.isRequestAnimationFrameThrottled()&quot;);
+        shouldBe(&quot;internals.requestAnimationFrameInterval&quot;, &quot;0.030&quot;);
+        shouldBeTrue(&quot;frame.contentWindow.internals.isRequestAnimationFrameThrottled()&quot;);
+        shouldBe(&quot;frame.contentWindow.internals.requestAnimationFrameInterval&quot;, &quot;0.030&quot;);
+
+        evalAndLog(&quot;frame.remove()&quot;);
+        evalAndLog(&quot;internals.setLowPowerModeEnabled(false);&quot;);
+        shouldBeFalse(&quot;internals.isRequestAnimationFrameThrottled()&quot;);
+        shouldBe(&quot;internals.requestAnimationFrameInterval&quot;, &quot;0.015&quot;);
+
+        evalAndLog(&quot;document.body.appendChild(frame)&quot;);
+        frame.onload = function() {
+            shouldBeFalse(&quot;frame.contentWindow.internals.isRequestAnimationFrameThrottled()&quot;);
+            shouldBe(&quot;frame.contentWindow.internals.requestAnimationFrameInterval&quot;, &quot;0.015&quot;);
+            finishJSTest();
+        }
+    }
+};
+document.body.appendChild(frame);
+&lt;/script&gt;
+&lt;script src=&quot;../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</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 &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+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>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;body&gt;
+&lt;script src=&quot;../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+description(&quot;Test that requestAnimationFrame gets throttled in low power mode.&quot;);
+
+let rAFHandle;
+let i = 0;
+function doWork()
+{
+    i++;
+    rAFHandle = requestAnimationFrame(doWork);
+}
+
+shouldBeFalse(&quot;internals.isRequestAnimationFrameThrottled()&quot;);
+shouldBe(&quot;internals.requestAnimationFrameInterval&quot;, &quot;Infinity&quot;);
+evalAndLog(&quot;rAFHandle = requestAnimationFrame(doWork);&quot;);
+shouldBeFalse(&quot;internals.isRequestAnimationFrameThrottled()&quot;);
+shouldBe(&quot;internals.requestAnimationFrameInterval&quot;, &quot;0.015&quot;);
+evalAndLog(&quot;internals.setLowPowerModeEnabled(true);&quot;);
+shouldBeTrue(&quot;internals.isRequestAnimationFrameThrottled()&quot;);
+shouldBe(&quot;internals.requestAnimationFrameInterval&quot;, &quot;0.030&quot;);
+evalAndLog(&quot;cancelAnimationFrame(rAFHandle);&quot;);
+shouldBeTrue(&quot;internals.isRequestAnimationFrameThrottled()&quot;);
+shouldBe(&quot;internals.requestAnimationFrameInterval&quot;, &quot;0.030&quot;);
+evalAndLog(&quot;rAFHandle = requestAnimationFrame(doWork);&quot;);
+shouldBeTrue(&quot;internals.isRequestAnimationFrameThrottled()&quot;);
+shouldBe(&quot;internals.requestAnimationFrameInterval&quot;, &quot;0.030&quot;);
+evalAndLog(&quot;internals.setLowPowerModeEnabled(false);&quot;);
+shouldBeFalse(&quot;internals.isRequestAnimationFrameThrottled()&quot;);
+shouldBe(&quot;internals.requestAnimationFrameInterval&quot;, &quot;0.015&quot;);
+&lt;/script&gt;
+&lt;script src=&quot;../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</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>+&lt;script&gt;
+let i = 0;
+function doWork()
+{
+    i++;
+    requestAnimationFrame(doWork);
+}
+
+requestAnimationFrame(doWork);
+&lt;/script&gt;
</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  &lt;cdumez@apple.com&gt;
+
+        [iOS] Throttle requestAnimationFrame to 30fps in low power mode
+        https://bugs.webkit.org/show_bug.cgi?id=168837
+        &lt;rdar://problem/30700929&gt;
+
+        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  &lt;msaboff@apple.com&gt;
</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&amp; operator-=(OptionSet&amp; lhs, OptionSet rhs)
+    {
+        lhs.m_storage &amp;= ~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 &amp; ~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  &lt;cdumez@apple.com&gt;
+
+        [iOS] Throttle requestAnimationFrame to 30fps in low power mode
+        https://bugs.webkit.org/show_bug.cgi?id=168837
+        &lt;rdar://problem/30700929&gt;
+
+        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  &lt;youenn@apple.com&gt;
</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-&gt;resume();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void Document::scriptedAnimationControllerSetThrottled(bool isThrottled)
-{
-    if (m_scriptedAnimationController)
-        m_scriptedAnimationController-&gt;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()-&gt;chrome().displayID() : 0);
</del><ins>+        m_scriptedAnimationController = ScriptedAnimationController::create(*this, page() ? page()-&gt;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 &quot;Settings.h&quot;
</span><span class="cx"> #include &lt;wtf/Ref.h&gt;
</span><span class="cx"> #include &lt;wtf/SystemTracing.h&gt;
</span><ins>+#include &lt;wtf/text/StringBuilder.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> #if USE(REQUEST_ANIMATION_FRAME_TIMER)
</span><span class="cx"> #include &lt;algorithm&gt;
</span><span class="lines">@@ -46,19 +47,27 @@
</span><span class="cx"> #include &lt;wtf/CurrentTime.h&gt;
</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() &amp;&amp; page()-&gt;isAlwaysOnLoggingAllowed(), PerformanceLogging, &quot;%p - ScriptedAnimationController::&quot; 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&amp; document, PlatformDisplayID displayID)
+    : m_document(&amp;document)
</ins><span class="cx"> #if USE(REQUEST_ANIMATION_FRAME_TIMER)
</span><span class="cx">     , m_animationTimer(*this, &amp;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 &amp;&amp; page-&gt;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) &amp;&amp; USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) &amp;&amp; !RELEASE_LOG_DISABLED
+
+static const char* throttlingReasonToString(ScriptedAnimationController::ThrottlingReason reason)
</ins><span class="cx"> {
</span><ins>+    switch (reason) {
+    case ScriptedAnimationController::ThrottlingReason::VisuallyIdle:
+        return &quot;VisuallyIdle&quot;;
+    case ScriptedAnimationController::ThrottlingReason::OutsideViewport:
+        return &quot;OutsideViewport&quot;;
+    case ScriptedAnimationController::ThrottlingReason::LowPowerMode:
+        return &quot;LowPowerMode&quot;;
+    }
+}
+
+static String throttlingReasonsToString(OptionSet&lt;ScriptedAnimationController::ThrottlingReason&gt; reasons)
+{
+    if (reasons.isEmpty())
+        return ASCIILiteral(&quot;[Unthrottled]&quot;);
+
+    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) &amp;&amp; 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, &quot;%p - Setting RequestAnimationFrame throttling state to %d in frame %p (isMainFrame: %d)&quot;, this, isThrottled, m_document-&gt;frame(), m_document-&gt;frame() ? m_document-&gt;frame()-&gt;isMainFrame() : 0);
</del><ins>+    m_throttlingReasons |= reason;
</ins><span class="cx"> 
</span><del>-    m_isThrottled = isThrottled;
</del><ins>+    RELEASE_LOG_IF_ALLOWED(&quot;addThrottlingReason(%s) -&gt; %s&quot;, 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) &amp;&amp; USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+    if (!m_throttlingReasons.contains(reason))
+        return;
+
+    m_throttlingReasons -= reason;
+
+    RELEASE_LOG_IF_ALLOWED(&quot;removeThrottlingReason(%s) -&gt; %s&quot;, 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) &amp;&amp; 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) &amp;&amp; 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-&gt;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 &amp;&amp; !m_isThrottled) {
</del><ins>+    if (!m_isUsingTimer &amp;&amp; !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) &amp;&amp; USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
-    if (m_isThrottled)
-        animationInterval = MinimumThrottledAnimationInterval;
-#endif
-
-    double scheduleDelay = std::max&lt;double&gt;(animationInterval - (m_document-&gt;domWindow()-&gt;nowTimestamp() - m_lastAnimationFrameTimestamp), 0);
</del><ins>+    Seconds animationInterval = interval();
+    double scheduleDelay = std::max&lt;double&gt;(animationInterval.value() - (m_document-&gt;domWindow()-&gt;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-&gt;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 &quot;DOMTimeStamp.h&quot;
</span><span class="cx"> #include &quot;PlatformScreen.h&quot;
</span><ins>+#include &lt;wtf/OptionSet.h&gt;
</ins><span class="cx"> #include &lt;wtf/RefCounted.h&gt;
</span><span class="cx"> #include &lt;wtf/RefPtr.h&gt;
</span><span class="cx"> #include &lt;wtf/Vector.h&gt;
</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&lt;ScriptedAnimationController&gt;
</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&lt;ScriptedAnimationController&gt; create(Document* document, PlatformDisplayID displayID)
</del><ins>+    static Ref&lt;ScriptedAnimationController&gt; create(Document&amp; 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 &lt;&lt; 0,
+        OutsideViewport = 1 &lt;&lt; 1,
+        LowPowerMode    = 1 &lt;&lt; 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&amp;, PlatformDisplayID);
</ins><span class="cx"> 
</span><ins>+    Page* page() const;
+
</ins><span class="cx">     typedef Vector&lt;RefPtr&lt;RequestAnimationFrameCallback&gt;&gt; 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&lt;DisplayRefreshMonitor&gt; 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&lt;ThrottlingReason&gt; 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() &amp;&amp; !m_size.isEmpty() &amp;&amp; frame().ownerRenderer();
</span><span class="cx"> 
</span><del>-    if (auto* scriptedAnimationController = document-&gt;scriptedAnimationController())
-        scriptedAnimationController-&gt;setThrottled(shouldThrottle);
</del><ins>+    if (auto* scriptedAnimationController = document-&gt;scriptedAnimationController()) {
+        if (shouldThrottle)
+            scriptedAnimationController-&gt;addThrottlingReason(ScriptedAnimationController::ThrottlingReason::OutsideViewport);
+        else
+            scriptedAnimationController-&gt;removeThrottlingReason(ScriptedAnimationController::ThrottlingReason::OutsideViewport);
+    }
</ins><span class="cx"> 
</span><span class="cx">     document-&gt;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 &quot;InspectorInstrumentation.h&quot;
</span><span class="cx"> #include &quot;LibWebRTCProvider.h&quot;
</span><span class="cx"> #include &quot;Logging.h&quot;
</span><ins>+#include &quot;LowPowerModeNotifier.h&quot;
</ins><span class="cx"> #include &quot;MainFrame.h&quot;
</span><span class="cx"> #include &quot;MediaCanStartListener.h&quot;
</span><span class="cx"> #include &quot;Navigator.h&quot;
</span><span class="lines">@@ -84,6 +85,7 @@
</span><span class="cx"> #include &quot;SVGDocumentExtensions.h&quot;
</span><span class="cx"> #include &quot;SchemeRegistry.h&quot;
</span><span class="cx"> #include &quot;ScriptController.h&quot;
</span><ins>+#include &quot;ScriptedAnimationController.h&quot;
</ins><span class="cx"> #include &quot;ScrollingCoordinator.h&quot;
</span><span class="cx"> #include &quot;Settings.h&quot;
</span><span class="cx"> #include &quot;SharedBuffer.h&quot;
</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&lt;PerformanceMonitor&gt;(*this))
</span><ins>+    , m_lowPowerModeNotifier(std::make_unique&lt;LowPowerModeNotifier&gt;([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() &amp;&amp; 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-&gt;isLowPowerModeEnabled();
+}
+
+void Page::setLowPowerModeEnabledOverrideForTesting(std::optional&lt;bool&gt; 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&amp; page, ThrottlingReasonOperation operation, ScriptedAnimationController::ThrottlingReason reason)
</ins><span class="cx"> {
</span><del>-    for (Frame* frame = &amp;mainFrame(); frame; frame = frame-&gt;tree().traverseNext()) {
-        if (frame-&gt;document())
-            frame-&gt;document()-&gt;scriptedAnimationControllerSetThrottled(isVisuallyIdle);
</del><ins>+    for (Frame* frame = &amp;page.mainFrame(); frame; frame = frame-&gt;tree().traverseNext()) {
+        auto* document = frame-&gt;document();
+        if (!document)
+            continue;
+        auto* scriptedAnimationController = document-&gt;scriptedAnimationController();
+        if (!scriptedAnimationController)
+            continue;
+
+        if (operation == ThrottlingReasonOperation::Add)
+            scriptedAnimationController-&gt;addThrottlingReason(reason);
+        else
+            scriptedAnimationController-&gt;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&amp;) const;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+    bool isLowPowerModeEnabled() const;
+    WEBCORE_EXPORT void setLowPowerModeEnabledOverrideForTesting(std::optional&lt;bool&gt;);
+
</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&lt;Ref&lt;PluginViewBase&gt;&gt; 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&lt;EventThrottlingBehavior&gt; m_eventThrottlingBehaviorOverride;
</span><span class="cx"> 
</span><span class="cx">     std::unique_ptr&lt;PerformanceMonitor&gt; m_performanceMonitor;
</span><ins>+    std::unique_ptr&lt;LowPowerModeNotifier&gt; m_lowPowerModeNotifier;
+    std::optional&lt;bool&gt; 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(&quot;&quot;);
</span><span class="lines">@@ -1019,6 +1020,14 @@
</span><span class="cx">     return scriptedAnimationController-&gt;isThrottled();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+double Internals::requestAnimationFrameInterval() const
+{
+    auto* scriptedAnimationController = contextDocument()-&gt;scriptedAnimationController();
+    if (!scriptedAnimationController)
+        return INFINITY;
+    return scriptedAnimationController-&gt;interval().value();
+}
+
</ins><span class="cx"> bool Internals::areTimersThrottled() const
</span><span class="cx"> {
</span><span class="cx">     return contextDocument()-&gt;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&lt;void&gt; Internals::setLowPowerModeEnabled(bool isEnabled)
+{
+    auto* document = contextDocument();
+    if (!document)
+        return Exception { INVALID_ACCESS_ERR };
+    auto* page = document-&gt;page();
+    if (!page)
+        return Exception { INVALID_ACCESS_ERR };
+
+    page-&gt;setLowPowerModeEnabledOverrideForTesting(isEnabled);
+    return { };
+}
+
</ins><span class="cx"> ExceptionOr&lt;void&gt; 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&lt;bool&gt; 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&lt;void&gt; setMarkedTextMatchesAreHighlighted(bool);
</span><span class="cx"> 
</span><span class="cx">     void invalidateFontCache();
</span><ins>+    ExceptionOr&lt;void&gt; setLowPowerModeEnabled(bool);
</ins><span class="cx"> 
</span><span class="cx">     ExceptionOr&lt;void&gt; 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  &lt;cdumez@apple.com&gt;
+
+        [iOS] Throttle requestAnimationFrame to 30fps in low power mode
+        https://bugs.webkit.org/show_bug.cgi?id=168837
+        &lt;rdar://problem/30700929&gt;
+
+        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  &lt;jbedard@apple.com&gt;
</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&lt;ExampleFlags&gt; set { ExampleFlags::A, ExampleFlags::B, ExampleFlags::C };
+
+    EXPECT_TRUE(((set -= ExampleFlags::A) == OptionSet&lt;ExampleFlags&gt; { ExampleFlags::B, ExampleFlags::C }));
+    EXPECT_TRUE((set == OptionSet&lt;ExampleFlags&gt; { ExampleFlags::B, ExampleFlags::C }));
+    EXPECT_TRUE(((set -= ExampleFlags::D) == OptionSet&lt;ExampleFlags&gt; { ExampleFlags::B, ExampleFlags::C }));
+    EXPECT_TRUE((set == OptionSet&lt;ExampleFlags&gt; { 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&lt;ExampleFlags&gt; set { ExampleFlags::A, ExampleFlags::B };
</span></span></pre>
</div>
</div>

</body>
</html>