Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 26 Jan 2020 21:35:32 +0000 (21:35 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 26 Jan 2020 21:35:32 +0000 (21:35 +0000)
https://bugs.webkit.org/show_bug.cgi?id=204713

Patch by Said Abou-Hallawa <said@apple.com> on 2020-01-26
Reviewed by Simon Fraser.

Source/WebCore:

Test: fast/animation/request-animation-frame-throttling-outside-viewport.html

requestAnimationFrame is throttled by a timer although its callback are
serviced by the page RenderingUpdate. This led to excessive rAF firing
which makes it more than the preferred frame per seconds.

The solution is to have two throttling types:

1) Page throttling (or full throttling) which slows down all the steps of
   RenderingUpdate for the main document and all the sub-documents.
2) Document throttling (or partial throttling) which only slows down the
   rAF of a certain document.

* Headers.cmake:
* WebCore.xcodeproj/project.pbxproj:

* animation/DocumentTimeline.cpp:
(WebCore::DocumentTimeline::animationInterval const):
(WebCore::DocumentTimeline::updateThrottlingState): Deleted.
* animation/DocumentTimeline.h:
There is no need to have DocumentTimeline throttling. It is already
throttled when the page RenderingUpdate is throttled.

* dom/Document.cpp:
(WebCore::Document::requestAnimationFrame):
(WebCore::Document::updateLastHandledUserGestureTimestamp):
LowPowerMode throttling is now handled by the page. So remove its handling
in the Document side.

* dom/ScriptedAnimationController.cpp:
(WebCore::ScriptedAnimationController::ScriptedAnimationController):
(WebCore::ScriptedAnimationController::page const):
(WebCore::ScriptedAnimationController::preferredScriptedAnimationInterval const):
(WebCore::ScriptedAnimationController::interval const):
(WebCore::ScriptedAnimationController::isThrottled const):
(WebCore::ScriptedAnimationController::isThrottledRelativeToPage const):
(WebCore::ScriptedAnimationController::shouldRescheduleRequestAnimationFrame const):
(WebCore::ScriptedAnimationController::registerCallback):
(WebCore::ScriptedAnimationController::cancelCallback):
(WebCore::ScriptedAnimationController::serviceRequestAnimationFrameCallbacks):
(WebCore::ScriptedAnimationController::scheduleAnimation):
(WebCore::throttlingReasonToString): Deleted.
(WebCore::throttlingReasonsToString): Deleted.
(WebCore::ScriptedAnimationController::addThrottlingReason): Deleted.
(WebCore::ScriptedAnimationController::removeThrottlingReason): Deleted.
(WebCore::ScriptedAnimationController::animationTimerFired): Deleted.
* dom/ScriptedAnimationController.h:
(WebCore::ScriptedAnimationController::addThrottlingReason):
(WebCore::ScriptedAnimationController::removeThrottlingReason):
Get rid of the rAF throttling timer. Service the rAF callback only when
the period from the current time stamp till the last service time stamp
is greater than the preferred rAF interval .

* page/FrameView.cpp:
(WebCore::FrameView::updateScriptedAnimationsAndTimersThrottlingState):
ThrottlingReason is now defined outside ScriptedAnimationController.

* page/Page.cpp:
(WebCore::Page::renderingUpdateThrottlingEnabled const):
(WebCore::Page::renderingUpdateThrottlingEnabledChanged):
(WebCore::Page::isRenderingUpdateThrottled const):

(WebCore::Page::preferredRenderingUpdateInterval const):
Calculate the preferred RenderingUpdate interval from the throttling
reasons.

(WebCore::Page::setIsVisuallyIdleInternal):
(WebCore::Page::handleLowModePowerChange):
Call adjustRenderingUpdateFrequency() when isLowPowerModeEnabled or
IsVisuallyIdle is toggled.

(WebCore::updateScriptedAnimationsThrottlingReason): Deleted.
* page/Page.h:

* page/RenderingUpdateScheduler.cpp:
(WebCore::RenderingUpdateScheduler::adjustFramesPerSecond):
(WebCore::RenderingUpdateScheduler::adjustRenderingUpdateFrequency):
Change the preferredFramesPerSecond of the DisplayRefreshMonitor if the
throttling is not aggressive e.g. 10_s. Otherwise use the timer.

(WebCore::RenderingUpdateScheduler::scheduleTimedRenderingUpdate):
Call adjustFramesPerSecond() when DisplayRefreshMonitor is created.

(WebCore::RenderingUpdateScheduler::startTimer):
* page/RenderingUpdateScheduler.h:

* page/Settings.yaml:
* page/SettingsBase.cpp:
(WebCore::SettingsBase::renderingUpdateThrottlingEnabledChanged):
* page/SettingsBase.h:
Add a setting to enable/disable RenderingUpdateThrottling.

* platform/graphics/AnimationFrameRate.h: Added.
(WebCore::preferredFrameInterval):
(WebCore::preferredFramesPerSecond):

* platform/graphics/DisplayRefreshMonitor.h:
(WebCore::DisplayRefreshMonitor::setPreferredFramesPerSecond):
* platform/graphics/DisplayRefreshMonitorManager.cpp:
(WebCore::DisplayRefreshMonitorManager::monitorForClient):
Rename createMonitorForClient() to monitorForClient() since it may return
a cached DisplayRefreshMonitor.

(WebCore::DisplayRefreshMonitorManager::setPreferredFramesPerSecond):
(WebCore::DisplayRefreshMonitorManager::scheduleAnimation):
(WebCore::DisplayRefreshMonitorManager::windowScreenDidChange):
No need to call registerClient(). This function was just ensuring the
DisplayRefreshMonitor is created. scheduleAnimation() does the same thing.

(WebCore::DisplayRefreshMonitorManager::createMonitorForClient): Deleted.
(WebCore::DisplayRefreshMonitorManager::registerClient): Deleted.
* platform/graphics/DisplayRefreshMonitorManager.h:
(WebCore::DisplayRefreshMonitorManager::DisplayRefreshMonitorManager): Deleted.

* platform/graphics/GraphicsLayerUpdater.cpp:
(WebCore::GraphicsLayerUpdater::GraphicsLayerUpdater):
* platform/graphics/ios/DisplayRefreshMonitorIOS.mm:
(-[WebDisplayLinkHandler setPreferredFramesPerSecond:]):
Set the preferredFramesPerSecond of the CADisplayLink.

* platform/ios/LowPowerModeNotifierIOS.mm:
(-[WebLowPowerModeObserver initWithNotifier:]):
Set the initial state of the low power mode.

Source/WebKit:

Create an IPC message on the DrawingArea to send a message from the
WebProcess to the UIProcess to setPreferredFramesPerSecond of the
DisplayRefreshMonitor.

* Shared/WebPreferences.yaml:
* UIProcess/API/C/WKPreferences.cpp:
(WKPreferencesSetRenderingUpdateThrottlingEnabled):
(WKPreferencesGetRenderingUpdateThrottlingEnabled):
* UIProcess/API/C/WKPreferencesRefPrivate.h:
Add a WKPreference key for RenderingUpdateThrottlingEnabled.

* UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.h:
* UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.messages.in:

* UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.mm:
(-[WKOneShotDisplayLinkHandler setPreferredFramesPerSecond:]):
(WebKit::RemoteLayerTreeDrawingAreaProxy::setPreferredFramesPerSecond):
Set the preferredFramesPerSecond of the CADisplayLink.

* WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.h:
* WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.mm:
(WebKit::RemoteLayerTreeDisplayRefreshMonitor::setPreferredFramesPerSecond):
Delegate the call to RemoteLayerTreeDrawingArea.

* WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.h:
* WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.mm:
(WebKit::RemoteLayerTreeDrawingArea::setPreferredFramesPerSecond):
Send the IPC message from the WebProcess to the UIProcess.

Source/WebKitLegacy/mac:

Add a WKPreference key for RenderingUpdateThrottling.

* WebView/WebPreferenceKeysPrivate.h:
* WebView/WebPreferences.mm:
(+[WebPreferences initialize]):
(-[WebPreferences renderingUpdateThrottlingEnabled]):
(-[WebPreferences setRenderingUpdateThrottlingEnabled:]):
* WebView/WebPreferencesPrivate.h:
* WebView/WebView.mm:
(-[WebView _preferencesChanged:]):

Source/WebKitLegacy/win:

Add a WKPreference key for RenderingUpdateThrottling.

* Interfaces/IWebPreferencesPrivate.idl:
* WebPreferenceKeysPrivate.h:
* WebPreferences.cpp:
(WebPreferences::initializeDefaultSettings):
(WebPreferences::renderingUpdateThrottlingEnabled):
(WebPreferences::setRenderingUpdateThrottlingEnabled):
* WebPreferences.h:
* WebView.cpp:
(WebView::notifyPreferencesChanged):

Tools:

RenderingUpdateThrottling is enabled by default. Turn it off for DRT and
WTR. In some cases, the page may not get visually active while it's
waiting for rAF. Throttling tests will have to explicitly turn it on.

* DumpRenderTree/mac/DumpRenderTree.mm:
(resetWebPreferencesToConsistentValues):
* DumpRenderTree/win/DumpRenderTree.cpp:
(resetWebPreferencesToConsistentValues):
* WebKitTestRunner/TestController.cpp:
(WTR::TestController::resetPreferencesToConsistentValues):

LayoutTests:

* fast/animation/css-animation-throttling-lowPowerMode.html:
* fast/animation/request-animation-frame-throttle-subframe.html:
* fast/animation/request-animation-frame-throttling-detached-iframe.html:
Enable RenderingUpdateThrottling for these tests.

* fast/animation/request-animation-frame-throttling-lowPowerMode-expected.txt:
* fast/animation/request-animation-frame-throttling-lowPowerMode.html:
Ensure the actual rAF interval is > 30ms for lowPowerMode.

* fast/animation/request-animation-frame-throttling-outside-viewport-expected.txt: Added.
* fast/animation/request-animation-frame-throttling-outside-viewport.html: Added.
* fast/animation/resources/frame-with-animation-2.html: Added.
Test the OutsideViewport throttling case.

* http/tests/frame-throttling/raf-throttle-in-cross-origin-subframe.html:
Enable RenderingUpdateThrottling for this test.

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

