Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 9 Dec 2019 21:12:17 +0000 (21:12 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 9 Dec 2019 21:12:17 +0000 (21:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=204713

Patch by Said Abou-Hallawa <sabouhallawa@apple.com> on 2019-12-09
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 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::interval const):
(WebCore::ScriptedAnimationController::isThrottled 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::suspendScriptedAnimations):
(WebCore::Page::resumeScriptedAnimations):
Use forEachDocument().

(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:
(WebCore::Page::isRenderingUpdateThrottled const):

* 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:

* 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::displayDidRefresh):
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.

Source/WebKit:

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

* 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.

LayoutTests:

* fast/animation/request-animation-frame-throttling-outside-viewport-expected.txt: Added.
* fast/animation/request-animation-frame-throttling-outside-viewport.html: Added.
* fast/animation/request-animation-frame-throttling-lowPowerMode-expected.txt:
* fast/animation/request-animation-frame-throttling-lowPowerMode.html:
* fast/animation/resources/frame-with-animation-2.html: Added.

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

33 files changed:
LayoutTests/ChangeLog
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]
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/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/WebKit/ChangeLog
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

index 1c9aa30..4de4b0c 100644 (file)
@@ -1,3 +1,16 @@
+2019-12-09  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        Throttling requestAnimationFrame should be controlled by RenderingUpdateScheduler
+        https://bugs.webkit.org/show_bug.cgi?id=204713
+
+        Reviewed by Simon Fraser.
+
+        * fast/animation/request-animation-frame-throttling-outside-viewport-expected.txt: Added.
+        * fast/animation/request-animation-frame-throttling-outside-viewport.html: Added.
+        * fast/animation/request-animation-frame-throttling-lowPowerMode-expected.txt:
+        * fast/animation/request-animation-frame-throttling-lowPowerMode.html:
+        * fast/animation/resources/frame-with-animation-2.html: Added.
+
 2019-12-09  youenn fablet  <youenn@apple.com>
 
         NetworkResourceLoader should consume its sandbox extensions when starting a fetch through service worker
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..7ad4b8f 100644 (file)
@@ -1,36 +1,32 @@
 <!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.");
 
-let rAFHandle;
-let i = 0;
-function doWork()
-{
-    i++;
-    rAFHandle = requestAnimationFrame(doWork);
-}
+        window.jsTestIsAsync = 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>
+        if (window.internals)
+            internals.setLowPowerModeEnabled(true);
+
+        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..4183863
--- /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 farmesPerSecond > 0 is true
+PASS ifarmeFarmesPerSecond == 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..7f55cd8
--- /dev/null
@@ -0,0 +1,45 @@
+<!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;
+
+        var farmesPerSecond = 0;
+        var ifarmeFarmesPerSecond = 0;
+
+        window.onmessage = function(e){
+            if (e.data == 'subFrameRAFMessage') {
+                ++ifarmeFarmesPerSecond;
+            }
+        };
+
+        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) {
+                    ++farmesPerSecond;
+                    window.requestAnimationFrame(doWork);
+                }
+                else {
+                    shouldBeTrue("farmesPerSecond > 0");
+
+                    // The OutsideViewport throttling = 10_s. subFrameRAFMessage
+                    // should not ever be received during the first second.
+                    shouldBeTrue("ifarmeFarmesPerSecond == 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 adab242..df59cd2 100644 (file)
@@ -1,3 +1,122 @@
+2019-12-09  Said Abou-Hallawa  <sabouhallawa@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 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::interval const):
+        (WebCore::ScriptedAnimationController::isThrottled 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::suspendScriptedAnimations):
+        (WebCore::Page::resumeScriptedAnimations):
+        Use forEachDocument().
+
+        (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:
+        (WebCore::Page::isRenderingUpdateThrottled const):
+    
+        * 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:
+
+        * 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::displayDidRefresh):
+        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.
+
 2019-12-09  Per Arne Vollan  <pvollan@apple.com>
 
         Unreviewed, speculative tvOS build fix after r253231.
index ee89ee3..5138b8c 100644 (file)
@@ -1011,6 +1011,7 @@ set(WebCore_PRIVATE_FRAMEWORK_HEADERS
     platform/gamepad/PlatformGamepad.h
 
     platform/graphics/ANGLEWebKitBridge.h
+    platform/graphics/AnimationFrameRate.h
     platform/graphics/AudioTrackPrivate.h
     platform/graphics/BitmapImage.h
     platform/graphics/Color.h
index 1082fb0..f566b71 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>"; };
                                1199FA59208E3C7F002358CC /* DisplayBox.h */,
                                E4FB4B35239BEB10003C336A /* DisplayInlineContent.cpp */,
                                E451C6332394058E00993190 /* DisplayInlineContent.h */,
+                               112FB350239C23C40087054A /* DisplayInlineRect.h */,
                                6FB47E612277425A00C7BCB0 /* DisplayLineBox.h */,
                                6F77868523491AC6004D9636 /* DisplayPainter.cpp */,
                                6F77868723491AD7004D9636 /* DisplayPainter.h */,
                                6FD9CD52227E21C800E53957 /* DisplayRect.h */,
-                               112FB350239C23C40087054A /* DisplayInlineRect.h */,
                                6FCE1A1822618AB3004F0343 /* DisplayRun.h */,
                        );
                        path = displaytree;
                                49E911B20EF86D27009D0CAF /* transforms */,
                                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 */,
                                7EDAAFC919A2CCDC0034DFD1 /* DiskCacheMonitorCocoa.h in Headers */,
                                1199FA5B208E3C7F002358CC /* DisplayBox.h in Headers */,
                                E451C6342394058F00993190 /* DisplayInlineContent.h in Headers */,
+                               112FB352239C23C40087054A /* DisplayInlineRect.h in Headers */,
+                               6FB47E632277425A00C7BCB0 /* DisplayLineBox.h in Headers */,
                                0FE5FBD31C3DD51E0007A2CA /* DisplayList.h in Headers */,
                                0FE5FBD51C3DD51E0007A2CA /* DisplayListItems.h in Headers */,
                                0FE5FBD71C3DD51E0007A2CA /* DisplayListRecorder.h in Headers */,
                                115CFA7E208B8E10001E6991 /* InlineFormattingState.h in Headers */,
                                6FE7CFA22177EEF2005B1573 /* InlineItem.h in Headers */,
                                BCE789161120D6080060ECE5 /* InlineIterator.h in Headers */,
-                               6FB47E632277425A00C7BCB0 /* DisplayLineBox.h in Headers */,
                                6FE198172178397C00446F08 /* InlineLineBreaker.h in Headers */,
                                6F0CD695229ED32700C5994E /* InlineLineBuilder.h in Headers */,
                                6F360E5023999421001512A7 /* InlineSoftLineBreakItem.h in Headers */,
                                BC5EB6680E81CB7100B25965 /* RenderStyleConstants.h in Headers */,
                                436708C112D9CA4B00044234 /* RenderSVGBlock.h in Headers */,
                                436708C312D9CA4B00044234 /* RenderSVGContainer.h in Headers */,
-                               112FB352239C23C40087054A /* DisplayInlineRect.h in Headers */,
                                2B365C841525119E0091D27B /* RenderSVGEllipse.h in Headers */,
                                43C092BC12D9E4EE00A989C3 /* RenderSVGForeignObject.h in Headers */,
                                436708C512D9CA4B00044234 /* RenderSVGGradientStop.h in Headers */,
index 4fe9980..16999a7 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)
@@ -193,16 +190,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 1612095..ef9cfe1 100644 (file)
@@ -6491,11 +6491,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));
@@ -6734,7 +6731,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..c47a24b 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,18 @@ bool ScriptedAnimationController::requestAnimationFrameEnabled() const
     return m_document && m_document->settings().requestAnimationFrameEnabled();
 }
 
