Diff
Modified: trunk/LayoutTests/ChangeLog (213472 => 213473)
--- trunk/LayoutTests/ChangeLog 2017-03-06 23:07:27 UTC (rev 213472)
+++ trunk/LayoutTests/ChangeLog 2017-03-06 23:07:50 UTC (rev 213473)
@@ -1,3 +1,16 @@
+2017-03-06 Chris Dumez <[email protected]>
+
+ [iOS] Throttle CSS animations to 30fps in low power mode
+ https://bugs.webkit.org/show_bug.cgi?id=169138
+ <rdar://problem/30837805>
+
+ Reviewed by Said Abou-Hallawa.
+
+ Add layout test coverage.
+
+ * fast/animation/css-animation-throttling-lowPowerMode-expected.txt: Added.
+ * fast/animation/css-animation-throttling-lowPowerMode.html: Added.
+
2017-03-06 Alex Christensen <[email protected]>
Fix URLs relative to file URLs with paths beginning with Windows drive letters
Added: trunk/LayoutTests/fast/animation/css-animation-throttling-lowPowerMode-expected.txt (0 => 213473)
--- trunk/LayoutTests/fast/animation/css-animation-throttling-lowPowerMode-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/animation/css-animation-throttling-lowPowerMode-expected.txt 2017-03-06 23:07:50 UTC (rev 213473)
@@ -0,0 +1,14 @@
+Tests that CSS animations are throttled in low power mode.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS internals.animationsInterval is 0.015
+internals.setLowPowerModeEnabled(true)
+PASS internals.animationsInterval is 0.030
+internals.setLowPowerModeEnabled(false)
+PASS internals.animationsInterval is 0.015
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Test text
Added: trunk/LayoutTests/fast/animation/css-animation-throttling-lowPowerMode.html (0 => 213473)
--- trunk/LayoutTests/fast/animation/css-animation-throttling-lowPowerMode.html (rev 0)
+++ trunk/LayoutTests/fast/animation/css-animation-throttling-lowPowerMode.html 2017-03-06 23:07:50 UTC (rev 213473)
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<style>
+@keyframes my-animation {
+ from {
+ left : 0px;
+ }
+ to {
+ left : 100px;
+ }
+}
+
+.run-animation {
+ position: relative;
+ animation: my-animation 0.5s infinite;
+ animation-direction: alternate;
+}
+</style>
+</head>
+<body>
+<p id="testElement" class="run-animation">Test text</p>
+<script>
+description("Tests that CSS animations are throttled in low power mode.");
+jsTestIsAsync = true;
+
+const element = document.getElementById("testElement");
+element._onanimationstart_ = function() {
+ element._onanimationstart_ = null;
+
+ shouldBe("internals.animationsInterval", "0.015");
+ evalAndLog("internals.setLowPowerModeEnabled(true)");
+ shouldBe("internals.animationsInterval", "0.030");
+ evalAndLog("internals.setLowPowerModeEnabled(false)");
+ shouldBe("internals.animationsInterval", "0.015");
+ finishJSTest();
+};
+</script>
+<script src=""
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (213472 => 213473)
--- trunk/Source/WebCore/ChangeLog 2017-03-06 23:07:27 UTC (rev 213472)
+++ trunk/Source/WebCore/ChangeLog 2017-03-06 23:07:50 UTC (rev 213473)
@@ -1,3 +1,34 @@
+2017-03-06 Chris Dumez <[email protected]>
+
+ [iOS] Throttle CSS animations to 30fps in low power mode
+ https://bugs.webkit.org/show_bug.cgi?id=169138
+ <rdar://problem/30837805>
+
+ Reviewed by Said Abou-Hallawa.
+
+ Throttle software CSS animations to 30fps in low power mode on iOS
+ to save battery.
+
+ Test: fast/animation/css-animation-throttling-lowPowerMode.html
+
+ * page/Page.cpp:
+ (WebCore::Page::handleLowModePowerChange):
+ * page/animation/CSSAnimationController.cpp:
+ (WebCore::CSSAnimationControllerPrivate::CSSAnimationControllerPrivate):
+ (WebCore::CSSAnimationControllerPrivate::updateAnimationTimer):
+ (WebCore::CSSAnimationControllerPrivate::updateThrottlingState):
+ (WebCore::CSSAnimationControllerPrivate::animationInterval):
+ (WebCore::CSSAnimationControllerPrivate::beginAnimationUpdateTime):
+ (WebCore::CSSAnimationControllerPrivate::beginAnimationUpdate):
+ (WebCore::CSSAnimationController::updateThrottlingState):
+ (WebCore::CSSAnimationController::animationInterval):
+ * page/animation/CSSAnimationController.h:
+ * page/animation/CSSAnimationControllerPrivate.h:
+ * testing/Internals.cpp:
+ (WebCore::Internals::animationsInterval):
+ * testing/Internals.h:
+ * testing/Internals.idl:
+
2017-03-03 Matt Rajca <[email protected]>
Media: notify clients when the user never plays a media element that was prevented from auto-playing
Modified: trunk/Source/WebCore/page/Page.cpp (213472 => 213473)
--- trunk/Source/WebCore/page/Page.cpp 2017-03-06 23:07:27 UTC (rev 213472)
+++ trunk/Source/WebCore/page/Page.cpp 2017-03-06 23:07:50 UTC (rev 213473)
@@ -1137,6 +1137,7 @@
void Page::handleLowModePowerChange(bool isLowPowerModeEnabled)
{
updateScriptedAnimationsThrottlingReason(*this, isLowPowerModeEnabled ? ThrottlingReasonOperation::Add : ThrottlingReasonOperation::Remove, ScriptedAnimationController::ThrottlingReason::LowPowerMode);
+ mainFrame().animation().updateThrottlingState();
}
void Page::userStyleSheetLocationChanged()
Modified: trunk/Source/WebCore/page/animation/CSSAnimationController.cpp (213472 => 213473)
--- trunk/Source/WebCore/page/animation/CSSAnimationController.cpp 2017-03-06 23:07:27 UTC (rev 213472)
+++ trunk/Source/WebCore/page/animation/CSSAnimationController.cpp 2017-03-06 23:07:50 UTC (rev 213473)
@@ -49,8 +49,9 @@
namespace WebCore {
// Allow a little more than 60fps to make sure we can at least hit that frame rate.
-static const double cAnimationTimerDelay = 0.015;
-static const double cBeginAnimationUpdateTimeNotSet = -1;
+static const Seconds animationTimerDelay { 0.015 };
+// Allow a little more than 30fps to make sure we can at least hit that frame rate.
+static const Seconds animationTimerThrottledDelay { 0.030 };
class AnimationPrivateUpdateBlock {
public:
@@ -72,10 +73,8 @@
: m_animationTimer(*this, &CSSAnimationControllerPrivate::animationTimerFired)
, m_updateStyleIfNeededDispatcher(*this, &CSSAnimationControllerPrivate::updateStyleIfNeededDispatcherFired)
, m_frame(frame)
- , m_beginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet)
, m_beginAnimationUpdateCount(0)
, m_waitingForAsyncStartNotification(false)
- , m_isSuspended(false)
, m_allowsNewAnimationsWhileSuspended(false)
{
}
@@ -176,8 +175,12 @@
// If we want service immediately, we start a repeating timer to reduce the overhead of starting
if (!timeToNextService) {
- if (!m_animationTimer.isActive() || !m_animationTimer.repeatInterval())
- m_animationTimer.startRepeating(cAnimationTimerDelay);
+ auto* page = m_frame.page();
+ bool shouldThrottle = page && page->isLowPowerModeEnabled();
+ Seconds delay = shouldThrottle ? animationTimerThrottledDelay : animationTimerDelay;
+
+ if (!m_animationTimer.isActive() || m_animationTimer.repeatInterval() != delay.value())
+ m_animationTimer.startRepeating(delay);
return;
}
@@ -284,6 +287,22 @@
return animation.isAnimatingProperty(property, true, runningState);
}
+void CSSAnimationControllerPrivate::updateThrottlingState()
+{
+ updateAnimationTimer();
+
+ for (auto* childFrame = m_frame.tree().firstChild(); childFrame; childFrame = childFrame->tree().nextSibling())
+ childFrame->animation().updateThrottlingState();
+}
+
+Seconds CSSAnimationControllerPrivate::animationInterval() const
+{
+ if (!m_animationTimer.isActive())
+ return Seconds { INFINITY };
+
+ return Seconds { m_animationTimer.repeatInterval() };
+}
+
void CSSAnimationControllerPrivate::suspendAnimations()
{
if (isSuspended())
@@ -400,16 +419,16 @@
double CSSAnimationControllerPrivate::beginAnimationUpdateTime()
{
ASSERT(m_beginAnimationUpdateCount);
- if (m_beginAnimationUpdateTime == cBeginAnimationUpdateTimeNotSet)
+ if (!m_beginAnimationUpdateTime)
m_beginAnimationUpdateTime = monotonicallyIncreasingTime();
- return m_beginAnimationUpdateTime;
+ return m_beginAnimationUpdateTime.value();
}
void CSSAnimationControllerPrivate::beginAnimationUpdate()
{
if (!m_beginAnimationUpdateCount)
- setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet);
+ m_beginAnimationUpdateTime = std::nullopt;
++m_beginAnimationUpdateCount;
}
@@ -725,6 +744,16 @@
m_data->animationFrameCallbackFired();
}
+void CSSAnimationController::updateThrottlingState()
+{
+ m_data->updateThrottlingState();
+}
+
+Seconds CSSAnimationController::animationInterval() const
+{
+ return m_data->animationInterval();
+}
+
bool CSSAnimationController::animationsAreSuspendedForDocument(Document* document)
{
return m_data->animationsAreSuspendedForDocument(document);
Modified: trunk/Source/WebCore/page/animation/CSSAnimationController.h (213472 => 213473)
--- trunk/Source/WebCore/page/animation/CSSAnimationController.h 2017-03-06 23:07:27 UTC (rev 213472)
+++ trunk/Source/WebCore/page/animation/CSSAnimationController.h 2017-03-06 23:07:50 UTC (rev 213473)
@@ -72,6 +72,9 @@
WEBCORE_EXPORT void resumeAnimations();
void serviceAnimations();
+ void updateThrottlingState();
+ WEBCORE_EXPORT Seconds animationInterval() const;
+
WEBCORE_EXPORT void suspendAnimationsForDocument(Document*);
WEBCORE_EXPORT void resumeAnimationsForDocument(Document*);
WEBCORE_EXPORT bool animationsAreSuspendedForDocument(Document*);
Modified: trunk/Source/WebCore/page/animation/CSSAnimationControllerPrivate.h (213472 => 213473)
--- trunk/Source/WebCore/page/animation/CSSAnimationControllerPrivate.h 2017-03-06 23:07:27 UTC (rev 213472)
+++ trunk/Source/WebCore/page/animation/CSSAnimationControllerPrivate.h 2017-03-06 23:07:50 UTC (rev 213473)
@@ -67,6 +67,9 @@
void resumeAnimations();
void animationFrameCallbackFired();
+ void updateThrottlingState();
+ Seconds animationInterval() const;
+
void suspendAnimationsForDocument(Document*);
void resumeAnimationsForDocument(Document*);
bool animationsAreSuspendedForDocument(Document*);
@@ -85,7 +88,6 @@
bool computeExtentOfAnimation(RenderElement&, LayoutRect&) const;
double beginAnimationUpdateTime();
- void setBeginAnimationUpdateTime(double t) { m_beginAnimationUpdateTime = t; }
void beginAnimationUpdate();
void endAnimationUpdate();
@@ -135,7 +137,7 @@
Vector<Ref<Element>> m_elementChangesToDispatch;
HashSet<Document*> m_suspendedDocuments;
- double m_beginAnimationUpdateTime;
+ std::optional<double> m_beginAnimationUpdateTime;
using AnimationsSet = HashSet<RefPtr<AnimationBase>>;
AnimationsSet m_animationsWaitingForStyle;
@@ -144,7 +146,7 @@
int m_beginAnimationUpdateCount;
bool m_waitingForAsyncStartNotification;
- bool m_isSuspended;
+ bool m_isSuspended { false };
// Used to flag whether we should revert to previous buggy
// behavior of allowing new transitions and animations to
Modified: trunk/Source/WebCore/testing/Internals.cpp (213472 => 213473)
--- trunk/Source/WebCore/testing/Internals.cpp 2017-03-06 23:07:27 UTC (rev 213472)
+++ trunk/Source/WebCore/testing/Internals.cpp 2017-03-06 23:07:50 UTC (rev 213473)
@@ -796,6 +796,15 @@
return document->frame()->animation().animationsAreSuspendedForDocument(document);
}
+double Internals::animationsInterval() const
+{
+ Document* document = contextDocument();
+ if (!document || !document->frame())
+ return INFINITY;
+
+ return document->frame()->animation().animationInterval().value();
+}
+
ExceptionOr<void> Internals::suspendAnimations() const
{
Document* document = contextDocument();
Modified: trunk/Source/WebCore/testing/Internals.h (213472 => 213473)
--- trunk/Source/WebCore/testing/Internals.h 2017-03-06 23:07:27 UTC (rev 213472)
+++ trunk/Source/WebCore/testing/Internals.h 2017-03-06 23:07:50 UTC (rev 213473)
@@ -148,6 +148,7 @@
ExceptionOr<void> resumeAnimations() const;
ExceptionOr<bool> pauseAnimationAtTimeOnElement(const String& animationName, double pauseTime, Element&);
ExceptionOr<bool> pauseAnimationAtTimeOnPseudoElement(const String& animationName, double pauseTime, Element&, const String& pseudoId);
+ double animationsInterval() const;
// CSS Transition testing.
ExceptionOr<bool> pauseTransitionAtTimeOnElement(const String& propertyName, double pauseTime, Element&);
Modified: trunk/Source/WebCore/testing/Internals.idl (213472 => 213473)
--- trunk/Source/WebCore/testing/Internals.idl 2017-03-06 23:07:27 UTC (rev 213472)
+++ trunk/Source/WebCore/testing/Internals.idl 2017-03-06 23:07:50 UTC (rev 213473)
@@ -135,6 +135,7 @@
[MayThrowException] boolean animationsAreSuspended();
[MayThrowException] boolean pauseAnimationAtTimeOnElement(DOMString animationName, unrestricted double pauseTime, Element element);
[MayThrowException] boolean pauseAnimationAtTimeOnPseudoElement(DOMString animationName, unrestricted double pauseTime, Element element, DOMString pseudoId);
+ readonly attribute double animationsInterval;
// CSS Transition testing.
[MayThrowException] boolean pauseTransitionAtTimeOnElement(DOMString propertyName, unrestricted double pauseTime, Element element);