59 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/animation/css-animation-throttling-lowPowerMode.html
LayoutTests/fast/animation/request-animation-frame-throttle-subframe.html
LayoutTests/fast/animation/request-animation-frame-throttling-detached-iframe.html
LayoutTests/fast/animation/request-animation-frame-throttling-lowPowerMode-expected.txt
LayoutTests/fast/animation/request-animation-frame-throttling-lowPowerMode.html
LayoutTests/fast/animation/request-animation-frame-throttling-outside-viewport-expected.txt [new file with mode: 0644]
LayoutTests/fast/animation/request-animation-frame-throttling-outside-viewport.html [new file with mode: 0644]
LayoutTests/fast/animation/resources/frame-with-animation-2.html [new file with mode: 0644]
LayoutTests/http/tests/frame-throttling/raf-throttle-in-cross-origin-subframe.html
Source/WebCore/ChangeLog
Source/WebCore/Headers.cmake
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/animation/DocumentTimeline.cpp
Source/WebCore/animation/DocumentTimeline.h
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/ScriptedAnimationController.cpp
Source/WebCore/dom/ScriptedAnimationController.h
Source/WebCore/page/FrameView.cpp
Source/WebCore/page/Page.cpp
Source/WebCore/page/Page.h
Source/WebCore/page/RenderingUpdateScheduler.cpp
Source/WebCore/page/RenderingUpdateScheduler.h
Source/WebCore/page/Settings.yaml
Source/WebCore/page/SettingsBase.cpp
Source/WebCore/page/SettingsBase.h
Source/WebCore/platform/graphics/AnimationFrameRate.h [new file with mode: 0644]
Source/WebCore/platform/graphics/DisplayRefreshMonitor.h
Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.cpp
Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.h
Source/WebCore/platform/graphics/GraphicsLayerUpdater.cpp
Source/WebCore/platform/graphics/ios/DisplayRefreshMonitorIOS.mm
Source/WebCore/platform/ios/LowPowerModeNotifierIOS.mm
Source/WebKit/ChangeLog
Source/WebKit/Shared/WebPreferences.yaml
Source/WebKit/UIProcess/API/C/WKPreferences.cpp
Source/WebKit/UIProcess/API/C/WKPreferencesRefPrivate.h
Source/WebKit/UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.h
Source/WebKit/UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.messages.in
Source/WebKit/UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.mm
Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.h
Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.mm
Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.h
Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.mm
Source/WebKitLegacy/mac/ChangeLog
Source/WebKitLegacy/mac/WebView/WebPreferenceKeysPrivate.h
Source/WebKitLegacy/mac/WebView/WebPreferences.mm
Source/WebKitLegacy/mac/WebView/WebPreferencesPrivate.h
Source/WebKitLegacy/mac/WebView/WebView.mm
Source/WebKitLegacy/win/ChangeLog
Source/WebKitLegacy/win/Interfaces/IWebPreferencesPrivate.idl
Source/WebKitLegacy/win/WebPreferenceKeysPrivate.h
Source/WebKitLegacy/win/WebPreferences.cpp
Source/WebKitLegacy/win/WebPreferences.h
Source/WebKitLegacy/win/WebView.cpp
Tools/ChangeLog
Tools/DumpRenderTree/mac/DumpRenderTree.mm
Tools/DumpRenderTree/win/DumpRenderTree.cpp
Tools/WebKitTestRunner/TestController.cpp

index f0367f6..c454b03 100644 (file)
@@ -1,3 +1,27 @@
+2020-01-26  Said Abou-Hallawa  <said@apple.com>
+
+        Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
+        https://bugs.webkit.org/show_bug.cgi?id=204713
+
+        Reviewed by Simon Fraser.
+
+        * fast/animation/css-animation-throttling-lowPowerMode.html:
+        * fast/animation/request-animation-frame-throttle-subframe.html:
+        * fast/animation/request-animation-frame-throttling-detached-iframe.html:
+        Enable RenderingUpdateThrottling for these tests.
+
+        * fast/animation/request-animation-frame-throttling-lowPowerMode-expected.txt:
+        * fast/animation/request-animation-frame-throttling-lowPowerMode.html:
+        Ensure the actual rAF interval is > 30ms for lowPowerMode.
+
+        * fast/animation/request-animation-frame-throttling-outside-viewport-expected.txt: Added.
+        * fast/animation/request-animation-frame-throttling-outside-viewport.html: Added.
+        * fast/animation/resources/frame-with-animation-2.html: Added.
+        Test the OutsideViewport throttling case.
+
+        * http/tests/frame-throttling/raf-throttle-in-cross-origin-subframe.html:
+        Enable RenderingUpdateThrottling for this test.
+
 2020-01-26  Eric Carlson  <eric.carlson@apple.com>
 
         media/modern-media-controls/media-controller/media-controller-auto-hide.html is flaky timing out
index b7d0564..f8d7e4c 100644 (file)
@@ -25,6 +25,9 @@
 description("Tests that CSS animations are throttled in low power mode.");
 jsTestIsAsync = true;
 
