https://bugs.webkit.org/show_bug.cgi?id=114915
Reviewed by Sam Weinig.
.:
Export AnimationController::isSuspended().
* Source/autotools/symbols.filter:
Source/WebCore:
When the Document's AnimationController was suspended, we still
started new transitions and animations. Change this so that
animations enter a paused-but-new state, where they are frozen
until the AnimationController resumes. At that time, it is as
if they had just appeared: any delay counts down before
the animation starts.
For transitions, the change in value must still happen, but
it does so immediately. No transitionend event should be fired.
This produces a slightly confusing behaviour, because any
in-progress transitions are suspended, but any new style changes
happen instantly. This might sound contradictory, but in general
suspending the document is a rare (and dangerous) thing to do.
Previously, the Document would call resumeAnimations as it loaded,
effectively starting all the animations. This meant if you suspended
animations before loading a document, it was ignored as soon as the
load finished. Now there is a separate method startAnimationsIfNotSuspended
which checks to see if the document is suspended as it loads.
In order to handle this case, I added a new state to the Animation
machinery: AnimationStatePausedNew. This is an animation that was created
in the suspended state.
Tests: animations/added-while-suspended.html
transitions/started-while-suspended.html
* WebCore.exp.in: Export AnimationController::isSuspended().
* dom/Document.cpp:
(WebCore::Document::implicitClose):
resumeAnimationsForDocument() -> startAnimationsIfNotSuspended()
* page/animation/AnimationBase.cpp:
(WebCore::nameForState): New name for AnimationStatePausedNew.
(WebCore::AnimationBase::updateStateMachine): Handle new state AnimationStatePausedNew. The
most important change is that when go from PausedNew to Running, we jump back into
the New state and continue from there.
(WebCore::AnimationBase::updatePlayState): suspended -> isSuspended
* page/animation/AnimationBase.h: New state: AnimationStatePausedNew
(WebCore::AnimationBase::waitingToStart): Add AnimationStatePausedNew.
(WebCore::AnimationBase::paused): Add AnimationStatePausedNew.
(WebCore::AnimationBase::isNew): Add AnimationStatePausedNew.
* page/animation/AnimationController.cpp:
(WebCore::AnimationControllerPrivate::AnimationControllerPrivate): Initialise m_suspended.
(WebCore::AnimationControllerPrivate::clear): suspended -> isSuspended
(WebCore::AnimationControllerPrivate::updateAnimations): Ditto.
(WebCore::AnimationControllerPrivate::updateAnimationTimerForRenderer): Ditto.
(WebCore::AnimationControllerPrivate::suspendAnimations): Update m_suspended.
(WebCore::AnimationControllerPrivate::resumeAnimations): Ditto.
(WebCore::AnimationControllerPrivate::suspendAnimationsForDocument):
(WebCore::AnimationControllerPrivate::resumeAnimationsForDocument):
(WebCore::AnimationControllerPrivate::startAnimationsIfNotSuspended): New method that will
only resume animations if we were not globally suspended.
(WebCore::AnimationController::isSuspended): New method.
(WebCore::AnimationController::suspendAnimations): Add logging.
(WebCore::AnimationController::resumeAnimations): Add logging.
(WebCore::AnimationController::suspendAnimationsForDocument): Add logging.
(WebCore::AnimationController::resumeAnimationsForDocument): Add logging.
(WebCore::AnimationController::startAnimationsIfNotSuspended): Calls private method.
* page/animation/AnimationController.h:
(AnimationController): Add isSuspended() and animationsForDocumentMayStart().
* page/animation/AnimationControllerPrivate.h:
(WebCore::AnimationControllerPrivate::isSuspended): New method.
(AnimationControllerPrivate): Add m_isSuspended member.
* page/animation/CompositeAnimation.cpp:
(WebCore::CompositeAnimation::CompositeAnimation): Moved from header - initialise m_isSuspended.
(WebCore::CompositeAnimation::updateTransitions): Do not create ImplicitAnimation if suspended.
(WebCore::CompositeAnimation::updateKeyframeAnimations): Move to AnimationStatePausedNew if suspended.
(WebCore::CompositeAnimation::suspendAnimations): m_suspended -> m_isSuspended
(WebCore::CompositeAnimation::resumeAnimations): Ditto.
* page/animation/CompositeAnimation.h:
(WebCore::CompositeAnimation::isSuspended): Renamed from suspended()
* page/animation/KeyframeAnimation.cpp:
(WebCore::KeyframeAnimation::animate): If we're in the AnimationStatePausedNew state, then
we need to go to the first frame (to handle fill mode).
* testing/Internals.cpp:
(WebCore::Internals::animationsAreSuspended): New exposed method to reflect AnimationController.
* testing/Internals.h: Add animationsAreSuspended.
* testing/Internals.idl: Ditto.
Source/WebKit:
Export AnimationController::isSuspended
* WebKit.vcxproj/WebKitExportGenerator/WebKitExports.def.in:
Source/WebKit/mac:
The WebView private API cssAnimationsSuspended did not necessarily
reflect the reality of the Frame's AnimationController value because it
was caching rather than asking directly. While the WebCore part of this
patch ensured loading the Document wouldn't resume all animations, it
is still better to ask directly.
* WebView/WebView.mm:
(-[WebView cssAnimationsSuspended]): Call into AnimationController.
(-[WebView setCSSAnimationsSuspended:]): Ditto.
* WebView/WebViewData.h: Remove cssAnimationsSuspended boolean.
* WebView/WebViewData.mm: Ditto.
(-[WebViewPrivate init]):
Source/WebKit/win:
Export AnimationController::isSuspended
* WebKit.vcproj/WebKitExports.def.in:
LayoutTests:
Two new tests. Add an animation or transition to
the document when the global animation controller is suspended.
In the animation case, nothing should happen until the
animations are resumed. In the transition case, the style
change should happen immediately and not fire any events.
* animations/added-while-suspended-expected.txt: Added.
* animations/added-while-suspended.html: Added.
* animations/suspend-transform-animation.html: Make sure to resume suspended animations
before quitting the test.
* transitions/started-while-suspended-expected.txt: Added.
* transitions/started-while-suspended.html: Added.
* transitions/suspend-transform-transition.html: Make sure to resume suspended animations
before quitting the test.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@149576
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2013-04-30 Dean Jackson <dino@apple.com>
+
+ Animations and Transitions should not start when globally suspended
+ https://bugs.webkit.org/show_bug.cgi?id=114915
+
+ Reviewed by Sam Weinig.
+
+ Export AnimationController::isSuspended().
+
+ * Source/autotools/symbols.filter:
+
2013-05-01 Benjamin Poulain <benjamin@webkit.org>
Remove the remaining wscript
+2013-04-30 Dean Jackson <dino@apple.com>
+
+ Animations and Transitions should not start when globally suspended
+ https://bugs.webkit.org/show_bug.cgi?id=114915
+
+ Reviewed by Sam Weinig.
+
+ Two new tests. Add an animation or transition to
+ the document when the global animation controller is suspended.
+ In the animation case, nothing should happen until the
+ animations are resumed. In the transition case, the style
+ change should happen immediately and not fire any events.
+
+ * animations/added-while-suspended-expected.txt: Added.
+ * animations/added-while-suspended.html: Added.
+ * animations/suspend-transform-animation.html: Make sure to resume suspended animations
+ before quitting the test.
+ * transitions/started-while-suspended-expected.txt: Added.
+ * transitions/started-while-suspended.html: Added.
+ * transitions/suspend-transform-transition.html: Make sure to resume suspended animations
+ before quitting the test.
+
2013-04-30 Robert Hogan <robert@webkit.org>
REGRESSION(r140907): Incorrect baseline on cells after updating vertical-align
--- /dev/null
+This test adds some elements to the document when animations should be paused. It will only have reproducible output when run in the test system
+
+Animations should not be suspended: PASS
+*** Suspending Animations
+Animations should be suspended: PASS
+*** Adding first box with animation
+*** Adding second box with animation and delay
+*** Resuming Animations
+Animation started on element with id: box
+Animation ended on element with id: box
+Animation started on element with id: box-with-delay
+Animation ended on element with id: box-with-delay
+Animations should not be suspended: PASS
+*** Animations finished
+
--- /dev/null
+<title>Test that new animations do not run while we are suspended</title>
+<style>
+#box {
+ position: relative;
+ height: 100px;
+ width: 100px;
+ background-color: blue;
+ -webkit-animation-name: move;
+ -webkit-animation-duration: 0.1s;
+}
+
+#box-with-delay {
+ position: relative;
+ height: 100px;
+ width: 100px;
+ background-color: blue;
+ -webkit-animation-name: move;
+ -webkit-animation-duration: 0.1s;
+ -webkit-animation-delay: 0.3s;
+}
+
+@-webkit-keyframes move {
+ from { left: 0; }
+ to { left: 500px; }
+}
+</style>
+<script>
+
+var animationsYetToEnd = 2;
+
+function suspend()
+{
+ if (window.internals)
+ internals.suspendAnimations(document);
+}
+
+function resume()
+{
+ if (window.internals)
+ internals.resumeAnimations(document);
+}
+
+function animationStarted(event)
+{
+ log("Animation started on element with id: " + event.target.id);
+}
+
+function animationEnded(event)
+{
+ log("Animation ended on element with id: " + event.target.id);
+ animationsYetToEnd--;
+ if (!animationsYetToEnd)
+ endTest();
+}
+
+function addDivWithId(id)
+{
+ var div = document.createElement("div");
+ div.id = id;
+ document.body.appendChild(div);
+}
+
+function addFirstBox()
+{
+ if (window.internals)
+ log("Animations should be suspended: " + (window.internals.animationsAreSuspended(document) ? "PASS" : "FAIL"));
+
+ log("*** Adding first box with animation");
+ addDivWithId("box");
+ setTimeout(addSecondBox, 100);
+}
+
+function addSecondBox()
+{
+ log("*** Adding second box with animation and delay");
+ addDivWithId("box-with-delay");
+ setTimeout(resumeAndContinue, 50);
+}
+
+function resumeAndContinue()
+{
+ log("*** Resuming Animations");
+ resume();
+}
+
+function endTest()
+{
+ if (window.internals)
+ log("Animations should not be suspended: " + (window.internals.animationsAreSuspended(document) ? "FAIL" : "PASS"));
+ log("*** Animations finished");
+ resume(); // Just in case.
+ if (window.testRunner)
+ testRunner.notifyDone();
+}
+
+function startTest()
+{
+ document.addEventListener("webkitAnimationStart", animationStarted, false);
+ document.addEventListener("webkitAnimationEnd", animationEnded, false);
+
+ if (window.internals)
+ log("Animations should not be suspended: " + (window.internals.animationsAreSuspended(document) ? "FAIL" : "PASS"));
+
+ setTimeout(function() {
+ log("*** Suspending Animations");
+ suspend();
+ setTimeout(addFirstBox, 50);
+ }, 50);
+}
+
+function log(message)
+{
+ var results = document.getElementById("results");
+ results.innerHTML = results.innerHTML + message + "<br>";
+}
+
+if (window.testRunner) {
+ testRunner.waitUntilDone();
+ testRunner.dumpAsText();
+}
+
+window.addEventListener("load", startTest, false);
+
+</script>
+<p>This test adds some elements to the document when animations should be paused. It will only have reproducible output when run in the test system</p>
+<div id="results">
+</div>
internals.suspendAnimations(document);
window.setTimeout(function() {
- if (window.testRunner)
+ if (window.testRunner) {
+ internals.resumeAnimations(document);
testRunner.notifyDone();
+ }
}, 250);
}
--- /dev/null
+This test sets the left property on the box below. It will only have reproducible output when run in the test system
+
+*** Starting test.
+Transitions should not be suspended: PASS
+*** Setting left property to 100px. We should see transition events.
+Transition ended on element with id: box
+*** Suspending Animations/Transitions
+Transitions should be suspended: PASS
+*** Setting left property to 200px. We should NOT see transition events.
+*** Resuming Animations/Transitions
+Transitions should not be suspended: PASS
+*** Test finished
+
--- /dev/null
+<title>Test that new transitions do not run while we are suspended</title>
+<style>
+#box {
+ position: relative;
+ left: 0px;
+ height: 100px;
+ width: 100px;
+ background-color: blue;
+ -webkit-transition: left 0.1s;
+}
+</style>
+<script>
+var box;
+
+function suspend()
+{
+ if (window.internals)
+ internals.suspendAnimations(document);
+}
+
+function resume()
+{
+ if (window.internals)
+ internals.resumeAnimations(document);
+}
+
+function transitionEnded(event)
+{
+ log("Transition ended on element with id: " + event.target.id);
+}
+
+function suspendAndContinue()
+{
+ log("*** Suspending Animations/Transitions");
+ suspend();
+ setTimeout(function() {
+ if (window.internals)
+ log("Transitions should be suspended: " + (window.internals.animationsAreSuspended(document) ? "PASS" : "FAIL"));
+ log("*** Setting left property to 200px. We should NOT see transition events.")
+ box.style.left = "200px";
+ setTimeout(function() {
+ endTest();
+ }, 200);
+ }, 100);
+}
+
+function resumeAndContinue()
+{
+}
+
+function endTest()
+{
+ log("*** Resuming Animations/Transitions");
+ resume();
+ if (window.internals)
+ log("Transitions should not be suspended: " + (window.internals.animationsAreSuspended(document) ? "FAIL" : "PASS"));
+
+ resume(); // Just in case.
+ log("*** Test finished");
+ if (window.testRunner)
+ testRunner.notifyDone();
+}
+
+function startTest()
+{
+ log("*** Starting test.")
+
+ box = document.getElementById("box");
+ document.addEventListener("webkitTransitionEnd", transitionEnded, false);
+
+ if (window.internals)
+ log("Transitions should not be suspended: " + (window.internals.animationsAreSuspended(document) ? "FAIL" : "PASS"));
+
+ setTimeout(function() {
+ log("*** Setting left property to 100px. We should see transition events.")
+ box.style.left = "100px";
+ setTimeout(function() {
+ suspendAndContinue();
+ }, 200);
+ }, 50);
+}
+
+function log(message)
+{
+ var results = document.getElementById("results");
+ results.innerHTML = results.innerHTML + message + "<br>";
+}
+
+if (window.testRunner) {
+ testRunner.waitUntilDone();
+ testRunner.dumpAsText();
+}
+
+window.addEventListener("load", startTest, false);
+
+</script>
+<p>This test sets the left property on the box below. It will only have reproducible output when run in the test system</p>
+<div id="box"></div>
+<div id="results">
+</div>
internals.suspendAnimations(document);
window.setTimeout(function() {
- if (window.testRunner)
+ if (window.testRunner) {
+ internals.resumeAnimations(document);
testRunner.notifyDone();
+ }
}, 250);
}
+2013-04-30 Dean Jackson <dino@apple.com>
+
+ Animations and Transitions should not start when globally suspended
+ https://bugs.webkit.org/show_bug.cgi?id=114915
+
+ Reviewed by Sam Weinig.
+
+ When the Document's AnimationController was suspended, we still
+ started new transitions and animations. Change this so that
+ animations enter a paused-but-new state, where they are frozen
+ until the AnimationController resumes. At that time, it is as
+ if they had just appeared: any delay counts down before
+ the animation starts.
+
+ For transitions, the change in value must still happen, but
+ it does so immediately. No transitionend event should be fired.
+ This produces a slightly confusing behaviour, because any
+ in-progress transitions are suspended, but any new style changes
+ happen instantly. This might sound contradictory, but in general
+ suspending the document is a rare (and dangerous) thing to do.
+
+ Previously, the Document would call resumeAnimations as it loaded,
+ effectively starting all the animations. This meant if you suspended
+ animations before loading a document, it was ignored as soon as the
+ load finished. Now there is a separate method startAnimationsIfNotSuspended
+ which checks to see if the document is suspended as it loads.
+
+ In order to handle this case, I added a new state to the Animation
+ machinery: AnimationStatePausedNew. This is an animation that was created
+ in the suspended state.
+
+ Tests: animations/added-while-suspended.html
+ transitions/started-while-suspended.html
+
+ * WebCore.exp.in: Export AnimationController::isSuspended().
+
+ * dom/Document.cpp:
+ (WebCore::Document::implicitClose):
+ resumeAnimationsForDocument() -> startAnimationsIfNotSuspended()
+
+ * page/animation/AnimationBase.cpp:
+ (WebCore::nameForState): New name for AnimationStatePausedNew.
+ (WebCore::AnimationBase::updateStateMachine): Handle new state AnimationStatePausedNew. The
+ most important change is that when go from PausedNew to Running, we jump back into
+ the New state and continue from there.
+ (WebCore::AnimationBase::updatePlayState): suspended -> isSuspended
+ * page/animation/AnimationBase.h: New state: AnimationStatePausedNew
+ (WebCore::AnimationBase::waitingToStart): Add AnimationStatePausedNew.
+ (WebCore::AnimationBase::paused): Add AnimationStatePausedNew.
+ (WebCore::AnimationBase::isNew): Add AnimationStatePausedNew.
+
+ * page/animation/AnimationController.cpp:
+ (WebCore::AnimationControllerPrivate::AnimationControllerPrivate): Initialise m_suspended.
+ (WebCore::AnimationControllerPrivate::clear): suspended -> isSuspended
+ (WebCore::AnimationControllerPrivate::updateAnimations): Ditto.
+ (WebCore::AnimationControllerPrivate::updateAnimationTimerForRenderer): Ditto.
+ (WebCore::AnimationControllerPrivate::suspendAnimations): Update m_suspended.
+ (WebCore::AnimationControllerPrivate::resumeAnimations): Ditto.
+ (WebCore::AnimationControllerPrivate::suspendAnimationsForDocument):
+ (WebCore::AnimationControllerPrivate::resumeAnimationsForDocument):
+ (WebCore::AnimationControllerPrivate::startAnimationsIfNotSuspended): New method that will
+ only resume animations if we were not globally suspended.
+ (WebCore::AnimationController::isSuspended): New method.
+ (WebCore::AnimationController::suspendAnimations): Add logging.
+ (WebCore::AnimationController::resumeAnimations): Add logging.
+ (WebCore::AnimationController::suspendAnimationsForDocument): Add logging.
+ (WebCore::AnimationController::resumeAnimationsForDocument): Add logging.
+ (WebCore::AnimationController::startAnimationsIfNotSuspended): Calls private method.
+ * page/animation/AnimationController.h:
+ (AnimationController): Add isSuspended() and animationsForDocumentMayStart().
+ * page/animation/AnimationControllerPrivate.h:
+ (WebCore::AnimationControllerPrivate::isSuspended): New method.
+ (AnimationControllerPrivate): Add m_isSuspended member.
+
+ * page/animation/CompositeAnimation.cpp:
+ (WebCore::CompositeAnimation::CompositeAnimation): Moved from header - initialise m_isSuspended.
+ (WebCore::CompositeAnimation::updateTransitions): Do not create ImplicitAnimation if suspended.
+ (WebCore::CompositeAnimation::updateKeyframeAnimations): Move to AnimationStatePausedNew if suspended.
+ (WebCore::CompositeAnimation::suspendAnimations): m_suspended -> m_isSuspended
+ (WebCore::CompositeAnimation::resumeAnimations): Ditto.
+ * page/animation/CompositeAnimation.h:
+ (WebCore::CompositeAnimation::isSuspended): Renamed from suspended()
+
+ * page/animation/KeyframeAnimation.cpp:
+ (WebCore::KeyframeAnimation::animate): If we're in the AnimationStatePausedNew state, then
+ we need to go to the first frame (to handle fill mode).
+
+ * testing/Internals.cpp:
+ (WebCore::Internals::animationsAreSuspended): New exposed method to reflect AnimationController.
+ * testing/Internals.h: Add animationsAreSuspended.
+ * testing/Internals.idl: Ditto.
+
2013-05-04 Sam Weinig <sam@webkit.org>
Move PopupMenuMac and SearchPopupMenuMac to Source/WebKit/mac
__ZN7WebCore18makeAllDirectoriesERKN3WTF6StringE
__ZN7WebCore18pluginScriptObjectEPN3JSC9ExecStateEPNS_13JSHTMLElementE
__ZN7WebCore18proxyServersForURLERKNS_4KURLEPKNS_17NetworkingContextE
+__ZNK7WebCore19AnimationController11isSuspendedEv
__ZN7WebCore19AnimationController16resumeAnimationsEv
__ZN7WebCore19AnimationController17suspendAnimationsEv
__ZN7WebCore19AnimationController20pauseAnimationAtTimeEPNS_12RenderObjectERKN3WTF12AtomicStringEd
Frame* f = frame();
if (f) {
f->loader()->icon()->startLoader();
- f->animation()->resumeAnimationsForDocument(this);
+ f->animation()->startAnimationsIfNotSuspended(this);
}
ImageLoader::dispatchPendingBeforeLoadEvents();
case AnimationBase::AnimationStateStartWaitResponse: return "StartWaitResponse";
case AnimationBase::AnimationStateLooping: return "Looping";
case AnimationBase::AnimationStateEnding: return "Ending";
+ case AnimationBase::AnimationStatePausedNew: return "PausedNew";
case AnimationBase::AnimationStatePausedWaitTimer: return "PausedWaitTimer";
case AnimationBase::AnimationStatePausedWaitStyleAvailable: return "PausedWaitStyleAvailable";
case AnimationBase::AnimationStatePausedWaitResponse: return "PausedWaitResponse";
m_requestedStartTime = beginAnimationUpdateTime();
LOG(Animations, "%p AnimationState %s -> StartWaitTimer", this, nameForState(m_animState));
m_animState = AnimationStateStartWaitTimer;
+ } else {
+ // We are pausing before we even started.
+ LOG(Animations, "%p AnimationState %s -> AnimationStatePausedNew", this, nameForState(m_animState));
+ m_animState = AnimationStatePausedNew;
}
break;
case AnimationStateStartWaitTimer:
m_animState = AnimationStateNew;
updateStateMachine(AnimationStateInputStartAnimation, 0);
break;
+ case AnimationStatePausedNew:
case AnimationStatePausedWaitResponse:
case AnimationStatePausedWaitStyleAvailable:
case AnimationStatePausedRun:
// AnimationStatePausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation.
// When the AnimationStateInputStartTimeSet comes in and we were in AnimationStatePausedRun, we will notice
// that we have already set the startTime and will ignore it.
- ASSERT(input == AnimationStateInputPlayStateRunning || input == AnimationStateInputStartTimeSet || input == AnimationStateInputStyleAvailable);
+ ASSERT(input == AnimationStateInputPlayStateRunning || input == AnimationStateInputStartTimeSet || input == AnimationStateInputStyleAvailable || input == AnimationStateInputStartAnimation);
ASSERT(paused());
-
+
if (input == AnimationStateInputPlayStateRunning) {
+ if (m_animState == AnimationStatePausedNew) {
+ // We were paused before we even started, and now we're supposed
+ // to start, so jump back to the New state and reset.
+ LOG(Animations, "%p AnimationState %s -> AnimationStateNew", this, nameForState(m_animState));
+ m_animState = AnimationStateNew;
+ updateStateMachine(input, param);
+ break;
+ }
+
// Update the times
if (m_animState == AnimationStatePausedRun)
m_startTime += beginAnimationUpdateTime() - m_pauseTime;
m_pauseTime += m_startTime;
break;
}
-
+
ASSERT(m_animState == AnimationStatePausedWaitStyleAvailable);
// We are paused but we got the callback that notifies us that style has been updated.
// We move to the AnimationStatePausedWaitResponse state
// When we get here, we can have one of 4 desired states: running, paused, suspended, paused & suspended.
// The state machine can be in one of two states: running, paused.
// Set the state machine to the desired state.
- bool pause = playState == AnimPlayStatePaused || m_compAnim->suspended();
-
+ bool pause = playState == AnimPlayStatePaused || m_compAnim->isSuspended();
+
if (pause == paused() && !isNew())
return;
-
+
updateStateMachine(pause ? AnimationStateInputPlayStatePaused : AnimationStateInputPlayStateRunning, -1);
}
AnimationStateStartWaitResponse, // animation started, waiting for response
AnimationStateLooping, // response received, animation running, loop timer running, waiting for fire
AnimationStateEnding, // received, animation running, end timer running, waiting for fire
+ AnimationStatePausedNew, // in pause mode when animation was created
AnimationStatePausedWaitTimer, // in pause mode when animation started
AnimationStatePausedWaitStyleAvailable, // in pause mode when waiting for style setup
AnimationStatePausedWaitResponse, // animation paused when in STARTING state
void updatePlayState(EAnimPlayState);
bool playStatePlaying() const;
- bool waitingToStart() const { return m_animState == AnimationStateNew || m_animState == AnimationStateStartWaitTimer; }
+ bool waitingToStart() const { return m_animState == AnimationStateNew || m_animState == AnimationStateStartWaitTimer || m_animState == AnimationStatePausedNew; }
bool preActive() const
{
return m_animState == AnimationStateNew || m_animState == AnimationStateStartWaitTimer || m_animState == AnimationStateStartWaitStyleAvailable || m_animState == AnimationStateStartWaitResponse;
bool postActive() const { return m_animState == AnimationStateDone; }
bool active() const { return !postActive() && !preActive(); }
bool running() const { return !isNew() && !postActive(); }
- bool paused() const { return m_pauseTime >= 0; }
- bool isNew() const { return m_animState == AnimationStateNew; }
+ bool paused() const { return m_pauseTime >= 0 || m_animState == AnimationStatePausedNew; }
+ bool isNew() const { return m_animState == AnimationStateNew || m_animState == AnimationStatePausedNew; }
bool waitingForStartTime() const { return m_animState == AnimationStateStartWaitResponse; }
bool waitingForStyleAvailable() const { return m_animState == AnimationStateStartWaitStyleAvailable; }
, m_animationsWaitingForStyle()
, m_animationsWaitingForStartTimeResponse()
, m_waitingForAsyncStartNotification(false)
+ , m_isSuspended(false)
{
}
if (!animation)
return false;
animation->clearRenderer();
- return animation->suspended();
+ return animation->isSuspended();
}
double AnimationControllerPrivate::updateAnimations(SetChanged callSetChanged/* = DoNotCallSetChanged*/)
RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
CompositeAnimation* compAnim = it->value.get();
- if (!compAnim->suspended() && compAnim->hasAnimations()) {
+ if (!compAnim->isSuspended() && compAnim->hasAnimations()) {
double t = compAnim->timeToNextService();
if (t != -1 && (t < timeToNextService || timeToNextService == -1))
timeToNextService = t;
double timeToNextService = 0;
RefPtr<CompositeAnimation> compAnim = m_compositeAnimations.get(renderer);
- if (!compAnim->suspended() && compAnim->hasAnimations())
+ if (!compAnim->isSuspended() && compAnim->hasAnimations())
timeToNextService = compAnim->timeToNextService();
if (m_animationTimer.isActive() && (m_animationTimer.repeatInterval() || m_animationTimer.nextFireInterval() <= timeToNextService))
void AnimationControllerPrivate::suspendAnimations()
{
+ if (isSuspended())
+ return;
+
suspendAnimationsForDocument(m_frame->document());
-
+
// Traverse subframes
for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
child->animation()->suspendAnimations();
+
+ m_isSuspended = true;
}
void AnimationControllerPrivate::resumeAnimations()
{
+ if (!isSuspended())
+ return;
+
resumeAnimationsForDocument(m_frame->document());
-
+
// Traverse subframes
for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
child->animation()->resumeAnimations();
+
+ m_isSuspended = false;
}
void AnimationControllerPrivate::suspendAnimationsForDocument(Document* document)
{
+ if (isSuspended())
+ return;
+
setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet);
-
+
RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
RenderObject* renderer = it->key;
compAnim->suspendAnimations();
}
}
-
+
updateAnimationTimer();
}
void AnimationControllerPrivate::resumeAnimationsForDocument(Document* document)
{
+ if (!isSuspended())
+ return;
+
setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet);
-
+
RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end();
for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) {
RenderObject* renderer = it->key;
compAnim->resumeAnimations();
}
}
-
+
updateAnimationTimer();
}
+void AnimationControllerPrivate::startAnimationsIfNotSuspended(Document* document)
+{
+ if (!isSuspended())
+ resumeAnimationsForDocument(document);
+}
+
bool AnimationControllerPrivate::pauseAnimationAtTime(RenderObject* renderer, const AtomicString& name, double t)
{
if (!renderer)
return m_data->isRunningAcceleratedAnimationOnRenderer(renderer, property, isRunningNow);
}
+bool AnimationController::isSuspended() const
+{
+ return m_data->isSuspended();
+}
+
void AnimationController::suspendAnimations()
{
+ LOG(Animations, "controller is suspending animations");
m_data->suspendAnimations();
}
void AnimationController::resumeAnimations()
{
+ LOG(Animations, "controller is resuming animations");
m_data->resumeAnimations();
}
void AnimationController::suspendAnimationsForDocument(Document* document)
{
+ LOG(Animations, "suspending animations for document %p", document);
m_data->suspendAnimationsForDocument(document);
}
void AnimationController::resumeAnimationsForDocument(Document* document)
{
+ LOG(Animations, "resuming animations for document %p", document);
m_data->resumeAnimationsForDocument(document);
}
+void AnimationController::startAnimationsIfNotSuspended(Document* document)
+{
+ LOG(Animations, "animations may start for document %p", document);
+ m_data->startAnimationsIfNotSuspended(document);
+}
+
void AnimationController::beginAnimationUpdate()
{
if (!m_beginAnimationUpdateCount)
bool isRunningAnimationOnRenderer(RenderObject*, CSSPropertyID, bool isRunningNow = true) const;
bool isRunningAcceleratedAnimationOnRenderer(RenderObject*, CSSPropertyID, bool isRunningNow = true) const;
+ bool isSuspended() const;
void suspendAnimations();
void resumeAnimations();
#if ENABLE(REQUEST_ANIMATION_FRAME)
void suspendAnimationsForDocument(Document*);
void resumeAnimationsForDocument(Document*);
+ void startAnimationsIfNotSuspended(Document*);
void beginAnimationUpdate();
void endAnimationUpdate();
bool hasAnimations() const { return !m_compositeAnimations.isEmpty(); }
+ bool isSuspended() const { return m_isSuspended; }
void suspendAnimations();
void resumeAnimations();
#if ENABLE(REQUEST_ANIMATION_FRAME)
void suspendAnimationsForDocument(Document*);
void resumeAnimationsForDocument(Document*);
+ void startAnimationsIfNotSuspended(Document*);
bool isRunningAnimationOnRenderer(RenderObject*, CSSPropertyID, bool isRunningNow) const;
bool isRunningAcceleratedAnimationOnRenderer(RenderObject*, CSSPropertyID, bool isRunningNow) const;
WaitingAnimationsSet m_animationsWaitingForStyle;
WaitingAnimationsSet m_animationsWaitingForStartTimeResponse;
bool m_waitingForAsyncStartNotification;
+ bool m_isSuspended;
};
} // namespace WebCore
namespace WebCore {
+CompositeAnimation::CompositeAnimation(AnimationControllerPrivate* animationController)
+ : m_animationController(animationController)
+{
+ m_suspended = animationController->isSuspended();
+}
+
CompositeAnimation::~CompositeAnimation()
{
// Toss the refs to all animations, but make sure we remove them from
if (targetStyle->transitions()) {
for (size_t i = 0; i < targetStyle->transitions()->size(); ++i) {
const Animation* anim = targetStyle->transitions()->animation(i);
- bool isActiveTransition = anim->duration() || anim->delay() > 0;
+ bool isActiveTransition = !m_suspended && (anim->duration() || anim->delay() > 0);
Animation::AnimationMode mode = anim->animationMode();
if (mode == Animation::AnimateNone)
keyframeAnim->setIndex(i);
} else if ((anim->duration() || anim->delay()) && anim->iterationCount() && animationName != none) {
keyframeAnim = KeyframeAnimation::create(const_cast<Animation*>(anim), renderer, i, this, targetStyle);
- LOG(Animations, "Creating KeyframeAnimation %p with keyframes %s duration %.2f delay %.2f, iterations %.2f", keyframeAnim.get(), anim->name().utf8().data(), anim->duration(), anim->delay(), anim->iterationCount());
+ LOG(Animations, "Creating KeyframeAnimation %p with keyframes %s, duration %.2f, delay %.2f, iterations %.2f", keyframeAnim.get(), anim->name().utf8().data(), anim->duration(), anim->delay(), anim->iterationCount());
+ if (m_suspended) {
+ keyframeAnim->updatePlayState(AnimPlayStatePaused);
+ LOG(Animations, " (created in suspended/paused state)");
+ }
#if !LOG_DISABLED
HashSet<CSSPropertyID>::const_iterator endProperties = keyframeAnim->keyframes().endProperties();
for (HashSet<CSSPropertyID>::const_iterator it = keyframeAnim->keyframes().beginProperties(); it != endProperties; ++it)
void suspendAnimations();
void resumeAnimations();
- bool suspended() const { return m_suspended; }
+ bool isSuspended() const { return m_suspended; }
bool hasAnimations() const { return !m_transitions.isEmpty() || !m_keyframeAnimations.isEmpty(); }
unsigned numberOfActiveAnimations() const;
private:
- CompositeAnimation(AnimationControllerPrivate* animationController)
- : m_animationController(animationController)
- , m_suspended(false)
- {
- }
+ CompositeAnimation(AnimationControllerPrivate*);
void updateTransitions(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle);
void updateKeyframeAnimations(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle);
prog = progress(scale, offset, timingFunction);
}
-void KeyframeAnimation::animate(CompositeAnimation*, RenderObject*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle)
+void KeyframeAnimation::animate(CompositeAnimation* compositeAnimation, RenderObject*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle)
{
// Fire the start timeout if needed
fireAnimationEventsIfNeeded();
// If we have not yet started, we will not have a valid start time, so just start the animation if needed.
- if (isNew() && m_animation->playState() == AnimPlayStatePlaying)
+ if (isNew() && m_animation->playState() == AnimPlayStatePlaying && !compositeAnimation->isSuspended())
updateStateMachine(AnimationStateInputStartAnimation, -1);
// If we get this far and the animation is done, it means we are cleaning up a just finished animation.
return 0;
}
+bool Internals::animationsAreSuspended(Document* document, ExceptionCode& ec) const
+{
+ if (!document || !document->frame()) {
+ ec = INVALID_ACCESS_ERR;
+ return false;
+ }
+
+ AnimationController* controller = document->frame()->animation();
+ if (!controller)
+ return false;
+
+ return controller->isSuspended();
+}
+
void Internals::suspendAnimations(Document* document, ExceptionCode& ec) const
{
if (!document || !document->frame()) {
// CSS Animation testing.
unsigned numberOfActiveAnimations() const;
+ bool animationsAreSuspended(Document*, ExceptionCode&) const;
void suspendAnimations(Document*, ExceptionCode&) const;
void resumeAnimations(Document*, ExceptionCode&) const;
bool pauseAnimationAtTimeOnElement(const String& animationName, double pauseTime, Element*, ExceptionCode&);
unsigned long numberOfActiveAnimations();
void suspendAnimations(Document document) raises (DOMException);
void resumeAnimations(Document document) raises (DOMException);
+ boolean animationsAreSuspended(in Document document) raises (DOMException);
boolean pauseAnimationAtTimeOnElement(DOMString animationName, double pauseTime, Element element) raises (DOMException);
boolean pauseAnimationAtTimeOnPseudoElement(DOMString animationName, double pauseTime, Element element, DOMString pseudoId) raises (DOMException);
+2013-05-04 Dean Jackson <dino@apple.com>
+
+ Animations and Transitions should not start when globally suspended
+ https://bugs.webkit.org/show_bug.cgi?id=114915
+
+ Reviewed by Sam Weinig.
+
+ Export AnimationController::isSuspended
+
+ * WebKit.vcxproj/WebKitExportGenerator/WebKitExports.def.in:
+
2013-05-04 Sam Weinig <sam@webkit.org>
Move PopupMenuMac and SearchPopupMenuMac to Source/WebKit/mac
+2013-05-04 Dean Jackson <dino@apple.com>
+
+ Animations and Transitions should not start when globally suspended
+ https://bugs.webkit.org/show_bug.cgi?id=114915
+
+ Reviewed by Sam Weinig.
+
+ The WebView private API cssAnimationsSuspended did not necessarily
+ reflect the reality of the Frame's AnimationController value because it
+ was caching rather than asking directly. While the WebCore part of this
+ patch ensured loading the Document wouldn't resume all animations, it
+ is still better to ask directly.
+
+ * WebView/WebView.mm:
+ (-[WebView cssAnimationsSuspended]): Call into AnimationController.
+ (-[WebView setCSSAnimationsSuspended:]): Ditto.
+ * WebView/WebViewData.h: Remove cssAnimationsSuspended boolean.
+ * WebView/WebViewData.mm: Ditto.
+ (-[WebViewPrivate init]):
+
2013-05-04 Sam Weinig <sam@webkit.org>
Move PopupMenuMac and SearchPopupMenuMac to Source/WebKit/mac
- (BOOL)cssAnimationsSuspended
{
- return _private->cssAnimationsSuspended;
+ // should ask the page!
+ Frame* frame = core([self mainFrame]);
+ if (frame)
+ return frame->animation()->isSuspended();
+
+ return false;
}
- (void)setCSSAnimationsSuspended:(BOOL)suspended
{
- if (suspended == _private->cssAnimationsSuspended)
+ Frame* frame = core([self mainFrame]);
+ if (suspended == frame->animation()->isSuspended())
return;
- _private->cssAnimationsSuspended = suspended;
-
- Frame* frame = core([self mainFrame]);
if (suspended)
frame->animation()->suspendAnimations();
else
BOOL becomingFirstResponder;
BOOL becomingFirstResponderFromOutside;
BOOL usesPageCache;
- BOOL cssAnimationsSuspended;
NSColor *backgroundColor;
allowsUndo = YES;
usesPageCache = YES;
shouldUpdateWhileOffscreen = YES;
- cssAnimationsSuspended = NO;
zoomMultiplier = 1;
zoomsTextOnly = NO;
+2013-05-04 Dean Jackson <dino@apple.com>
+
+ Animations and Transitions should not start when globally suspended
+ https://bugs.webkit.org/show_bug.cgi?id=114915
+
+ Reviewed by Sam Weinig.
+
+ Export AnimationController::isSuspended
+
+ * WebKit.vcproj/WebKitExports.def.in:
+
2013-05-01 Sergio Villar Senin <svillar@igalia.com>
Show a block cursor in overtype mode
?viewportArguments@Page@WebCore@@QBE?AUViewportArguments@2@XZ
?isPageBoxVisible@Document@WebCore@@QAE_NH@Z
?isActive@InsertionPoint@WebCore@@QBE_NXZ
+ ?isSuspended@AnimationController@WebCore@@QBE_NXZ
?suspendAnimations@AnimationController@WebCore@@QAEXXZ
?resumeAnimations@AnimationController@WebCore@@QAEXXZ
?setAllowsRoundingHacks@TextRun@WebCore@@SAX_N@Z
_ZNK7WebCore14DocumentMarker11descriptionEv;
_ZN7WebCore8Document16isPageBoxVisibleEi;
_ZN7WebCore18ContentDistributor22ensureSelectFeatureSetEPNS_13ElementShadowE;
+_ZNK7WebCore19AnimationController11isSuspendedEv;
_ZN7WebCore19AnimationController17suspendAnimationsEv;
_ZN7WebCore19AnimationController16resumeAnimationsEv;
_ZN7WebCore19AnimationController20pauseAnimationAtTimeEPNS_12RenderObjectERKN3WTF12AtomicStringEd;