+Page* ScriptedAnimationController::page() const
+{
+    return m_document ? m_document->page() : nullptr;
+}
+
+Seconds ScriptedAnimationController::interval() const
+{
+    if (auto* page = this->page())
+        return std::max(preferredFrameInterval(m_throttlingReasons), page->preferredRenderingUpdateInterval());
+    return preferredFrameInterval(m_throttlingReasons);
+}
+
 void ScriptedAnimationController::suspend()
 {
     ++m_suspendCount;
@@ -83,110 +75,42 @@ 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)
-{
-#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
-}
-
-void ScriptedAnimationController::removeThrottlingReason(ThrottlingReason reason)
-{
-#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
-}
-
 bool ScriptedAnimationController::isThrottled() const
 {
-#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
-    return !m_throttlingReasons.isEmpty();
-#else
+    auto* page = this->page();
+    if (!m_throttlingReasons.isEmpty() || (page && page->isRenderingUpdateThrottled())) {
+        ASSERT(interval() > FullSpeedAnimationInterval);
+        return true;
+    }
     return false;
-#endif
 }
 
 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 +118,13 @@ void ScriptedAnimationController::serviceRequestAnimationFrameCallbacks(DOMHighR
     if (!m_callbacks.size() || m_suspendCount || !requestAnimationFrameEnabled())
         return;
 
+    bool isThrottlingRelativeToPage = page() && page()->preferredRenderingUpdateInterval() < preferredFrameInterval(m_throttlingReasons);
+    bool canSkipFrame = Seconds(timestamp - m_lastAnimationFrameTimestamp) < preferredFrameInterval(m_throttlingReasons);
+    if (isThrottlingRelativeToPage && canSkipFrame) {
+        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 +154,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..b7b2107 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,26 @@ 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;
+    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 04012f9..1bf35f4 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 5655dc2..a59cb32 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "ActivityStateChangeObserver.h"
 #include "AlternativeTextClient.h"
+#include "AnimationFrameRate.h"
 #include "ApplicationCacheStorage.h"
 #include "ApplicationStateChangeListener.h"
 #include "AuthenticatorCoordinator.h"
@@ -1369,54 +1370,44 @@ void Page::updateRendering()
 void Page::suspendScriptedAnimations()
 {
     m_scriptedAnimationsSuspended = true;
-    for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) {
-        if (frame->document())
-            frame->document()->suspendScriptedAnimationControllerCallbacks();
-    }
+    forEachDocument([&] (Document& document) {
+        document.suspendScriptedAnimationControllerCallbacks();
+    });
 }
 
 void Page::resumeScriptedAnimations()
 {
     m_scriptedAnimationsSuspended = false;
-    for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) {
-        if (frame->document())
-            frame->document()->resumeScriptedAnimationControllerCallbacks();
-    }
+    forEachDocument([&] (Document& document) {
+        document.resumeScriptedAnimationControllerCallbacks();
+    });
 }
 