+if (window.internals)
+  internals.settings.setRenderingUpdateThrottlingEnabled(true);
+
 const element = document.getElementById("testElement");
 element.onanimationstart = function() {
     element.onanimationstart = null;
index f8940f3..4c49fee 100644 (file)
@@ -6,6 +6,9 @@
 description("Tests that requestAnimationFrame is throttled in subframes that are outside the viewport");
 window.jsTestIsAsync = true;
 
+if (window.internals)
+    internals.settings.setRenderingUpdateThrottlingEnabled(true);
+
 function checkSubframesThrottled()
 {
     shouldBeTrue("testFrame.contentWindow.internals.isRequestAnimationFrameThrottled()");
index 74280c3..e7c93e9 100644 (file)
@@ -6,6 +6,9 @@
 description("Test that requestAnimationFrame gets the right throttling in an iframe when inserted into a document.");
 jsTestIsAsync = true;
 
+if (window.internals)
+    internals.settings.setRenderingUpdateThrottlingEnabled(true);
+
 let i = 0;
 function doWork()
 {
index 9833ffb..353c21e 100644 (file)
@@ -3,23 +3,7 @@ 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 farmesPerSecond < 35 is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
index dbae09f..fe05bf9 100644 (file)
@@ -1,36 +1,33 @@
 <!DOCTYPE html>
 <html>
 <body>
-<script src="../../resources/js-test-pre.js"></script>
-<script>
-description("Test that requestAnimationFrame gets throttled in low power mode.");
+    <script src="../../resources/js-test-pre.js"></script>
+    <script>
+        description("Test that requestAnimationFrame gets throttled in low power mode.");
+        window.jsTestIsAsync = true;
 
-let rAFHandle;
-let i = 0;
-function doWork()
-{
-    i++;
-    rAFHandle = requestAnimationFrame(doWork);
-}
+        if (window.internals) {
+            internals.settings.setRenderingUpdateThrottlingEnabled(true);
+            internals.setLowPowerModeEnabled(true);
+        }
 
-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>
+        var start = null;
+        var farmesPerSecond = 0;
+        function doWork(timestamp) {
+            if (!start)
+                start = timestamp;
+            if (timestamp - start < 1000) {
+                ++farmesPerSecond;
+                window.requestAnimationFrame(doWork);
+            }
+            else {
+                // The LowPowerMode throttling interval = 30_ms. The frame rate ~= 33.3 fps.
+                shouldBeTrue("farmesPerSecond < 35");
+                finishJSTest();
+            }
+        }
+        window.requestAnimationFrame(doWork);
+    </script>
+    <script src="../../resources/js-test-post.js"></script>
 </body>
 </html>
diff --git a/LayoutTests/fast/animation/request-animation-frame-throttling-outside-viewport-expected.txt b/LayoutTests/fast/animation/request-animation-frame-throttling-outside-viewport-expected.txt
new file mode 100644 (file)
index 0000000..7c8bb5a
--- /dev/null
@@ -0,0 +1,11 @@
+Test that requestAnimationFrame gets the right throttling in an iframe when when it's outside the viewport.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS framesPerSecond > 0 is true
+PASS iframeFramesPerSecond == 0 is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/animation/request-animation-frame-throttling-outside-viewport.html b/LayoutTests/fast/animation/request-animation-frame-throttling-outside-viewport.html
new file mode 100644 (file)
index 0000000..5a5fd19
--- /dev/null
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<body>
+    <div style="height: 1000px;"></div>
+    <script src="../../resources/js-test-pre.js"></script>
+    <script>
+        description("Test that requestAnimationFrame gets the right throttling in an iframe when when it's outside the viewport.");
+        jsTestIsAsync = true;
+
+        if (window.internals)
+            internals.settings.setRenderingUpdateThrottlingEnabled(true);
+
+        var framesPerSecond = 0;
+        var iframeFramesPerSecond = 0;
+
+        window.onmessage = function(e){
+            if (e.data == 'subFrameRAFMessage') {
+                ++iframeFramesPerSecond;
+            }
+        };
+
+        const frame = document.createElement("iframe");
+        frame.src = "resources/frame-with-animation-2.html";
+        frame.onload = function() {
+            var start = null;
+            function doWork(timestamp) {
+                if (!start)
+                    start = timestamp;
+                if (timestamp - start < 1000) {
+                    ++framesPerSecond;
+                    window.requestAnimationFrame(doWork);
+                }
+                else {
+                    shouldBeTrue("framesPerSecond > 0");
+
+                    // The OutsideViewport throttling = 10_s. subFrameRAFMessage
+                    // should not ever be received during the first second.
+                    shouldBeTrue("iframeFramesPerSecond == 0");
+                    finishJSTest();
+                }
+            }
+            window.requestAnimationFrame(doWork);
+        }
+        document.body.appendChild(frame);
+    </script>
+    <script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/animation/resources/frame-with-animation-2.html b/LayoutTests/fast/animation/resources/frame-with-animation-2.html
new file mode 100644 (file)
index 0000000..883fc0e
--- /dev/null
@@ -0,0 +1,7 @@
+<script>
+       function doWork(timestamp) {
+               window.top.postMessage('subFrameRAFMessage', '*');
+               window.requestAnimationFrame(doWork);
+       }
+       window.requestAnimationFrame(doWork);
+</script>
index 70f1dfa..70e8c4c 100644 (file)
     <script>
     description("Tests that requestAnimationFrame is throttled in subframes that are cross-origin, and not in same-origin frames");
     window.jsTestIsAsync = true;
-    
+
+    if (window.internals)
+        internals.settings.setRenderingUpdateThrottlingEnabled(true);
+
     var crossOriginFrame;
     var sameOriginFrame
 
index 21076e2..2d7519d 100644 (file)
@@ -1,3 +1,134 @@
+2020-01-26  Said Abou-Hallawa  <said@apple.com>
+
+        Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
+        https://bugs.webkit.org/show_bug.cgi?id=204713
+
+        Reviewed by Simon Fraser.
+
+        Test: fast/animation/request-animation-frame-throttling-outside-viewport.html
+
+        requestAnimationFrame is throttled by a timer although its callback are
+        serviced by the page RenderingUpdate. This led to excessive rAF firing
+        which makes it more than the preferred frame per seconds.
+
+        The solution is to have two throttling types:
+
+        1) Page throttling (or full throttling) which slows down all the steps of
+           RenderingUpdate for the main document and all the sub-documents.
+        2) Document throttling (or partial throttling) which only slows down the
+           rAF of a certain document.
+
+        * Headers.cmake:
+        * WebCore.xcodeproj/project.pbxproj:
+
+        * animation/DocumentTimeline.cpp:
+        (WebCore::DocumentTimeline::animationInterval const):
+        (WebCore::DocumentTimeline::updateThrottlingState): Deleted.
+        * animation/DocumentTimeline.h:
+        There is no need to have DocumentTimeline throttling. It is already 
+        throttled when the page RenderingUpdate is throttled.
+
+        * dom/Document.cpp:
+        (WebCore::Document::requestAnimationFrame):
+        (WebCore::Document::updateLastHandledUserGestureTimestamp):
+        LowPowerMode throttling is now handled by the page. So remove its handling
+        in the Document side.
+
+        * dom/ScriptedAnimationController.cpp:
+        (WebCore::ScriptedAnimationController::ScriptedAnimationController):
+        (WebCore::ScriptedAnimationController::page const):
+        (WebCore::ScriptedAnimationController::preferredScriptedAnimationInterval const):
+        (WebCore::ScriptedAnimationController::interval const):
+        (WebCore::ScriptedAnimationController::isThrottled const):
+        (WebCore::ScriptedAnimationController::isThrottledRelativeToPage const):
+        (WebCore::ScriptedAnimationController::shouldRescheduleRequestAnimationFrame const):
+        (WebCore::ScriptedAnimationController::registerCallback):
+        (WebCore::ScriptedAnimationController::cancelCallback):
+        (WebCore::ScriptedAnimationController::serviceRequestAnimationFrameCallbacks):
+        (WebCore::ScriptedAnimationController::scheduleAnimation):
+        (WebCore::throttlingReasonToString): Deleted.
+        (WebCore::throttlingReasonsToString): Deleted.
+        (WebCore::ScriptedAnimationController::addThrottlingReason): Deleted.
+        (WebCore::ScriptedAnimationController::removeThrottlingReason): Deleted.
+        (WebCore::ScriptedAnimationController::animationTimerFired): Deleted.
+        * dom/ScriptedAnimationController.h:
+        (WebCore::ScriptedAnimationController::addThrottlingReason):
+        (WebCore::ScriptedAnimationController::removeThrottlingReason):
+        Get rid of the rAF throttling timer. Service the rAF callback only when
+        the period from the current time stamp till the last service time stamp
+        is greater than the preferred rAF interval .
+
+        * page/FrameView.cpp:
+        (WebCore::FrameView::updateScriptedAnimationsAndTimersThrottlingState):
+        ThrottlingReason is now defined outside ScriptedAnimationController.
+
+        * page/Page.cpp:
+        (WebCore::Page::renderingUpdateThrottlingEnabled const):
+        (WebCore::Page::renderingUpdateThrottlingEnabledChanged):
+        (WebCore::Page::isRenderingUpdateThrottled const):
+
+        (WebCore::Page::preferredRenderingUpdateInterval const):
+        Calculate the preferred RenderingUpdate interval from the throttling
+        reasons.
+
+        (WebCore::Page::setIsVisuallyIdleInternal):
+        (WebCore::Page::handleLowModePowerChange):
+        Call adjustRenderingUpdateFrequency() when isLowPowerModeEnabled or 
+        IsVisuallyIdle is toggled.
+
+        (WebCore::updateScriptedAnimationsThrottlingReason): Deleted.
+        * page/Page.h:
+
+        * page/RenderingUpdateScheduler.cpp:
+        (WebCore::RenderingUpdateScheduler::adjustFramesPerSecond):
+        (WebCore::RenderingUpdateScheduler::adjustRenderingUpdateFrequency):
+        Change the preferredFramesPerSecond of the DisplayRefreshMonitor if the
+        throttling is not aggressive e.g. 10_s. Otherwise use the timer.
+
+        (WebCore::RenderingUpdateScheduler::scheduleTimedRenderingUpdate):
+        Call adjustFramesPerSecond() when DisplayRefreshMonitor is created.
+
+        (WebCore::RenderingUpdateScheduler::startTimer):
+        * page/RenderingUpdateScheduler.h:
+
+        * page/Settings.yaml:
+        * page/SettingsBase.cpp:
+        (WebCore::SettingsBase::renderingUpdateThrottlingEnabledChanged):
+        * page/SettingsBase.h:
+        Add a setting to enable/disable RenderingUpdateThrottling.
+
+        * platform/graphics/AnimationFrameRate.h: Added.
+        (WebCore::preferredFrameInterval):
+        (WebCore::preferredFramesPerSecond):
+
+        * platform/graphics/DisplayRefreshMonitor.h:
+        (WebCore::DisplayRefreshMonitor::setPreferredFramesPerSecond):
+        * platform/graphics/DisplayRefreshMonitorManager.cpp:
+        (WebCore::DisplayRefreshMonitorManager::monitorForClient):
+        Rename createMonitorForClient() to monitorForClient() since it may return
+        a cached DisplayRefreshMonitor.
+
+        (WebCore::DisplayRefreshMonitorManager::setPreferredFramesPerSecond):
+        (WebCore::DisplayRefreshMonitorManager::scheduleAnimation):
+        (WebCore::DisplayRefreshMonitorManager::windowScreenDidChange):
+        No need to call registerClient(). This function was just ensuring the
+        DisplayRefreshMonitor is created. scheduleAnimation() does the same thing.
+
+        (WebCore::DisplayRefreshMonitorManager::createMonitorForClient): Deleted.
+        (WebCore::DisplayRefreshMonitorManager::registerClient): Deleted.
+        * platform/graphics/DisplayRefreshMonitorManager.h:
+        (WebCore::DisplayRefreshMonitorManager::DisplayRefreshMonitorManager): Deleted.
+
+        * platform/graphics/GraphicsLayerUpdater.cpp:
+        (WebCore::GraphicsLayerUpdater::GraphicsLayerUpdater):
+        * platform/graphics/ios/DisplayRefreshMonitorIOS.mm:
+        (-[WebDisplayLinkHandler setPreferredFramesPerSecond:]):
+        Set the preferredFramesPerSecond of the CADisplayLink.
+
+        * platform/ios/LowPowerModeNotifierIOS.mm:
+        (-[WebLowPowerModeObserver initWithNotifier:]):
+        Set the initial state of the low power mode.
+
 2020-01-26  Rob Buis  <rbuis@igalia.com>
 
         Improve compatibility with hyperlink auditing spec
index 2cbd63f..d3e44d4 100644 (file)
@@ -1034,6 +1034,7 @@ set(WebCore_PRIVATE_FRAMEWORK_HEADERS
 
     platform/graphics/AlphaPremultiplication.h
     platform/graphics/ANGLEWebKitBridge.h
+    platform/graphics/AnimationFrameRate.h
     platform/graphics/AudioTrackPrivate.h
     platform/graphics/BitmapImage.h
     platform/graphics/Color.h
index 672f1cf..9ad8a93 100644 (file)
                72144333223EC8B000F12FF7 /* SVGProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = 55EE5363223B2A2400FBA944 /* SVGProperty.h */; settings = {ATTRIBUTES = (Private, ); }; };
                72144334223EC91600F12FF7 /* SVGPropertyOwner.h in Headers */ = {isa = PBXBuildFile; fileRef = 55EE5360223B2A2100FBA944 /* SVGPropertyOwner.h */; settings = {ATTRIBUTES = (Private, ); }; };
                72283F0E230B268C00F5D828 /* ImagePaintingOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 72C18A3F230B04B7006847C7 /* ImagePaintingOptions.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               722A815D238FDAF000C00583 /* AnimationFrameRate.h in Headers */ = {isa = PBXBuildFile; fileRef = 722A815C238FD50500C00583 /* AnimationFrameRate.h */; settings = {ATTRIBUTES = (Private, ); }; };
                724ED3321A3A8B2300F5F13C /* JSEXTBlendMinMax.h in Headers */ = {isa = PBXBuildFile; fileRef = 724ED3301A3A8B2300F5F13C /* JSEXTBlendMinMax.h */; };
                724EE5501DC80D7F00A91FFB /* ActivityState.h in Headers */ = {isa = PBXBuildFile; fileRef = 724EE54E1DC7F25B00A91FFB /* ActivityState.h */; settings = {ATTRIBUTES = (Private, ); }; };
                724EE5511DC80D8400A91FFB /* ActivityStateChangeObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 724EE54F1DC7F25B00A91FFB /* ActivityStateChangeObserver.h */; settings = {ATTRIBUTES = (Private, ); }; };
                71FF851822A3F81F005D5959 /* NavigatorMaxTouchPoints.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = NavigatorMaxTouchPoints.idl; sourceTree = "<group>"; };
                721443452240C8BA00F12FF7 /* SVGAnimatedValueProperty.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SVGAnimatedValueProperty.h; sourceTree = "<group>"; };
                721443462240CAD200F12FF7 /* SVGValueProperty.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SVGValueProperty.h; sourceTree = "<group>"; };
+               722A815C238FD50500C00583 /* AnimationFrameRate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AnimationFrameRate.h; sourceTree = "<group>"; };
                724ED3291A3A7E5400F5F13C /* EXTBlendMinMax.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EXTBlendMinMax.cpp; sourceTree = "<group>"; };
                724ED32A1A3A7E5400F5F13C /* EXTBlendMinMax.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTBlendMinMax.h; sourceTree = "<group>"; };
                724ED32B1A3A7E5400F5F13C /* EXTBlendMinMax.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = EXTBlendMinMax.idl; sourceTree = "<group>"; };
                                7299BC6423D686A600CC6883 /* AlphaPremultiplication.h */,
                                490707E41219C04300D90E51 /* ANGLEWebKitBridge.cpp */,
                                490707E51219C04300D90E51 /* ANGLEWebKitBridge.h */,
+                               722A815C238FD50500C00583 /* AnimationFrameRate.h */,
                                BEF29EE91715DD0900C4B4C9 /* AudioTrackPrivate.h */,
                                A89943270B42338700D7C802 /* BitmapImage.cpp */,
                                A89943260B42338700D7C802 /* BitmapImage.h */,
                                71EFCEDC202B38A900D7C411 /* AnimationEffect.h in Headers */,
                                71E2C42621C935280024F8C8 /* AnimationEffectPhase.h in Headers */,
                                319848011A1D817B00A13318 /* AnimationEvent.h in Headers */,
+                               722A815D238FDAF000C00583 /* AnimationFrameRate.h in Headers */,
                                49E912AD0EFAC906009D0CAF /* AnimationList.h in Headers */,
                                714C7C661FDAD2A100F2BEE1 /* AnimationPlaybackEvent.h in Headers */,
                                714C7C671FDAD2A900F2BEE1 /* AnimationPlaybackEventInit.h in Headers */,
index 18db091..2d5ae35 100644 (file)
@@ -44,9 +44,6 @@
 #include "RenderLayerBacking.h"
 #include <JavaScriptCore/VM.h>
 
-static const Seconds defaultAnimationInterval { 15_ms };
-static const Seconds throttledAnimationInterval { 30_ms };
-
 namespace WebCore {
 
 Ref<DocumentTimeline> DocumentTimeline::create(Document& document)
@@ -192,16 +189,11 @@ Vector<RefPtr<WebAnimation>> DocumentTimeline::getAnimations() const
     return animations;
 }
 
-void DocumentTimeline::updateThrottlingState()
-{
-    scheduleAnimationResolution();
-}
-
 Seconds DocumentTimeline::animationInterval() const
 {
     if (!m_document || !m_document->page())
         return Seconds::infinity();
-    return m_document->page()->isLowPowerModeEnabled() ? throttledAnimationInterval : defaultAnimationInterval;
+    return m_document->page()->preferredRenderingUpdateInterval();
 }
 
 void DocumentTimeline::suspendAnimations()
index 2c35d24..fcc3b03 100644 (file)
@@ -73,7 +73,6 @@ public:
     
     void updateAnimationsAndSendEvents(DOMHighResTimeStamp timestamp);
 
-    void updateThrottlingState();
     WEBCORE_EXPORT Seconds animationInterval() const;
     WEBCORE_EXPORT void suspendAnimations();
     WEBCORE_EXPORT void resumeAnimations();
index ce9aabc..60f87d3 100644 (file)
@@ -6499,11 +6499,8 @@ int Document::requestAnimationFrame(Ref<RequestAnimationFrameCallback>&& callbac
         if (!page() || page()->scriptedAnimationsSuspended())
             m_scriptedAnimationController->suspend();
 
-        if (page() && page()->isLowPowerModeEnabled())
-            m_scriptedAnimationController->addThrottlingReason(ScriptedAnimationController::ThrottlingReason::LowPowerMode);
-
         if (!topOrigin().canAccess(securityOrigin()) && !hasHadUserInteraction())
-            m_scriptedAnimationController->addThrottlingReason(ScriptedAnimationController::ThrottlingReason::NonInteractedCrossOriginFrame);
+            m_scriptedAnimationController->addThrottlingReason(ThrottlingReason::NonInteractedCrossOriginFrame);
     }
 
     return m_scriptedAnimationController->registerCallback(WTFMove(callback));
@@ -6742,7 +6739,7 @@ void Document::updateLastHandledUserGestureTimestamp(MonotonicTime time)
 
     if (static_cast<bool>(time) && m_scriptedAnimationController) {
         // It's OK to always remove NonInteractedCrossOriginFrame even if this frame isn't cross-origin.
-        m_scriptedAnimationController->removeThrottlingReason(ScriptedAnimationController::ThrottlingReason::NonInteractedCrossOriginFrame);
+        m_scriptedAnimationController->removeThrottlingReason(ThrottlingReason::NonInteractedCrossOriginFrame);
     }
 
     // DOM Timer alignment may depend on the user having interacted with the document.
index 014d4cd..d7f6875 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2011 Google Inc. All Rights Reserved.
+ * Copyright (C) 2019 Apple Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "config.h"
 #include "ScriptedAnimationController.h"
 
-#include "Chrome.h"
-#include "ChromeClient.h"
-#include "CustomHeaderFields.h"
-#include "DOMWindow.h"
-#include "Document.h"
-#include "DocumentLoader.h"
-#include "Frame.h"
-#include "FrameView.h"
 #include "InspectorInstrumentation.h"
-#include "Logging.h"
 #include "Page.h"
 #include "RequestAnimationFrameCallback.h"
 #include "Settings.h"
-#include <algorithm>
-#include <wtf/Ref.h>
 #include <wtf/SystemTracing.h>
-#include <wtf/text/StringBuilder.h>
-
-// Allow a little more than 60fps to make sure we can at least hit that frame rate.
-static const Seconds fullSpeedAnimationInterval { 15_ms };
-// Allow a little more than 30fps to make sure we can at least hit that frame rate.
-static const Seconds halfSpeedThrottlingAnimationInterval { 30_ms };
-static const Seconds aggressiveThrottlingAnimationInterval { 10_s };
-
-#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(page() && page()->isAlwaysOnLoggingAllowed(), PerformanceLogging, "%p - ScriptedAnimationController::" fmt, this, ##__VA_ARGS__)
 
 namespace WebCore {
 
 ScriptedAnimationController::ScriptedAnimationController(Document& document)
     : m_document(makeWeakPtr(document))
-    , m_animationTimer(*this, &ScriptedAnimationController::animationTimerFired)
 {
 }
 
@@ -67,6 +47,25 @@ bool ScriptedAnimationController::requestAnimationFrameEnabled() const
     return m_document && m_document->settings().requestAnimationFrameEnabled();
 }
 
+Page* ScriptedAnimationController::page() const
+{
+    return m_document ? m_document->page() : nullptr;
+}
+
+Seconds ScriptedAnimationController::preferredScriptedAnimationInterval() const
+{
+    if (auto* page = this->page())
+        return page->renderingUpdateThrottlingEnabled() ? preferredFrameInterval(m_throttlingReasons) : FullSpeedAnimationInterval;
+    return FullSpeedAnimationInterval;
+}
+
+Seconds ScriptedAnimationController::interval() const
+{
+    if (auto* page = this->page())
+        return std::max(preferredScriptedAnimationInterval(), page->preferredRenderingUpdateInterval());
+    return FullSpeedAnimationInterval;
+}
+
 void ScriptedAnimationController::suspend()
 {
     ++m_suspendCount;
@@ -83,110 +82,51 @@ void ScriptedAnimationController::resume()
         scheduleAnimation();
 }
 
-#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) && !RELEASE_LOG_DISABLED
-
-static const char* throttlingReasonToString(ScriptedAnimationController::ThrottlingReason reason)
-{
-    switch (reason) {
-    case ScriptedAnimationController::ThrottlingReason::VisuallyIdle:
-        return "VisuallyIdle";
-    case ScriptedAnimationController::ThrottlingReason::OutsideViewport:
-        return "OutsideViewport";
-    case ScriptedAnimationController::ThrottlingReason::LowPowerMode:
-        return "LowPowerMode";
-    case ScriptedAnimationController::ThrottlingReason::NonInteractedCrossOriginFrame:
-        return "NonInteractiveCrossOriginFrame";
-    }
-}
-
-static String throttlingReasonsToString(OptionSet<ScriptedAnimationController::ThrottlingReason> reasons)
-{
-    if (reasons.isEmpty())
-        return "[Unthrottled]"_s;
-
-    StringBuilder builder;
-    for (auto reason : reasons) {
-        if (!builder.isEmpty())
-            builder.append('|');
-        builder.append(throttlingReasonToString(reason));
-    }
-    return builder.toString();
-}
-
-#endif
-
-void ScriptedAnimationController::addThrottlingReason(ThrottlingReason reason)
+bool ScriptedAnimationController::isThrottled() const
 {
-#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
-    if (m_throttlingReasons.contains(reason))
-        return;
-
-    m_throttlingReasons.add(reason);
-
-    RELEASE_LOG_IF_ALLOWED("addThrottlingReason(%s) -> %s", throttlingReasonToString(reason), throttlingReasonsToString(m_throttlingReasons).utf8().data());
-
-    if (m_animationTimer.isActive()) {
-        m_animationTimer.stop();
-        scheduleAnimation();
-    }
-#else
-    UNUSED_PARAM(reason);
-#endif
+    if (auto* page = this->page())
+        return page->renderingUpdateThrottlingEnabled() && (page->isRenderingUpdateThrottled() || !m_throttlingReasons.isEmpty());
+    return false;
 }
 
-void ScriptedAnimationController::removeThrottlingReason(ThrottlingReason reason)
+bool ScriptedAnimationController::isThrottledRelativeToPage() const
 {
-#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
-    if (!m_throttlingReasons.contains(reason))
-        return;
-
-    m_throttlingReasons.remove(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
+    if (auto* page = this->page())
+        return page->preferredRenderingUpdateInterval() < preferredScriptedAnimationInterval();
+    return false;
 }
 
-bool ScriptedAnimationController::isThrottled() const
+bool ScriptedAnimationController::shouldRescheduleRequestAnimationFrame(DOMHighResTimeStamp timestamp) const
 {
-#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
-    return !m_throttlingReasons.isEmpty();
-#else
-    return false;
-#endif
+    return isThrottledRelativeToPage() && Seconds(timestamp - m_lastAnimationFrameTimestamp) < preferredScriptedAnimationInterval();
 }
 
 ScriptedAnimationController::CallbackId ScriptedAnimationController::registerCallback(Ref<RequestAnimationFrameCallback>&& callback)
 {
-    ScriptedAnimationController::CallbackId id = ++m_nextCallbackId;
+    CallbackId callbackId = ++m_nextCallbackId;
     callback->m_firedOrCancelled = false;
-    callback->m_id = id;
+    callback->m_id = callbackId;
     m_callbacks.append(WTFMove(callback));
 
     if (m_document)
-        InspectorInstrumentation::didRequestAnimationFrame(*m_document, id);
+        InspectorInstrumentation::didRequestAnimationFrame(*m_document, callbackId);
 
     if (!m_suspendCount)
         scheduleAnimation();
-    return id;
+    return callbackId;
 }
 
-void ScriptedAnimationController::cancelCallback(CallbackId id)
+void ScriptedAnimationController::cancelCallback(CallbackId callbackId)
 {
-    for (size_t i = 0; i < m_callbacks.size(); ++i) {
-        if (m_callbacks[i]->m_id == id) {
-            m_callbacks[i]->m_firedOrCancelled = true;
-            InspectorInstrumentation::didCancelAnimationFrame(*m_document, id);
-            m_callbacks.remove(i);
-            return;
-        }
-    }
+    bool cancelled = m_callbacks.removeFirstMatching([&](auto& callback) {
+        if (callback->m_id != callbackId)
+            return false;
+        callback->m_firedOrCancelled = true;
+        return true;
+    });
+
+    if (cancelled && m_document)
+        InspectorInstrumentation::didCancelAnimationFrame(*m_document, callbackId);
 }
 
 void ScriptedAnimationController::serviceRequestAnimationFrameCallbacks(DOMHighResTimeStamp timestamp)
@@ -194,6 +134,11 @@ void ScriptedAnimationController::serviceRequestAnimationFrameCallbacks(DOMHighR
     if (!m_callbacks.size() || m_suspendCount || !requestAnimationFrameEnabled())
         return;
 
+    if (shouldRescheduleRequestAnimationFrame(timestamp)) {
+        scheduleAnimation();
+        return;
+    }
+    
     TraceScope tracingScope(RAFCallbackStart, RAFCallbackEnd);
 
     // We round this to the nearest microsecond so that we can return a time that matches what is returned by document.timeline.currentTime.
@@ -223,72 +168,19 @@ void ScriptedAnimationController::serviceRequestAnimationFrameCallbacks(DOMHighR
         return callback->m_firedOrCancelled;
     });
 
+    m_lastAnimationFrameTimestamp = timestamp;
+
     if (m_callbacks.size())
         scheduleAnimation();
 }
 
-Seconds ScriptedAnimationController::interval() const
-{
-#if 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;
-
-    if (m_throttlingReasons.contains(ThrottlingReason::NonInteractedCrossOriginFrame))
-        return halfSpeedThrottlingAnimationInterval;
-
-    ASSERT(m_throttlingReasons.isEmpty());
-#endif
-    return fullSpeedAnimationInterval;
-}
-
-Page* ScriptedAnimationController::page() const
-{
-    return m_document ? m_document->page() : nullptr;
-}
-
 void ScriptedAnimationController::scheduleAnimation()
 {
     if (!requestAnimationFrameEnabled())
         return;
 
-#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
-    if (!m_isUsingTimer && !isThrottled()) {
-        if (auto* page = this->page()) {
-            page->renderingUpdateScheduler().scheduleTimedRenderingUpdate();
-            return;
-        }
-
-        m_isUsingTimer = true;
-    }
-#endif
-    if (m_animationTimer.isActive())
-        return;
-
-    Seconds animationInterval = interval();
-    Seconds scheduleDelay = std::max(animationInterval - Seconds(m_document->domWindow()->nowTimestamp() - m_lastAnimationFrameTimestamp), 0_s);
-
-    if (isThrottled()) {
-        // FIXME: not ideal to snapshot time both in now() and nowTimestamp(), the latter of which also has reduced resolution.
-        MonotonicTime now = MonotonicTime::now();
-
-        MonotonicTime fireTime = now + scheduleDelay;
-        Seconds alignmentInterval = 10_ms;
-        // Snap to the nearest alignmentInterval.
-        Seconds alignment = (fireTime + alignmentInterval / 2) % alignmentInterval;
-        MonotonicTime alignedFireTime = fireTime - alignment;
-        scheduleDelay = alignedFireTime - now;
-    }
-
-    m_animationTimer.startOneShot(scheduleDelay);
-}
-
-void ScriptedAnimationController::animationTimerFired()
-{
-    m_lastAnimationFrameTimestamp = m_document->domWindow()->nowTimestamp();
-    serviceRequestAnimationFrameCallbacks(m_lastAnimationFrameTimestamp);
+    if (auto* page = this->page())
+        page->renderingUpdateScheduler().scheduleTimedRenderingUpdate();
 }
 
 }