-enum class ThrottlingReasonOperation { Add, Remove };
-static void updateScriptedAnimationsThrottlingReason(Page& page, ThrottlingReasonOperation operation, ScriptedAnimationController::ThrottlingReason reason)
+Seconds Page::preferredRenderingUpdateInterval() const
 {
-    for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
-        auto* document = frame->document();
-        if (!document)
-            continue;
-        auto* scriptedAnimationController = document->scriptedAnimationController();
-        if (!scriptedAnimationController)
-            continue;
-
-        if (operation == ThrottlingReasonOperation::Add)
-            scriptedAnimationController->addThrottlingReason(reason);
-        else
-            scriptedAnimationController->removeThrottlingReason(reason);
-    }
+    return preferredFrameInterval(m_throttlingReasons);
 }
 
 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;
+    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;
+    renderingUpdateScheduler().adjustRenderingUpdateFrequency();
+
+    if (!RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled())
         mainFrame().animation().updateThrottlingState();
+
     updateDOMTimerAlignmentInterval();
 }
 
index 5f4ceb0..18a1edc 100644 (file)
@@ -21,6 +21,7 @@
 #pragma once
 
 #include "ActivityState.h"
+#include "AnimationFrameRate.h"
 #include "DisabledAdaptations.h"
 #include "Document.h"
 #include "FindOptions.h"
@@ -273,6 +274,7 @@ public:
     PerformanceMonitor* performanceMonitor() { return m_performanceMonitor.get(); }
 
     RenderingUpdateScheduler& renderingUpdateScheduler();
+    bool isRenderingUpdateThrottled() const { return !m_throttlingReasons.isEmpty(); }
 
     ValidationMessageClient* validationMessageClient() const { return m_validationMessageClient.get(); }
     void updateValidationBubbleStateIfNeeded();
@@ -713,6 +715,8 @@ public:
     bool isLowPowerModeEnabled() const;
     WEBCORE_EXPORT void setLowPowerModeEnabledOverrideForTesting(Optional<bool>);
 
+    Seconds preferredRenderingUpdateInterval() const;
+
     WEBCORE_EXPORT void applicationWillResignActive();
     WEBCORE_EXPORT void applicationDidEnterBackground();
     WEBCORE_EXPORT void applicationWillEnterForeground();
@@ -1008,6 +1012,7 @@ private:
     bool m_mediaPlaybackIsSuspended { false };
     bool m_mediaBufferingIsSuspended { false };
     bool m_inUpdateRendering { false };
+    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 6f06081..4fcb441 100644 (file)
@@ -46,12 +46,17 @@ public:
     }
 
     RenderingUpdateScheduler(Page&);
+
+    void adjustRenderingUpdateFrequency();
     void scheduleTimedRenderingUpdate();
     void scheduleImmediateRenderingUpdate();
     void scheduleRenderingUpdate();
 
 private:
 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
+#if PLATFORM(IOS_FAMILY)
+    void adjustFramesPerSecond();
+#endif
     RefPtr<DisplayRefreshMonitor> createDisplayRefreshMonitor(PlatformDisplayID) const final;
     void windowScreenDidChange(PlatformDisplayID);
     void displayRefreshFired() final;
@@ -64,6 +69,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;
 };
diff --git a/Source/WebCore/platform/graphics/AnimationFrameRate.h b/Source/WebCore/platform/graphics/AnimationFrameRate.h
new file mode 100644 (file)
index 0000000..ed05915
--- /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 d49db69..9cbb723 100644 (file)
@@ -1,3 +1,32 @@
+2019-12-09  Said Abou-Hallawa  <sabouhallawa@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.
+
+        * 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.
+
 2019-12-09  youenn fablet  <youenn@apple.com>
 
         NetworkResourceLoader should consume its sandbox extensions when starting a fetch through service worker
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 1d383d9..0a6f428 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&) override;
 
index e8da19a..0834a04 100644 (file)
@@ -126,6 +126,11 @@ void RemoteLayerTreeDrawingArea::adoptDisplayRefreshMonitorsFromDrawingArea(Draw
     }
 }
 
+void RemoteLayerTreeDrawingArea::setPreferredFramesPerSecond(FramesPerSecond preferredFramesPerSecond)
+{
+    send(Messages::RemoteLayerTreeDrawingAreaProxy::SetPreferredFramesPerSecond(preferredFramesPerSecond));
+}
+
 void RemoteLayerTreeDrawingArea::updateRootLayers()
 {
     Vector<Ref<GraphicsLayer>> children;