index 0d21208..09d982b 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2011 Google Inc. All Rights Reserved.
+ * Copyright (C) 2019 Apple Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #pragma once
 
+#include "AnimationFrameRate.h"
 #include "DOMHighResTimeStamp.h"
-#include "Timer.h"
 #include <wtf/OptionSet.h>
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
 #include <wtf/Vector.h>
+#include <wtf/WeakPtr.h>
 
 namespace WebCore {
 
@@ -48,6 +50,7 @@ public:
     ~ScriptedAnimationController();
     void clearDocumentPointer() { m_document = nullptr; }
     bool requestAnimationFrameEnabled() const;
+    WEBCORE_EXPORT Seconds interval() const;
 
     typedef int CallbackId;
 
@@ -57,41 +60,29 @@ public:
 
     void suspend();
     void resume();
-
-    enum class ThrottlingReason {
-        VisuallyIdle                    = 1 << 0,
-        OutsideViewport                 = 1 << 1,
-        LowPowerMode                    = 1 << 2,
-        NonInteractedCrossOriginFrame   = 1 << 3,
-    };
-    void addThrottlingReason(ThrottlingReason);
-    void removeThrottlingReason(ThrottlingReason);
-
+    
+    void addThrottlingReason(ThrottlingReason reason) { m_throttlingReasons.add(reason); }
+    void removeThrottlingReason(ThrottlingReason reason) { m_throttlingReasons.remove(reason); }
     WEBCORE_EXPORT bool isThrottled() const;
-    WEBCORE_EXPORT Seconds interval() const;
 
 private:
     ScriptedAnimationController(Document&);
 
-    void scheduleAnimation();
-    void animationTimerFired();
-
     Page* page() const;
+    Seconds preferredScriptedAnimationInterval() const;
+    bool isThrottledRelativeToPage() const;
+    bool shouldRescheduleRequestAnimationFrame(DOMHighResTimeStamp) const;
+    void scheduleAnimation();
 
-    typedef Vector<RefPtr<RequestAnimationFrameCallback>> CallbackList;
+    using CallbackList = Vector<RefPtr<RequestAnimationFrameCallback>>;
     CallbackList m_callbacks;
+    DOMHighResTimeStamp m_lastAnimationFrameTimestamp { 0 };
 
     WeakPtr<Document> m_document;
     CallbackId m_nextCallbackId { 0 };
     int m_suspendCount { 0 };
 
-    Timer m_animationTimer;
-    double m_lastAnimationFrameTimestamp { 0 };
-
-#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
     OptionSet<ThrottlingReason> m_throttlingReasons;
-    bool m_isUsingTimer { false };
-#endif
 };
 
 } // namespace WebCore
index 5705a60..0a528e6 100644 (file)
@@ -2524,9 +2524,9 @@ void FrameView::updateScriptedAnimationsAndTimersThrottlingState(const IntRect&
 
     if (auto* scriptedAnimationController = document->scriptedAnimationController()) {
         if (shouldThrottle)
-            scriptedAnimationController->addThrottlingReason(ScriptedAnimationController::ThrottlingReason::OutsideViewport);
+            scriptedAnimationController->addThrottlingReason(ThrottlingReason::OutsideViewport);
         else
-            scriptedAnimationController->removeThrottlingReason(ScriptedAnimationController::ThrottlingReason::OutsideViewport);
+            scriptedAnimationController->removeThrottlingReason(ThrottlingReason::OutsideViewport);
     }
 
     document->setTimerThrottlingEnabled(shouldThrottle);
index 0e47045..4f8cf25 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "ActivityStateChangeObserver.h"
 #include "AlternativeTextClient.h"
+#include "AnimationFrameRate.h"
 #include "ApplicationCacheStorage.h"
 #include "AuthenticatorCoordinator.h"
 #include "BackForwardCache.h"
@@ -1371,34 +1372,50 @@ void Page::resumeScriptedAnimations()
     });
 }
 
-enum class ThrottlingReasonOperation { Add, Remove };
-static void updateScriptedAnimationsThrottlingReason(Page& page, ThrottlingReasonOperation operation, ScriptedAnimationController::ThrottlingReason reason)
+bool Page::renderingUpdateThrottlingEnabled() const
 {
-    page.forEachDocument([&] (Document& document) {
-        if (auto* controller = document.scriptedAnimationController()) {
-            if (operation == ThrottlingReasonOperation::Add)
-                controller->addThrottlingReason(reason);
-            else
-                controller->removeThrottlingReason(reason);
-        }
-    });
+    return m_settings->renderingUpdateThrottlingEnabled();
+}
+
+void Page::renderingUpdateThrottlingEnabledChanged()
+{
+    renderingUpdateScheduler().adjustRenderingUpdateFrequency();
+}
+
+bool Page::isRenderingUpdateThrottled() const
+{
+    return renderingUpdateThrottlingEnabled() && !m_throttlingReasons.isEmpty();
+}
+
+Seconds Page::preferredRenderingUpdateInterval() const
+{
+    return renderingUpdateThrottlingEnabled() ? preferredFrameInterval(m_throttlingReasons) : FullSpeedAnimationInterval;
 }
 
 void Page::setIsVisuallyIdleInternal(bool isVisuallyIdle)
 {
-    updateScriptedAnimationsThrottlingReason(*this, isVisuallyIdle ? ThrottlingReasonOperation::Add : ThrottlingReasonOperation::Remove, ScriptedAnimationController::ThrottlingReason::VisuallyIdle);
+    if (isVisuallyIdle == m_throttlingReasons.contains(ThrottlingReason::VisuallyIdle))
+        return;
+
+    m_throttlingReasons = m_throttlingReasons ^ ThrottlingReason::VisuallyIdle;
+
+    if (renderingUpdateThrottlingEnabled())
+        renderingUpdateScheduler().adjustRenderingUpdateFrequency();
 }
 
 void Page::handleLowModePowerChange(bool isLowPowerModeEnabled)
 {
-    updateScriptedAnimationsThrottlingReason(*this, isLowPowerModeEnabled ? ThrottlingReasonOperation::Add : ThrottlingReasonOperation::Remove, ScriptedAnimationController::ThrottlingReason::LowPowerMode);
-    if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
-        forEachDocument([] (Document& document) {
-            if (auto timeline = document.existingTimeline())
-                timeline->updateThrottlingState();
-        });
-    } else
+    if (isLowPowerModeEnabled == m_throttlingReasons.contains(ThrottlingReason::LowPowerMode))
+        return;
+
+    m_throttlingReasons = m_throttlingReasons ^ ThrottlingReason::LowPowerMode;
+
+    if (renderingUpdateThrottlingEnabled())
+        renderingUpdateScheduler().adjustRenderingUpdateFrequency();
+
+    if (!RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled())
         mainFrame().animation().updateThrottlingState();
+
     updateDOMTimerAlignmentInterval();
 }
 
index cea8117..f1ae43f 100644 (file)
@@ -21,6 +21,7 @@
 #pragma once
 
 #include "ActivityState.h"
+#include "AnimationFrameRate.h"
 #include "DisabledAdaptations.h"
 #include "Document.h"
 #include "FindOptions.h"
@@ -697,6 +698,11 @@ public:
     bool isLowPowerModeEnabled() const;
     WEBCORE_EXPORT void setLowPowerModeEnabledOverrideForTesting(Optional<bool>);
 
+    bool renderingUpdateThrottlingEnabled() const;
+    void renderingUpdateThrottlingEnabledChanged();
+    bool isRenderingUpdateThrottled() const;
+    Seconds preferredRenderingUpdateInterval() const;
+
     WEBCORE_EXPORT void applicationWillResignActive();
     WEBCORE_EXPORT void applicationDidEnterBackground();
     WEBCORE_EXPORT void applicationWillEnterForeground();
@@ -992,6 +998,7 @@ private:
     bool m_inUpdateRendering { false };
     bool m_hasResourceLoadClient { false };
     Vector<UserContentURLPattern> m_corsDisablingPatterns;
+    OptionSet<ThrottlingReason> m_throttlingReasons;
 };
 
 inline PageGroup& Page::group()
index 3b881df..a466fa6 100644 (file)
@@ -42,6 +42,27 @@ RenderingUpdateScheduler::RenderingUpdateScheduler(Page& page)
 #endif
 }
 
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) && PLATFORM(IOS_FAMILY)
+void RenderingUpdateScheduler::adjustFramesPerSecond()
+{
+    Seconds interval = m_page.preferredRenderingUpdateInterval();
+    // CADisplayLink.preferredFramesPerSecond is an integer. So a fraction PreferredFramesPerSecond can't be set.
+    if (interval < 1_s)
+        DisplayRefreshMonitorManager::sharedManager().setPreferredFramesPerSecond(*this, preferredFramesPerSecond(interval));
+}
+#endif
+
+void RenderingUpdateScheduler::adjustRenderingUpdateFrequency()
+{
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) && PLATFORM(IOS_FAMILY)
+    adjustFramesPerSecond();
+#endif
+    if (isScheduled()) {
+        clearScheduled();
+        scheduleTimedRenderingUpdate();
+    }
+}
+
 void RenderingUpdateScheduler::scheduleTimedRenderingUpdate()
 {
     if (isScheduled())
@@ -55,12 +76,25 @@ void RenderingUpdateScheduler::scheduleTimedRenderingUpdate()
 
     tracePoint(ScheduleRenderingUpdate);
 
+    Seconds interval = m_page.preferredRenderingUpdateInterval();
+
 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
-    if (!DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this))
+    // CADisplayLink.preferredFramesPerSecond is an integer. Fall back to timer if the PreferredFramesPerSecond is a fraction.
+    if (interval < 1_s) {
+#if PLATFORM(IOS_FAMILY)
+        if (!m_isMonitorCreated) {
+            adjustFramesPerSecond();
+            m_isMonitorCreated = true;
+        }
+#else
+        if (interval == FullSpeedAnimationInterval)
+#endif
+            m_scheduled = DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this);
+    }
 #endif
-        startTimer(Seconds(1.0 / 60));
 
-    m_scheduled = true;
+    if (!isScheduled())
+        startTimer(interval);
 }
 
 bool RenderingUpdateScheduler::isScheduled() const
@@ -74,6 +108,7 @@ void RenderingUpdateScheduler::startTimer(Seconds delay)
     ASSERT(!isScheduled());
     m_refreshTimer = makeUnique<Timer>(*this, &RenderingUpdateScheduler::displayRefreshFired);
     m_refreshTimer->startOneShot(delay);
+    m_scheduled = true;
 }
 
 void RenderingUpdateScheduler::clearScheduled()
index da3d0cb..f85f1bd 100644 (file)
@@ -46,6 +46,8 @@ public:
     }
 
     RenderingUpdateScheduler(Page&);
+    
+    void adjustRenderingUpdateFrequency();
     void scheduleTimedRenderingUpdate();
     void scheduleImmediateRenderingUpdate();
     void scheduleRenderingUpdate();
@@ -56,6 +58,9 @@ public:
 
 private:
 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+#if PLATFORM(IOS_FAMILY)
+    void adjustFramesPerSecond();
+#endif
     RefPtr<DisplayRefreshMonitor> createDisplayRefreshMonitor(PlatformDisplayID) const final;
     void displayRefreshFired() final;
 #else
@@ -67,6 +72,9 @@ private:
     void clearScheduled();
 
     Page& m_page;
+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) && PLATFORM(IOS_FAMILY)
+    bool m_isMonitorCreated;
+#endif
     bool m_scheduled { false };
     std::unique_ptr<Timer> m_refreshTimer;
 };
index 50d2340..1372e16 100644 (file)
@@ -765,6 +765,10 @@ hiddenPageCSSAnimationSuspensionEnabled:
   initial: false
   onChange: hiddenPageCSSAnimationSuspensionEnabledChanged
 
+renderingUpdateThrottlingEnabled:
+  initial: true
+  onChange: renderingUpdateThrottlingEnabledChanged
+
 storageBlockingPolicy:
   type: SecurityOrigin::StorageBlockingPolicy
   initial: SecurityOrigin::AllowAllStorage
index c9899ad..69cb3d1 100644 (file)
@@ -407,6 +407,12 @@ void SettingsBase::hiddenPageCSSAnimationSuspensionEnabledChanged()
         m_page->hiddenPageCSSAnimationSuspensionStateChanged();
 }
 
+void SettingsBase::renderingUpdateThrottlingEnabledChanged()
+{
+    if (m_page)
+        m_page->renderingUpdateThrottlingEnabledChanged();
+}
+
 void SettingsBase::resourceUsageOverlayVisibleChanged()
 {
 #if ENABLE(RESOURCE_USAGE)
index 251b7e6..2556e59 100644 (file)
@@ -194,6 +194,7 @@ protected:
     void scrollingPerformanceLoggingEnabledChanged();
     void hiddenPageDOMTimerThrottlingStateChanged();
     void hiddenPageCSSAnimationSuspensionEnabledChanged();
+    void renderingUpdateThrottlingEnabledChanged();
     void resourceUsageOverlayVisibleChanged();
     void iceCandidateFilteringEnabledChanged();
 #if ENABLE(TEXT_AUTOSIZING)
diff --git a/Source/WebCore/platform/graphics/AnimationFrameRate.h b/Source/WebCore/platform/graphics/AnimationFrameRate.h
new file mode 100644 (file)
index 0000000..6d7ee1b
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/OptionSet.h>
+#include <wtf/Seconds.h>
+
+namespace WebCore {
+
+using FramesPerSecond = unsigned;
+
+enum class ThrottlingReason {
+    VisuallyIdle                    = 1 << 0,
+    OutsideViewport                 = 1 << 1,
+    LowPowerMode                    = 1 << 2,
+    NonInteractedCrossOriginFrame   = 1 << 3,
+};
+
+// Allow a little more than 60fps to make sure we can at least hit that frame rate.
+constexpr const Seconds FullSpeedAnimationInterval { 15_ms };
+// Allow a little more than 30fps to make sure we can at least hit that frame rate.
+constexpr const Seconds HalfSpeedThrottlingAnimationInterval { 30_ms };
+constexpr const Seconds AggressiveThrottlingAnimationInterval { 10_s };
+
+constexpr const FramesPerSecond FullSpeedFramesPerSecond = 60;
+constexpr const FramesPerSecond HalfSpeedThrottlingFramesPerSecond = 30;
+constexpr const FramesPerSecond ZeroFramesPerSecond = 0;
+
+inline Seconds preferredFrameInterval(const OptionSet<ThrottlingReason>& throttlingReasons)
+{
+    if (throttlingReasons.containsAny({ ThrottlingReason::VisuallyIdle, ThrottlingReason::OutsideViewport }))
+        return AggressiveThrottlingAnimationInterval;
+
+    if (throttlingReasons.containsAny({ ThrottlingReason::LowPowerMode, ThrottlingReason::NonInteractedCrossOriginFrame }))
+        return HalfSpeedThrottlingAnimationInterval;
+
+    ASSERT(throttlingReasons.isEmpty());
+    return FullSpeedAnimationInterval;
+}
+
+inline FramesPerSecond preferredFramesPerSecond(Seconds preferredFrameInterval)
+{
+    if (preferredFrameInterval == FullSpeedAnimationInterval)
+        return FullSpeedFramesPerSecond;
+
+    if (preferredFrameInterval == HalfSpeedThrottlingAnimationInterval)
+        return HalfSpeedThrottlingFramesPerSecond;
+
+    ASSERT_NOT_REACHED();
+    return ZeroFramesPerSecond;
+}
+
+}
index d3542b0..3ee7b51 100644 (file)
@@ -27,6 +27,7 @@
 
 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
 
+#include "AnimationFrameRate.h"
 #include "PlatformScreen.h"
 #include <wtf/HashSet.h>
 #include <wtf/Lock.h>
@@ -45,6 +46,8 @@ public:
 
     virtual void displayLinkFired() { }
 
+    virtual void setPreferredFramesPerSecond(FramesPerSecond) { }
+
     // Return true if callback request was scheduled, false if it couldn't be
     // (e.g., hardware refresh is not available)
     virtual bool requestRefreshCallback() = 0;
index ce04729..8fcc176 100644 (file)
@@ -42,8 +42,11 @@ DisplayRefreshMonitorManager& DisplayRefreshMonitorManager::sharedManager()
     return manager.get();
 }
 
-DisplayRefreshMonitor* DisplayRefreshMonitorManager::createMonitorForClient(DisplayRefreshMonitorClient& client)
+DisplayRefreshMonitor* DisplayRefreshMonitorManager::monitorForClient(DisplayRefreshMonitorClient& client)
 {
+    if (!client.hasDisplayID())
+        return nullptr;
+
     PlatformDisplayID clientDisplayID = client.displayID();
     if (auto* existingMonitor = monitorForDisplayID(clientDisplayID)) {
         existingMonitor->addClient(client);
@@ -54,21 +57,13 @@ DisplayRefreshMonitor* DisplayRefreshMonitorManager::createMonitorForClient(Disp
     if (!monitor)
         return nullptr;
 
-    LOG(RequestAnimationFrame, "DisplayRefreshMonitorManager::createMonitorForClient() - created monitor %p", monitor.get());
+    LOG(RequestAnimationFrame, "DisplayRefreshMonitorManager::monitorForClient() - created monitor %p", monitor.get());
     monitor->addClient(client);
     DisplayRefreshMonitor* result = monitor.get();
     m_monitors.append({ WTFMove(monitor) });
     return result;
 }
 
-void DisplayRefreshMonitorManager::registerClient(DisplayRefreshMonitorClient& client)
-{
-    if (!client.hasDisplayID())
-        return;
-
-    createMonitorForClient(client);
-}
-
 void DisplayRefreshMonitorManager::unregisterClient(DisplayRefreshMonitorClient& client)
 {
     if (!client.hasDisplayID())
@@ -85,17 +80,19 @@ void DisplayRefreshMonitorManager::unregisterClient(DisplayRefreshMonitorClient&
     }
 }
 
-bool DisplayRefreshMonitorManager::scheduleAnimation(DisplayRefreshMonitorClient& client)
+void DisplayRefreshMonitorManager::setPreferredFramesPerSecond(DisplayRefreshMonitorClient& client, FramesPerSecond preferredFramesPerSecond)
 {
-    if (!client.hasDisplayID())
-        return false;
-
-    DisplayRefreshMonitor* monitor = createMonitorForClient(client);
-    if (!monitor)
-        return false;
+    if (auto* monitor = monitorForClient(client))
+        monitor->setPreferredFramesPerSecond(preferredFramesPerSecond);
+}
 
-    client.setIsScheduled(true);
-    return monitor->requestRefreshCallback();
+bool DisplayRefreshMonitorManager::scheduleAnimation(DisplayRefreshMonitorClient& client)
+{
+    if (auto* monitor = monitorForClient(client)) {
+        client.setIsScheduled(true);
+        return monitor->requestRefreshCallback();
+    }
+    return false;
 }
 
 void DisplayRefreshMonitorManager::displayDidRefresh(DisplayRefreshMonitor& monitor)
@@ -116,7 +113,6 @@ void DisplayRefreshMonitorManager::windowScreenDidChange(PlatformDisplayID displ
     
     unregisterClient(client);
     client.setDisplayID(displayID);
-    registerClient(client);
     if (client.isScheduled())
         scheduleAnimation(client);
 }
index 4e0d4ea..bd1d86b 100644 (file)
@@ -27,6 +27,7 @@
 
 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
 
+#include "AnimationFrameRate.h"
 #include "DisplayRefreshMonitor.h"
 #include "PlatformScreen.h"
 #include <wtf/NeverDestroyed.h>
@@ -37,28 +38,27 @@ namespace WebCore {
 
 class DisplayRefreshMonitorManager {
     friend class NeverDestroyed<DisplayRefreshMonitorManager>;
+    friend class DisplayRefreshMonitor;
 public:
     WEBCORE_EXPORT static DisplayRefreshMonitorManager& sharedManager();
-    
-    void registerClient(DisplayRefreshMonitorClient&);
+
     void unregisterClient(DisplayRefreshMonitorClient&);
 
+    void setPreferredFramesPerSecond(DisplayRefreshMonitorClient&, FramesPerSecond);
     bool scheduleAnimation(DisplayRefreshMonitorClient&);
     void windowScreenDidChange(PlatformDisplayID, DisplayRefreshMonitorClient&);
 
     WEBCORE_EXPORT void displayWasUpdated(PlatformDisplayID);
-    
+
 private:
-    friend class DisplayRefreshMonitor;
-    void displayDidRefresh(DisplayRefreshMonitor&);
-    
-    DisplayRefreshMonitorManager() { }
+    DisplayRefreshMonitorManager() = default;
     virtual ~DisplayRefreshMonitorManager();
 
+    void displayDidRefresh(DisplayRefreshMonitor&);
+
     size_t findMonitorForDisplayID(PlatformDisplayID) const;
     DisplayRefreshMonitor* monitorForDisplayID(PlatformDisplayID) const;
-
-    DisplayRefreshMonitor* createMonitorForClient(DisplayRefreshMonitorClient&);
+    DisplayRefreshMonitor* monitorForClient(DisplayRefreshMonitorClient&);
 
     struct DisplayRefreshMonitorWrapper {
         DisplayRefreshMonitorWrapper(DisplayRefreshMonitorWrapper&&) = default;
index a911bdd..9bcd23f 100644 (file)
@@ -35,7 +35,6 @@ namespace WebCore {
 GraphicsLayerUpdater::GraphicsLayerUpdater(GraphicsLayerUpdaterClient& client, PlatformDisplayID displayID)
     : m_client(client)
 {
-    DisplayRefreshMonitorManager::sharedManager().registerClient(*this);
     DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this);
     DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this);
 }
index ee94779..18e809d 100644 (file)
@@ -40,6 +40,7 @@ using WebCore::DisplayRefreshMonitorIOS;
 }
 
 - (id)initWithMonitor:(DisplayRefreshMonitorIOS*)monitor;
+- (void)setPreferredFramesPerSecond:(NSInteger)preferredFramesPerSecond;
 - (void)handleDisplayLink:(CADisplayLink *)sender;
 - (void)invalidate;
 
@@ -65,6 +66,11 @@ using WebCore::DisplayRefreshMonitorIOS;
     [super dealloc];
 }
 
+- (void)setPreferredFramesPerSecond:(NSInteger)preferredFramesPerSecond
+{
+    m_displayLink.preferredFramesPerSecond = preferredFramesPerSecond;
+}
+
 - (void)handleDisplayLink:(CADisplayLink *)sender
 {
     UNUSED_PARAM(sender);
index 509abef..57de006 100644 (file)
@@ -48,8 +48,9 @@
         return nil;
 
     _notifier = &notifier;
-    _isLowPowerModeEnabled = [NSProcessInfo processInfo].lowPowerModeEnabled;
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_didReceiveLowPowerModeChange) name:NSProcessInfoPowerStateDidChangeNotification object:nil];
+    // Set the initial state of the low power mode.
+    [self _didReceiveLowPowerModeChange];
     return self;
 }
 
index e709ddc..f77ee39 100644 (file)
@@ -1,3 +1,39 @@
+2020-01-26  Said Abou-Hallawa  <said@apple.com>
+
+        Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
+        https://bugs.webkit.org/show_bug.cgi?id=204713
+
+        Reviewed by Simon Fraser.
+
+        Create an IPC message on the DrawingArea to send a message from the
+        WebProcess to the UIProcess to setPreferredFramesPerSecond of the
+        DisplayRefreshMonitor.
+
+        * Shared/WebPreferences.yaml:
+        * UIProcess/API/C/WKPreferences.cpp:
+        (WKPreferencesSetRenderingUpdateThrottlingEnabled):
+        (WKPreferencesGetRenderingUpdateThrottlingEnabled):
+        * UIProcess/API/C/WKPreferencesRefPrivate.h:
+        Add a WKPreference key for RenderingUpdateThrottlingEnabled.
+
+        * UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.h:
+        * UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.messages.in:
+
+        * UIProcess/RemoteLayerTree/RemoteLayerTreeDrawingAreaProxy.mm:
+        (-[WKOneShotDisplayLinkHandler setPreferredFramesPerSecond:]):
+        (WebKit::RemoteLayerTreeDrawingAreaProxy::setPreferredFramesPerSecond):
+        Set the preferredFramesPerSecond of the CADisplayLink.
+
+        * WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.h:
+        * WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.mm:
+        (WebKit::RemoteLayerTreeDisplayRefreshMonitor::setPreferredFramesPerSecond):
+        Delegate the call to RemoteLayerTreeDrawingArea.
+
+        * WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.h:
+        * WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.mm:
+        (WebKit::RemoteLayerTreeDrawingArea::setPreferredFramesPerSecond):
+        Send the IPC message from the WebProcess to the UIProcess.
+
 2020-01-25  Brady Eidson  <beidson@apple.com>
 
         Make ContentWorlds be identified by an ObjectIdentifier instead of a uint64_t
index c42954d..f2fd636 100644 (file)
@@ -488,6 +488,10 @@ HiddenPageCSSAnimationSuspensionEnabled:
   type: bool
   defaultValue: DEFAULT_HIDDEN_PAGE_CSS_ANIMATION_SUSPENSION_ENABLED
 
+RenderingUpdateThrottlingEnabled:
+  type: bool
+  defaultValue: true
+
 LowPowerVideoAudioBufferSizeEnabled:
   type: bool
   defaultValue: true
index baa23e5..cc708a1 100644 (file)
@@ -1309,6 +1309,16 @@ bool WKPreferencesGetHiddenPageCSSAnimationSuspensionEnabled(WKPreferencesRef pr
     return toImpl(preferencesRef)->hiddenPageCSSAnimationSuspensionEnabled();
 }
 
+void WKPreferencesSetRenderingUpdateThrottlingEnabled(WKPreferencesRef preferencesRef, bool enabled)
+{
+    toImpl(preferencesRef)->setRenderingUpdateThrottlingEnabled(enabled);
+}
+
+bool WKPreferencesGetRenderingUpdateThrottlingEnabled(WKPreferencesRef preferencesRef)
+{
+    return toImpl(preferencesRef)->renderingUpdateThrottlingEnabled();
+}
+
 void WKPreferencesSetIncrementalRenderingSuppressionTimeout(WKPreferencesRef preferencesRef, double timeout)
 {
     toImpl(preferencesRef)->setIncrementalRenderingSuppressionTimeout(timeout);
index afbd2a8..e614e4c 100644 (file)
@@ -302,6 +302,10 @@ WK_EXPORT bool WKPreferencesGetHiddenPageDOMTimerThrottlingAutoIncreases(WKPrefe
 WK_EXPORT void WKPreferencesSetHiddenPageCSSAnimationSuspensionEnabled(WKPreferencesRef preferences, bool enabled);
 WK_EXPORT bool WKPreferencesGetHiddenPageCSSAnimationSuspensionEnabled(WKPreferencesRef preferences);
 
+// Defaults to true.
+WK_EXPORT void WKPreferencesSetRenderingUpdateThrottlingEnabled(WKPreferencesRef preferences, bool enabled);
+WK_EXPORT bool WKPreferencesGetRenderingUpdateThrottlingEnabled(WKPreferencesRef preferences);
+
 // Defaults to false
 WK_EXPORT void WKPreferencesSetSnapshotAllPlugIns(WKPreferencesRef preferencesRef, bool enabled);
 WK_EXPORT bool WKPreferencesGetSnapshotAllPlugIns(WKPreferencesRef preferencesRef);
index 556591e..b70d532 100644 (file)
@@ -28,6 +28,7 @@
 #include "DrawingAreaProxy.h"
 #include "RemoteLayerTreeHost.h"
 #include "TransactionID.h"
+#include <WebCore/AnimationFrameRate.h>
 #include <WebCore/FloatPoint.h>
 #include <WebCore/IntPoint.h>
 #include <WebCore/IntSize.h>
@@ -97,6 +98,7 @@ private:
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
 
     // Message handlers
+    void setPreferredFramesPerSecond(WebCore::FramesPerSecond);
     void willCommitLayerTree(TransactionID);
     void commitLayerTree(const RemoteLayerTreeTransaction&, const RemoteScrollingCoordinatorTransaction&);
     
index d1047a9..ed0328d 100644 (file)
@@ -21,6 +21,7 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 messages -> RemoteLayerTreeDrawingAreaProxy : DrawingAreaProxy NotRefCounted {
+    void SetPreferredFramesPerSecond(unsigned preferredFramesPerSecond)
     void WillCommitLayerTree(WebKit::TransactionID transactionID)
     void CommitLayerTree(WebKit::RemoteLayerTreeTransaction layerTreeTransaction, WebKit::RemoteScrollingCoordinatorTransaction scrollingTreeTransaction)
 }
index 380f124..ff31111 100644 (file)
@@ -49,6 +49,7 @@
 }
 
 - (id)initWithDrawingAreaProxy:(WebKit::RemoteLayerTreeDrawingAreaProxy*)drawingAreaProxy;
+- (void)setPreferredFramesPerSecond:(NSInteger)preferredFramesPerSecond;
 - (void)displayLinkFired:(CADisplayLink *)sender;
 - (void)invalidate;
 - (void)schedule;
     [super dealloc];
 }
 
+- (void)setPreferredFramesPerSecond:(NSInteger)preferredFramesPerSecond
+{
+    _displayLink.preferredFramesPerSecond = preferredFramesPerSecond;
+}
+
 - (void)displayLinkFired:(CADisplayLink *)sender
 {
     ASSERT(isUIThread());
@@ -183,6 +189,15 @@ void RemoteLayerTreeDrawingAreaProxy::sendUpdateGeometry()
     m_isWaitingForDidUpdateGeometry = true;
 }
 
+void RemoteLayerTreeDrawingAreaProxy::setPreferredFramesPerSecond(FramesPerSecond preferredFramesPerSecond)
+{
+#if PLATFORM(IOS_FAMILY)
+    [displayLinkHandler() setPreferredFramesPerSecond:preferredFramesPerSecond];
+#else
+    UNUSED_PARAM(preferredFramesPerSecond);
+#endif
+}
+
 void RemoteLayerTreeDrawingAreaProxy::willCommitLayerTree(TransactionID transactionID)
 {
     m_pendingLayerTreeTransactionID = transactionID;
index d1f7747..b97f1f4 100644 (file)
@@ -28,6 +28,7 @@
 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
 
 #include "RemoteLayerTreeDrawingArea.h"
+#include <WebCore/AnimationFrameRate.h>
 #include <WebCore/DisplayRefreshMonitor.h>
 
 namespace WebKit {
@@ -41,6 +42,7 @@ public:
     
     virtual ~RemoteLayerTreeDisplayRefreshMonitor();
 
+    void setPreferredFramesPerSecond(WebCore::FramesPerSecond) override;
     bool requestRefreshCallback() override;
 
     void didUpdateLayers();
index 35c8141..157cf40 100644 (file)
@@ -43,6 +43,12 @@ RemoteLayerTreeDisplayRefreshMonitor::~RemoteLayerTreeDisplayRefreshMonitor()
         m_drawingArea->willDestroyDisplayRefreshMonitor(this);
 }
 
+void RemoteLayerTreeDisplayRefreshMonitor::setPreferredFramesPerSecond(FramesPerSecond preferredFramesPerSecond)
+{
+    if (m_drawingArea)
+        m_drawingArea->setPreferredFramesPerSecond(preferredFramesPerSecond);
+}
+
 bool RemoteLayerTreeDisplayRefreshMonitor::requestRefreshCallback()
 {
     if (!m_drawingArea || !isActive())
index f111683..c8c9142 100644 (file)
@@ -29,6 +29,7 @@
 #include "DrawingArea.h"
 #include "GraphicsLayerCARemote.h"
 #include "RemoteLayerTreeTransaction.h"
+#include <WebCore/AnimationFrameRate.h>
 #include <WebCore/GraphicsLayerClient.h>
 #include <WebCore/Timer.h>
 #include <atomic>
@@ -73,6 +74,7 @@ private:
 
     RefPtr<WebCore::DisplayRefreshMonitor> createDisplayRefreshMonitor(WebCore::PlatformDisplayID) override;
     void willDestroyDisplayRefreshMonitor(WebCore::DisplayRefreshMonitor*);
+    void setPreferredFramesPerSecond(WebCore::FramesPerSecond);
 
     bool shouldUseTiledBackingForFrameView(const WebCore::FrameView&) const override;
 
index 26a0303..8781829 100644 (file)
@@ -123,6 +123,11 @@ void RemoteLayerTreeDrawingArea::adoptDisplayRefreshMonitorsFromDrawingArea(Draw
     }
 }
 
+void RemoteLayerTreeDrawingArea::setPreferredFramesPerSecond(FramesPerSecond preferredFramesPerSecond)
+{
+    send(Messages::RemoteLayerTreeDrawingAreaProxy::SetPreferredFramesPerSecond(preferredFramesPerSecond));
+}
+
 void RemoteLayerTreeDrawingArea::updateRootLayers()
 {
     Vector<Ref<GraphicsLayer>> children;
index 1663287..9748f1c 100644 (file)
@@ -1,3 +1,21 @@
+2020-01-26  Said Abou-Hallawa  <said@apple.com>
+
+        Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
+        https://bugs.webkit.org/show_bug.cgi?id=204713
+
+        Reviewed by Simon Fraser.
+
+        Add a WKPreference key for RenderingUpdateThrottling.
+
+        * WebView/WebPreferenceKeysPrivate.h:
+        * WebView/WebPreferences.mm:
+        (+[WebPreferences initialize]):
+        (-[WebPreferences renderingUpdateThrottlingEnabled]):
+        (-[WebPreferences setRenderingUpdateThrottlingEnabled:]):
+        * WebView/WebPreferencesPrivate.h:
+        * WebView/WebView.mm:
+        (-[WebView _preferencesChanged:]):
+
 2020-01-25  Antti Koivisto  <antti@apple.com>
 
         [LFC][Integration] Re-enable line layout integration
index eabcc8c..cc9b976 100644 (file)
 #define WebKitPlugInSnapshottingEnabledPreferenceKey @"WebKitPlugInSnapshottingEnabled"
 #define WebKitHiddenPageDOMTimerThrottlingEnabledPreferenceKey @"WebKitHiddenPageDOMTimerThrottlingEnabled"
 #define WebKitHiddenPageCSSAnimationSuspensionEnabledPreferenceKey @"WebKitHiddenPageCSSAnimationSuspensionEnabled"
+#define WebKitRenderingUpdateThrottlingEnabledPreferenceKey @"WebKitRenderingUpdateThrottlingEnabled"
 #define WebKitLowPowerVideoAudioBufferSizeEnabledPreferenceKey @"WebKitLowPowerVideoAudioBufferSizeEnabled"
 #define WebKitUseLegacyTextAlignPositionedElementBehaviorPreferenceKey @"WebKitUseLegacyTextAlignPositionedElementBehavior"
 #define WebKitMediaSourceEnabledPreferenceKey @"WebKitMediaSourceEnabled"
index be2ce42..577400b 100644 (file)
@@ -593,6 +593,7 @@ public:
         [NSNumber numberWithLongLong:ApplicationCacheStorage::noQuota()], WebKitApplicationCacheDefaultOriginQuota,
         @NO, WebKitHiddenPageDOMTimerThrottlingEnabledPreferenceKey,
         @NO, WebKitHiddenPageCSSAnimationSuspensionEnabledPreferenceKey,
+        @YES, WebKitRenderingUpdateThrottlingEnabledPreferenceKey,
         @NO, WebKitLowPowerVideoAudioBufferSizeEnabledPreferenceKey,
         
         @NO, WebKitUseLegacyTextAlignPositionedElementBehaviorPreferenceKey,
@@ -2785,6 +2786,16 @@ static NSString *classIBCreatorID = nil;
     [self _setBoolValue:enabled forKey:WebKitHiddenPageCSSAnimationSuspensionEnabledPreferenceKey];
 }
 
+- (BOOL)renderingUpdateThrottlingEnabled
+{
+    return [self _boolValueForKey:WebKitRenderingUpdateThrottlingEnabledPreferenceKey];
+}
+
+- (void)setRenderingUpdateThrottlingEnabled:(BOOL)enabled
+{
+    [self _setBoolValue:enabled forKey:WebKitRenderingUpdateThrottlingEnabledPreferenceKey];
+}
+
 - (BOOL)lowPowerVideoAudioBufferSizeEnabled
 {
     return [self _boolValueForKey:WebKitLowPowerVideoAudioBufferSizeEnabledPreferenceKey];
index f171f80..8d217f1 100644 (file)
@@ -489,6 +489,9 @@ extern NSString *WebPreferencesCacheModelChangedInternalNotification WEBKIT_DEPR
 - (BOOL)hiddenPageCSSAnimationSuspensionEnabled;
 - (void)setHiddenPageCSSAnimationSuspensionEnabled:(BOOL)flag;
 
+- (BOOL)renderingUpdateThrottlingEnabled;
+- (void)setRenderingUpdateThrottlingEnabled:(BOOL)flag;
+
 - (BOOL)lowPowerVideoAudioBufferSizeEnabled;
 - (void)setLowPowerVideoAudioBufferSizeEnabled:(BOOL)enabled;
 
index 5c296b6..7b7152c 100644 (file)
@@ -3133,8 +3133,8 @@ static bool needsSelfRetainWhileLoadingQuirk()
     RuntimeEnabledFeatures::sharedFeatures().setAdClickAttributionEnabled([preferences adClickAttributionEnabled]);
 
     settings.setHiddenPageDOMTimerThrottlingEnabled([preferences hiddenPageDOMTimerThrottlingEnabled]);
-
     settings.setHiddenPageCSSAnimationSuspensionEnabled([preferences hiddenPageCSSAnimationSuspensionEnabled]);
+    settings.setRenderingUpdateThrottlingEnabled([preferences renderingUpdateThrottlingEnabled]);
 
     WebCore::DeprecatedGlobalSettings::setResourceLoadStatisticsEnabled([preferences resourceLoadStatisticsEnabled]);
 
index 380cae0..e0286d5 100644 (file)
@@ -1,3 +1,22 @@
+2020-01-26  Said Abou-Hallawa  <said@apple.com>
+
+        Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
+        https://bugs.webkit.org/show_bug.cgi?id=204713
+
+        Reviewed by Simon Fraser.
+
+        Add a WKPreference key for RenderingUpdateThrottling.
+
+        * Interfaces/IWebPreferencesPrivate.idl:
+        * WebPreferenceKeysPrivate.h:
+        * WebPreferences.cpp:
+        (WebPreferences::initializeDefaultSettings):
+        (WebPreferences::renderingUpdateThrottlingEnabled):
+        (WebPreferences::setRenderingUpdateThrottlingEnabled):
+        * WebPreferences.h:
+        * WebView.cpp:
+        (WebView::notifyPreferencesChanged):
+
 2020-01-21  Sihui Liu  <sihui_liu@apple.com>
 
         Disable WebSQL everywhere by default except in tests
index c3a95f7..8447f63 100644 (file)
@@ -256,4 +256,6 @@ interface IWebPreferencesPrivate7 : IWebPreferencesPrivate6
     HRESULT setAspectRatioOfImgFromWidthAndHeightEnabled([in] BOOL enabled);
     HRESULT setWebSQLEnabled([in] BOOL enabled);
     HRESULT webSQLEnabled([out, retval] BOOL* enabled);
+    HRESULT setRenderingUpdateThrottlingEnabled([in] BOOL enabled);
+    HRESULT renderingUpdateThrottlingEnabled([out, retval] BOOL* enabled);
 }
index 219873e..9576512 100644 (file)
 #define WebKitAspectRatioOfImgFromWidthAndHeightEnabledPreferenceKey "WebKitAspectRatioOfImgFromWidthAndHeightEnabled"
 
 #define WebKitWebSQLEnabledPreferenceKey "WebKitWebSQLEnabled"
+
+#define WebKitRenderingUpdateThrottlingEnabledPreferenceKey "WebKitRenderingUpdateThrottlingEnabled"
index 291b699..ee5f440 100644 (file)
@@ -351,6 +351,8 @@ void WebPreferences::initializeDefaultSettings()
 
     CFDictionaryAddValue(defaults, CFSTR(WebKitWebSQLEnabledPreferenceKey), kCFBooleanFalse);
 
+    CFDictionaryAddValue(defaults, CFSTR(WebKitRenderingUpdateThrottlingEnabledPreferenceKey), kCFBooleanTrue);
+
     defaultSettings = defaults;
 #endif
 }
@@ -2450,3 +2452,18 @@ HRESULT WebPreferences::setWebSQLEnabled(BOOL enabled)
     setBoolValue(WebKitWebSQLEnabledPreferenceKey, enabled);
     return S_OK;
 }
+
+HRESULT WebPreferences::renderingUpdateThrottlingEnabled(_Out_ BOOL* enabled)
+{
+    if (!enabled)
+        return E_POINTER;
+    *enabled = boolValueForKey(WebKitRenderingUpdateThrottlingEnabledPreferenceKey);
+    return S_OK;
+}
+
+HRESULT WebPreferences::setRenderingUpdateThrottlingEnabled(BOOL enabled)
+{
+    setBoolValue(WebKitRenderingUpdateThrottlingEnabledPreferenceKey, enabled);
+    return S_OK;
+}
+
index 73dd6f2..2a89f6b 100644 (file)
@@ -301,6 +301,8 @@ public:
     virtual HRESULT STDMETHODCALLTYPE setAspectRatioOfImgFromWidthAndHeightEnabled(BOOL);
     virtual HRESULT STDMETHODCALLTYPE webSQLEnabled(_Out_ BOOL*);
     virtual HRESULT STDMETHODCALLTYPE setWebSQLEnabled(BOOL);
+    virtual HRESULT STDMETHODCALLTYPE renderingUpdateThrottlingEnabled(_Out_ BOOL*);
+    virtual HRESULT STDMETHODCALLTYPE setRenderingUpdateThrottlingEnabled(BOOL);
 
     // WebPreferences
 
index 890f692..e48bfa5 100644 (file)
@@ -5604,6 +5604,11 @@ HRESULT WebView::notifyPreferencesChanged(IWebNotification* notification)
         return hr;
     settings.setRequestAnimationFrameEnabled(enabled);
 
+    hr = prefsPrivate->renderingUpdateThrottlingEnabled(&enabled);
+    if (FAILED(hr))
+        return hr;
+    settings.setRenderingUpdateThrottlingEnabled(enabled);
+
     hr = prefsPrivate->mockScrollbarsEnabled(&enabled);
     if (FAILED(hr))
         return hr;
index 7437669..afb779b 100644 (file)
@@ -1,3 +1,21 @@
+2020-01-26  Said Abou-Hallawa  <said@apple.com>
+
+        Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
+        https://bugs.webkit.org/show_bug.cgi?id=204713
+
+        Reviewed by Simon Fraser.
+
+        RenderingUpdateThrottling is enabled by default. Turn it off for DRT and
+        WTR. In some cases, the page may not get visually active while it's
+        waiting for rAF. Throttling tests will have to explicitly turn it on.
+
+        * DumpRenderTree/mac/DumpRenderTree.mm:
+        (resetWebPreferencesToConsistentValues):
+        * DumpRenderTree/win/DumpRenderTree.cpp:
+        (resetWebPreferencesToConsistentValues):
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::resetPreferencesToConsistentValues):
+
 2020-01-25  Mark Lam  <mark.lam@apple.com>
 
         Add some tests for dynamically allocated StaticStringImpls.
index a47fe16..dcc7179 100644 (file)
@@ -994,6 +994,7 @@ static void resetWebPreferencesToConsistentValues()
 
     [preferences setHiddenPageDOMTimerThrottlingEnabled:NO];
     [preferences setHiddenPageCSSAnimationSuspensionEnabled:NO];
+    [preferences setRenderingUpdateThrottlingEnabled:NO];
     [preferences setRemotePlaybackEnabled:YES];
 
     [preferences setMediaDevicesEnabled:YES];
index 5f8e7a8..89c4dd7 100644 (file)
@@ -800,6 +800,7 @@ static void enableExperimentalFeatures(IWebPreferences* preferences)
     prefsPrivate->setWebAnimationsCompositeOperationsEnabled(TRUE);
     prefsPrivate->setServerTimingEnabled(TRUE);
     prefsPrivate->setAspectRatioOfImgFromWidthAndHeightEnabled(TRUE);
+    prefsPrivate->setRenderingUpdateThrottlingEnabled(FALSE);
     // FIXME: WebGL2
     // FIXME: WebRTC
 }
index ecb882f..425ee17 100644 (file)
@@ -907,6 +907,7 @@ void TestController::resetPreferencesToConsistentValues(const TestOptions& optio
 
     WKPreferencesSetHiddenPageDOMTimerThrottlingEnabled(preferences, false);
     WKPreferencesSetHiddenPageCSSAnimationSuspensionEnabled(preferences, false);
+    WKPreferencesSetRenderingUpdateThrottlingEnabled(preferences, false);
 
     WKPreferencesSetAcceleratedDrawingEnabled(preferences, m_shouldUseAcceleratedDrawing || options.useAcceleratedDrawing);
     // FIXME: We should be testing the default.