1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/style/nsAnimationManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1137 @@ 1.4 +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsAnimationManager.h" 1.10 +#include "nsTransitionManager.h" 1.11 + 1.12 +#include "mozilla/EventDispatcher.h" 1.13 +#include "mozilla/MemoryReporting.h" 1.14 + 1.15 +#include "nsPresContext.h" 1.16 +#include "nsRuleProcessorData.h" 1.17 +#include "nsStyleSet.h" 1.18 +#include "nsStyleChangeList.h" 1.19 +#include "nsCSSRules.h" 1.20 +#include "RestyleManager.h" 1.21 +#include "nsStyleAnimation.h" 1.22 +#include "nsLayoutUtils.h" 1.23 +#include "nsIFrame.h" 1.24 +#include "nsIDocument.h" 1.25 +#include "ActiveLayerTracker.h" 1.26 +#include <math.h> 1.27 + 1.28 +using namespace mozilla; 1.29 +using namespace mozilla::css; 1.30 + 1.31 +ElementAnimations::ElementAnimations(mozilla::dom::Element *aElement, 1.32 + nsIAtom *aElementProperty, 1.33 + nsAnimationManager *aAnimationManager, 1.34 + TimeStamp aNow) 1.35 + : CommonElementAnimationData(aElement, aElementProperty, 1.36 + aAnimationManager, aNow), 1.37 + mNeedsRefreshes(true) 1.38 +{ 1.39 +} 1.40 + 1.41 +static void 1.42 +ElementAnimationsPropertyDtor(void *aObject, 1.43 + nsIAtom *aPropertyName, 1.44 + void *aPropertyValue, 1.45 + void *aData) 1.46 +{ 1.47 + ElementAnimations *ea = static_cast<ElementAnimations*>(aPropertyValue); 1.48 +#ifdef DEBUG 1.49 + NS_ABORT_IF_FALSE(!ea->mCalledPropertyDtor, "can't call dtor twice"); 1.50 + ea->mCalledPropertyDtor = true; 1.51 +#endif 1.52 + delete ea; 1.53 +} 1.54 + 1.55 +double 1.56 +ElementAnimations::GetPositionInIteration(TimeDuration aElapsedDuration, 1.57 + TimeDuration aIterationDuration, 1.58 + double aIterationCount, 1.59 + uint32_t aDirection, 1.60 + StyleAnimation* aAnimation, 1.61 + ElementAnimations* aEa, 1.62 + EventArray* aEventsToDispatch) 1.63 +{ 1.64 + MOZ_ASSERT(!aAnimation == !aEa && !aAnimation == !aEventsToDispatch); 1.65 + 1.66 + // Set |currentIterationCount| to the (fractional) number of 1.67 + // iterations we've completed up to the current position. 1.68 + double currentIterationCount = aElapsedDuration / aIterationDuration; 1.69 + bool dispatchStartOrIteration = false; 1.70 + if (currentIterationCount >= aIterationCount) { 1.71 + if (aAnimation) { 1.72 + // Dispatch 'animationend' when needed. 1.73 + if (aAnimation->mLastNotification != 1.74 + StyleAnimation::LAST_NOTIFICATION_END) { 1.75 + aAnimation->mLastNotification = StyleAnimation::LAST_NOTIFICATION_END; 1.76 + AnimationEventInfo ei(aEa->mElement, aAnimation->mName, NS_ANIMATION_END, 1.77 + aElapsedDuration, aEa->PseudoElement()); 1.78 + aEventsToDispatch->AppendElement(ei); 1.79 + } 1.80 + 1.81 + if (!aAnimation->FillsForwards()) { 1.82 + // No animation data. 1.83 + return -1; 1.84 + } 1.85 + } else { 1.86 + // If aAnimation is null, that means we're on the compositor 1.87 + // thread. We want to just keep filling forwards until the main 1.88 + // thread gets around to updating the compositor thread (which 1.89 + // might take a little while). So just assume we fill fowards and 1.90 + // move on. 1.91 + } 1.92 + currentIterationCount = aIterationCount; 1.93 + } else { 1.94 + if (aAnimation && !aAnimation->IsPaused()) { 1.95 + aEa->mNeedsRefreshes = true; 1.96 + } 1.97 + if (currentIterationCount < 0.0) { 1.98 + NS_ASSERTION(aAnimation, "Should not run animation that hasn't started yet on the compositor"); 1.99 + if (!aAnimation->FillsBackwards()) { 1.100 + // No animation data. 1.101 + return -1; 1.102 + } 1.103 + currentIterationCount = 0.0; 1.104 + } else { 1.105 + dispatchStartOrIteration = aAnimation && !aAnimation->IsPaused(); 1.106 + } 1.107 + } 1.108 + 1.109 + // Set |positionInIteration| to the position from 0% to 100% along 1.110 + // the keyframes. 1.111 + NS_ABORT_IF_FALSE(currentIterationCount >= 0.0, "must be positive"); 1.112 + double whichIteration = floor(currentIterationCount); 1.113 + if (whichIteration == aIterationCount && whichIteration != 0.0) { 1.114 + // When the animation's iteration count is an integer (as it 1.115 + // normally is), we need to end at 100% of its last iteration 1.116 + // rather than 0% of the next one (unless it's zero). 1.117 + whichIteration -= 1.0; 1.118 + } 1.119 + double positionInIteration = currentIterationCount - whichIteration; 1.120 + 1.121 + bool thisIterationReverse = false; 1.122 + switch (aDirection) { 1.123 + case NS_STYLE_ANIMATION_DIRECTION_NORMAL: 1.124 + thisIterationReverse = false; 1.125 + break; 1.126 + case NS_STYLE_ANIMATION_DIRECTION_REVERSE: 1.127 + thisIterationReverse = true; 1.128 + break; 1.129 + case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE: 1.130 + // uint64_t has more integer precision than double does, so if 1.131 + // whichIteration is that large, we've already lost and we're just 1.132 + // guessing. But the animation is presumably oscillating so fast 1.133 + // it doesn't matter anyway. 1.134 + thisIterationReverse = (uint64_t(whichIteration) & 1) == 1; 1.135 + break; 1.136 + case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE_REVERSE: 1.137 + // see as previous case 1.138 + thisIterationReverse = (uint64_t(whichIteration) & 1) == 0; 1.139 + break; 1.140 + } 1.141 + if (thisIterationReverse) { 1.142 + positionInIteration = 1.0 - positionInIteration; 1.143 + } 1.144 + 1.145 + // Dispatch 'animationstart' or 'animationiteration' when needed. 1.146 + if (aAnimation && dispatchStartOrIteration && 1.147 + whichIteration != aAnimation->mLastNotification) { 1.148 + // Notify 'animationstart' even if a negative delay puts us 1.149 + // past the first iteration. 1.150 + // Note that when somebody changes the animation-duration 1.151 + // dynamically, this will fire an extra iteration event 1.152 + // immediately in many cases. It's not clear to me if that's the 1.153 + // right thing to do. 1.154 + uint32_t message = 1.155 + aAnimation->mLastNotification == StyleAnimation::LAST_NOTIFICATION_NONE 1.156 + ? NS_ANIMATION_START : NS_ANIMATION_ITERATION; 1.157 + 1.158 + aAnimation->mLastNotification = whichIteration; 1.159 + AnimationEventInfo ei(aEa->mElement, aAnimation->mName, message, 1.160 + aElapsedDuration, aEa->PseudoElement()); 1.161 + aEventsToDispatch->AppendElement(ei); 1.162 + } 1.163 + 1.164 + return positionInIteration; 1.165 +} 1.166 + 1.167 +void 1.168 +ElementAnimations::EnsureStyleRuleFor(TimeStamp aRefreshTime, 1.169 + EventArray& aEventsToDispatch, 1.170 + bool aIsThrottled) 1.171 +{ 1.172 + if (!mNeedsRefreshes) { 1.173 + mStyleRuleRefreshTime = aRefreshTime; 1.174 + return; 1.175 + } 1.176 + 1.177 + // If we're performing animations on the compositor thread, then we can skip 1.178 + // most of the work in this method. But even if we are throttled, then we 1.179 + // have to do the work if an animation is ending in order to get correct end 1.180 + // of animation behaviour (the styles of the animation disappear, or the fill 1.181 + // mode behaviour). This loop checks for any finishing animations and forces 1.182 + // the style recalculation if we find any. 1.183 + if (aIsThrottled) { 1.184 + for (uint32_t animIdx = mAnimations.Length(); animIdx-- != 0; ) { 1.185 + StyleAnimation &anim = mAnimations[animIdx]; 1.186 + 1.187 + if (anim.mProperties.Length() == 0 || 1.188 + anim.mIterationDuration.ToMilliseconds() <= 0.0) { 1.189 + continue; 1.190 + } 1.191 + 1.192 + uint32_t oldLastNotification = anim.mLastNotification; 1.193 + 1.194 + // We need to call GetPositionInIteration here to populate 1.195 + // aEventsToDispatch. 1.196 + // The ElapsedDurationAt() call here handles pausing. But: 1.197 + // FIXME: avoid recalculating every time when paused. 1.198 + GetPositionInIteration(anim.ElapsedDurationAt(aRefreshTime), 1.199 + anim.mIterationDuration, anim.mIterationCount, 1.200 + anim.mDirection, &anim, this, &aEventsToDispatch); 1.201 + 1.202 + // GetPositionInIteration just adjusted mLastNotification; check 1.203 + // its new value against the value before we called 1.204 + // GetPositionInIteration. 1.205 + // XXX We shouldn't really be using mLastNotification as a general 1.206 + // indicator that the animation has finished, it should be reserved for 1.207 + // events. If we use it differently in the future this use might need 1.208 + // changing. 1.209 + if (!anim.mIsRunningOnCompositor || 1.210 + (anim.mLastNotification != oldLastNotification && 1.211 + anim.mLastNotification == StyleAnimation::LAST_NOTIFICATION_END)) { 1.212 + aIsThrottled = false; 1.213 + break; 1.214 + } 1.215 + } 1.216 + } 1.217 + 1.218 + if (aIsThrottled) { 1.219 + return; 1.220 + } 1.221 + 1.222 + // mStyleRule may be null and valid, if we have no style to apply. 1.223 + if (mStyleRuleRefreshTime.IsNull() || 1.224 + mStyleRuleRefreshTime != aRefreshTime) { 1.225 + mStyleRuleRefreshTime = aRefreshTime; 1.226 + mStyleRule = nullptr; 1.227 + // We'll set mNeedsRefreshes to true below in all cases where we need them. 1.228 + mNeedsRefreshes = false; 1.229 + 1.230 + // FIXME(spec): assume that properties in higher animations override 1.231 + // those in lower ones. 1.232 + // Therefore, we iterate from last animation to first. 1.233 + nsCSSPropertySet properties; 1.234 + 1.235 + for (uint32_t animIdx = mAnimations.Length(); animIdx-- != 0; ) { 1.236 + StyleAnimation &anim = mAnimations[animIdx]; 1.237 + 1.238 + if (anim.mProperties.Length() == 0 || 1.239 + anim.mIterationDuration.ToMilliseconds() <= 0.0) { 1.240 + // No animation data. 1.241 + continue; 1.242 + } 1.243 + 1.244 + // The ElapsedDurationAt() call here handles pausing. But: 1.245 + // FIXME: avoid recalculating every time when paused. 1.246 + double positionInIteration = 1.247 + GetPositionInIteration(anim.ElapsedDurationAt(aRefreshTime), 1.248 + anim.mIterationDuration, anim.mIterationCount, 1.249 + anim.mDirection, &anim, this, 1.250 + &aEventsToDispatch); 1.251 + 1.252 + // The position is -1 when we don't have fill data for the current time, 1.253 + // so we shouldn't animate. 1.254 + if (positionInIteration == -1) 1.255 + continue; 1.256 + 1.257 + NS_ABORT_IF_FALSE(0.0 <= positionInIteration && 1.258 + positionInIteration <= 1.0, 1.259 + "position should be in [0-1]"); 1.260 + 1.261 + for (uint32_t propIdx = 0, propEnd = anim.mProperties.Length(); 1.262 + propIdx != propEnd; ++propIdx) 1.263 + { 1.264 + const AnimationProperty &prop = anim.mProperties[propIdx]; 1.265 + 1.266 + NS_ABORT_IF_FALSE(prop.mSegments[0].mFromKey == 0.0, 1.267 + "incorrect first from key"); 1.268 + NS_ABORT_IF_FALSE(prop.mSegments[prop.mSegments.Length() - 1].mToKey 1.269 + == 1.0, 1.270 + "incorrect last to key"); 1.271 + 1.272 + if (properties.HasProperty(prop.mProperty)) { 1.273 + // A later animation already set this property. 1.274 + continue; 1.275 + } 1.276 + properties.AddProperty(prop.mProperty); 1.277 + 1.278 + NS_ABORT_IF_FALSE(prop.mSegments.Length() > 0, 1.279 + "property should not be in animations if it " 1.280 + "has no segments"); 1.281 + 1.282 + // FIXME: Maybe cache the current segment? 1.283 + const AnimationPropertySegment *segment = prop.mSegments.Elements(), 1.284 + *segmentEnd = segment + prop.mSegments.Length(); 1.285 + while (segment->mToKey < positionInIteration) { 1.286 + NS_ABORT_IF_FALSE(segment->mFromKey < segment->mToKey, 1.287 + "incorrect keys"); 1.288 + ++segment; 1.289 + if (segment == segmentEnd) { 1.290 + NS_ABORT_IF_FALSE(false, "incorrect positionInIteration"); 1.291 + break; // in order to continue in outer loop (just below) 1.292 + } 1.293 + NS_ABORT_IF_FALSE(segment->mFromKey == (segment-1)->mToKey, 1.294 + "incorrect keys"); 1.295 + } 1.296 + if (segment == segmentEnd) { 1.297 + continue; 1.298 + } 1.299 + NS_ABORT_IF_FALSE(segment->mFromKey < segment->mToKey, 1.300 + "incorrect keys"); 1.301 + NS_ABORT_IF_FALSE(segment >= prop.mSegments.Elements() && 1.302 + size_t(segment - prop.mSegments.Elements()) < 1.303 + prop.mSegments.Length(), 1.304 + "out of array bounds"); 1.305 + 1.306 + if (!mStyleRule) { 1.307 + // Allocate the style rule now that we know we have animation data. 1.308 + mStyleRule = new css::AnimValuesStyleRule(); 1.309 + } 1.310 + 1.311 + double positionInSegment = (positionInIteration - segment->mFromKey) / 1.312 + (segment->mToKey - segment->mFromKey); 1.313 + double valuePosition = 1.314 + segment->mTimingFunction.GetValue(positionInSegment); 1.315 + 1.316 + nsStyleAnimation::Value *val = 1.317 + mStyleRule->AddEmptyValue(prop.mProperty); 1.318 + 1.319 +#ifdef DEBUG 1.320 + bool result = 1.321 +#endif 1.322 + nsStyleAnimation::Interpolate(prop.mProperty, 1.323 + segment->mFromValue, segment->mToValue, 1.324 + valuePosition, *val); 1.325 + NS_ABORT_IF_FALSE(result, "interpolate must succeed now"); 1.326 + } 1.327 + } 1.328 + } 1.329 +} 1.330 + 1.331 + 1.332 +bool 1.333 +ElementAnimations::HasAnimationOfProperty(nsCSSProperty aProperty) const 1.334 +{ 1.335 + for (uint32_t animIdx = mAnimations.Length(); animIdx-- != 0; ) { 1.336 + const StyleAnimation &anim = mAnimations[animIdx]; 1.337 + if (anim.HasAnimationOfProperty(aProperty)) { 1.338 + return true; 1.339 + } 1.340 + } 1.341 + return false; 1.342 +} 1.343 + 1.344 +bool 1.345 +ElementAnimations::CanPerformOnCompositorThread(CanAnimateFlags aFlags) const 1.346 +{ 1.347 + nsIFrame* frame = nsLayoutUtils::GetStyleFrame(mElement); 1.348 + if (!frame) { 1.349 + return false; 1.350 + } 1.351 + 1.352 + if (mElementProperty != nsGkAtoms::animationsProperty) { 1.353 + if (nsLayoutUtils::IsAnimationLoggingEnabled()) { 1.354 + nsCString message; 1.355 + message.AppendLiteral("Gecko bug: Async animation of pseudoelements not supported. See bug 771367 ("); 1.356 + message.Append(nsAtomCString(mElementProperty)); 1.357 + message.AppendLiteral(")"); 1.358 + LogAsyncAnimationFailure(message, mElement); 1.359 + } 1.360 + return false; 1.361 + } 1.362 + 1.363 + TimeStamp now = frame->PresContext()->RefreshDriver()->MostRecentRefresh(); 1.364 + 1.365 + for (uint32_t animIdx = mAnimations.Length(); animIdx-- != 0; ) { 1.366 + const StyleAnimation& anim = mAnimations[animIdx]; 1.367 + for (uint32_t propIdx = 0, propEnd = anim.mProperties.Length(); 1.368 + propIdx != propEnd; ++propIdx) { 1.369 + if (IsGeometricProperty(anim.mProperties[propIdx].mProperty) && 1.370 + anim.IsRunningAt(now)) { 1.371 + aFlags = CanAnimateFlags(aFlags | CanAnimate_HasGeometricProperty); 1.372 + break; 1.373 + } 1.374 + } 1.375 + } 1.376 + 1.377 + bool hasOpacity = false; 1.378 + bool hasTransform = false; 1.379 + for (uint32_t animIdx = mAnimations.Length(); animIdx-- != 0; ) { 1.380 + const StyleAnimation& anim = mAnimations[animIdx]; 1.381 + if (!anim.IsRunningAt(now)) { 1.382 + continue; 1.383 + } 1.384 + 1.385 + for (uint32_t propIdx = 0, propEnd = anim.mProperties.Length(); 1.386 + propIdx != propEnd; ++propIdx) { 1.387 + const AnimationProperty& prop = anim.mProperties[propIdx]; 1.388 + if (!CanAnimatePropertyOnCompositor(mElement, 1.389 + prop.mProperty, 1.390 + aFlags) || 1.391 + IsCompositorAnimationDisabledForFrame(frame)) { 1.392 + return false; 1.393 + } 1.394 + if (prop.mProperty == eCSSProperty_opacity) { 1.395 + hasOpacity = true; 1.396 + } else if (prop.mProperty == eCSSProperty_transform) { 1.397 + hasTransform = true; 1.398 + } 1.399 + } 1.400 + } 1.401 + // This animation can be done on the compositor. Mark the frame as active, in 1.402 + // case we are able to throttle this animation. 1.403 + if (hasOpacity) { 1.404 + ActiveLayerTracker::NotifyAnimated(frame, eCSSProperty_opacity); 1.405 + } 1.406 + if (hasTransform) { 1.407 + ActiveLayerTracker::NotifyAnimated(frame, eCSSProperty_transform); 1.408 + } 1.409 + return true; 1.410 +} 1.411 + 1.412 +ElementAnimations* 1.413 +nsAnimationManager::GetElementAnimations(dom::Element *aElement, 1.414 + nsCSSPseudoElements::Type aPseudoType, 1.415 + bool aCreateIfNeeded) 1.416 +{ 1.417 + if (!aCreateIfNeeded && PR_CLIST_IS_EMPTY(&mElementData)) { 1.418 + // Early return for the most common case. 1.419 + return nullptr; 1.420 + } 1.421 + 1.422 + nsIAtom *propName; 1.423 + if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) { 1.424 + propName = nsGkAtoms::animationsProperty; 1.425 + } else if (aPseudoType == nsCSSPseudoElements::ePseudo_before) { 1.426 + propName = nsGkAtoms::animationsOfBeforeProperty; 1.427 + } else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) { 1.428 + propName = nsGkAtoms::animationsOfAfterProperty; 1.429 + } else { 1.430 + NS_ASSERTION(!aCreateIfNeeded, 1.431 + "should never try to create transitions for pseudo " 1.432 + "other than :before or :after"); 1.433 + return nullptr; 1.434 + } 1.435 + ElementAnimations *ea = static_cast<ElementAnimations*>( 1.436 + aElement->GetProperty(propName)); 1.437 + if (!ea && aCreateIfNeeded) { 1.438 + // FIXME: Consider arena-allocating? 1.439 + ea = new ElementAnimations(aElement, propName, this, 1.440 + mPresContext->RefreshDriver()->MostRecentRefresh()); 1.441 + nsresult rv = aElement->SetProperty(propName, ea, 1.442 + ElementAnimationsPropertyDtor, false); 1.443 + if (NS_FAILED(rv)) { 1.444 + NS_WARNING("SetProperty failed"); 1.445 + delete ea; 1.446 + return nullptr; 1.447 + } 1.448 + if (propName == nsGkAtoms::animationsProperty) { 1.449 + aElement->SetMayHaveAnimations(); 1.450 + } 1.451 + 1.452 + AddElementData(ea); 1.453 + } 1.454 + 1.455 + return ea; 1.456 +} 1.457 + 1.458 + 1.459 +void 1.460 +nsAnimationManager::EnsureStyleRuleFor(ElementAnimations* aET) 1.461 +{ 1.462 + aET->EnsureStyleRuleFor(mPresContext->RefreshDriver()->MostRecentRefresh(), 1.463 + mPendingEvents, 1.464 + false); 1.465 + CheckNeedsRefresh(); 1.466 +} 1.467 + 1.468 +/* virtual */ void 1.469 +nsAnimationManager::RulesMatching(ElementRuleProcessorData* aData) 1.470 +{ 1.471 + NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext, 1.472 + "pres context mismatch"); 1.473 + nsIStyleRule *rule = 1.474 + GetAnimationRule(aData->mElement, 1.475 + nsCSSPseudoElements::ePseudo_NotPseudoElement); 1.476 + if (rule) { 1.477 + aData->mRuleWalker->Forward(rule); 1.478 + } 1.479 +} 1.480 + 1.481 +/* virtual */ void 1.482 +nsAnimationManager::RulesMatching(PseudoElementRuleProcessorData* aData) 1.483 +{ 1.484 + NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext, 1.485 + "pres context mismatch"); 1.486 + if (aData->mPseudoType != nsCSSPseudoElements::ePseudo_before && 1.487 + aData->mPseudoType != nsCSSPseudoElements::ePseudo_after) { 1.488 + return; 1.489 + } 1.490 + 1.491 + // FIXME: Do we really want to be the only thing keeping a 1.492 + // pseudo-element alive? I *think* the non-animation restyle should 1.493 + // handle that, but should add a test. 1.494 + nsIStyleRule *rule = GetAnimationRule(aData->mElement, aData->mPseudoType); 1.495 + if (rule) { 1.496 + aData->mRuleWalker->Forward(rule); 1.497 + } 1.498 +} 1.499 + 1.500 +/* virtual */ void 1.501 +nsAnimationManager::RulesMatching(AnonBoxRuleProcessorData* aData) 1.502 +{ 1.503 +} 1.504 + 1.505 +#ifdef MOZ_XUL 1.506 +/* virtual */ void 1.507 +nsAnimationManager::RulesMatching(XULTreeRuleProcessorData* aData) 1.508 +{ 1.509 +} 1.510 +#endif 1.511 + 1.512 +/* virtual */ size_t 1.513 +nsAnimationManager::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const 1.514 +{ 1.515 + return CommonAnimationManager::SizeOfExcludingThis(aMallocSizeOf); 1.516 + 1.517 + // Measurement of the following members may be added later if DMD finds it is 1.518 + // worthwhile: 1.519 + // - mPendingEvents 1.520 +} 1.521 + 1.522 +/* virtual */ size_t 1.523 +nsAnimationManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const 1.524 +{ 1.525 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.526 +} 1.527 + 1.528 +nsIStyleRule* 1.529 +nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext, 1.530 + mozilla::dom::Element* aElement) 1.531 +{ 1.532 + if (!mPresContext->IsProcessingAnimationStyleChange()) { 1.533 + if (!mPresContext->IsDynamic()) { 1.534 + // For print or print preview, ignore animations. 1.535 + return nullptr; 1.536 + } 1.537 + 1.538 + // Everything that causes our animation data to change triggers a 1.539 + // style change, which in turn triggers a non-animation restyle. 1.540 + // Likewise, when we initially construct frames, we're not in a 1.541 + // style change, but also not in an animation restyle. 1.542 + 1.543 + const nsStyleDisplay *disp = aStyleContext->StyleDisplay(); 1.544 + ElementAnimations *ea = 1.545 + GetElementAnimations(aElement, aStyleContext->GetPseudoType(), false); 1.546 + if (!ea && 1.547 + disp->mAnimationNameCount == 1 && 1.548 + disp->mAnimations[0].GetName().IsEmpty()) { 1.549 + return nullptr; 1.550 + } 1.551 + 1.552 + // build the animations list 1.553 + InfallibleTArray<StyleAnimation> newAnimations; 1.554 + BuildAnimations(aStyleContext, newAnimations); 1.555 + 1.556 + if (newAnimations.IsEmpty()) { 1.557 + if (ea) { 1.558 + ea->Destroy(); 1.559 + } 1.560 + return nullptr; 1.561 + } 1.562 + 1.563 + TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh(); 1.564 + 1.565 + if (ea) { 1.566 + ea->mStyleRule = nullptr; 1.567 + ea->mStyleRuleRefreshTime = TimeStamp(); 1.568 + ea->UpdateAnimationGeneration(mPresContext); 1.569 + 1.570 + // Copy over the start times and (if still paused) pause starts 1.571 + // for each animation (matching on name only) that was also in the 1.572 + // old list of animations. 1.573 + // This means that we honor dynamic changes, which isn't what the 1.574 + // spec says to do, but WebKit seems to honor at least some of 1.575 + // them. See 1.576 + // http://lists.w3.org/Archives/Public/www-style/2011Apr/0079.html 1.577 + // In order to honor what the spec said, we'd copy more data over 1.578 + // (or potentially optimize BuildAnimations to avoid rebuilding it 1.579 + // in the first place). 1.580 + if (!ea->mAnimations.IsEmpty()) { 1.581 + for (uint32_t newIdx = 0, newEnd = newAnimations.Length(); 1.582 + newIdx != newEnd; ++newIdx) { 1.583 + StyleAnimation *newAnim = &newAnimations[newIdx]; 1.584 + 1.585 + // Find the matching animation with this name in the old list 1.586 + // of animations. Because of this code, they must all have 1.587 + // the same start time, though they might differ in pause 1.588 + // state. So if a page uses multiple copies of the same 1.589 + // animation in one element's animation list, and gives them 1.590 + // different pause states, they, well, get what they deserve. 1.591 + // We'll use the last one since it's more likely to be the one 1.592 + // doing something. 1.593 + const StyleAnimation *oldAnim = nullptr; 1.594 + for (uint32_t oldIdx = ea->mAnimations.Length(); oldIdx-- != 0; ) { 1.595 + const StyleAnimation *a = &ea->mAnimations[oldIdx]; 1.596 + if (a->mName == newAnim->mName) { 1.597 + oldAnim = a; 1.598 + break; 1.599 + } 1.600 + } 1.601 + if (!oldAnim) { 1.602 + continue; 1.603 + } 1.604 + 1.605 + newAnim->mStartTime = oldAnim->mStartTime; 1.606 + newAnim->mLastNotification = oldAnim->mLastNotification; 1.607 + 1.608 + if (oldAnim->IsPaused()) { 1.609 + if (newAnim->IsPaused()) { 1.610 + // Copy pause start just like start time. 1.611 + newAnim->mPauseStart = oldAnim->mPauseStart; 1.612 + } else { 1.613 + // Handle change in pause state by adjusting start 1.614 + // time to unpause. 1.615 + newAnim->mStartTime += refreshTime - oldAnim->mPauseStart; 1.616 + } 1.617 + } 1.618 + } 1.619 + } 1.620 + } else { 1.621 + ea = GetElementAnimations(aElement, aStyleContext->GetPseudoType(), 1.622 + true); 1.623 + } 1.624 + ea->mAnimations.SwapElements(newAnimations); 1.625 + ea->mNeedsRefreshes = true; 1.626 + 1.627 + ea->EnsureStyleRuleFor(refreshTime, mPendingEvents, false); 1.628 + CheckNeedsRefresh(); 1.629 + // We don't actually dispatch the mPendingEvents now. We'll either 1.630 + // dispatch them the next time we get a refresh driver notification 1.631 + // or the next time somebody calls 1.632 + // nsPresShell::FlushPendingNotifications. 1.633 + if (!mPendingEvents.IsEmpty()) { 1.634 + mPresContext->Document()->SetNeedStyleFlush(); 1.635 + } 1.636 + } 1.637 + 1.638 + return GetAnimationRule(aElement, aStyleContext->GetPseudoType()); 1.639 +} 1.640 + 1.641 +class PercentageHashKey : public PLDHashEntryHdr 1.642 +{ 1.643 +public: 1.644 + typedef const float& KeyType; 1.645 + typedef const float* KeyTypePointer; 1.646 + 1.647 + PercentageHashKey(KeyTypePointer aKey) : mValue(*aKey) { } 1.648 + PercentageHashKey(const PercentageHashKey& toCopy) : mValue(toCopy.mValue) { } 1.649 + ~PercentageHashKey() { } 1.650 + 1.651 + KeyType GetKey() const { return mValue; } 1.652 + bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mValue; } 1.653 + 1.654 + static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } 1.655 + static PLDHashNumber HashKey(KeyTypePointer aKey) { 1.656 + static_assert(sizeof(PLDHashNumber) == sizeof(uint32_t), 1.657 + "this hash function assumes PLDHashNumber is uint32_t"); 1.658 + static_assert(PLDHashNumber(-1) > PLDHashNumber(0), 1.659 + "this hash function assumes PLDHashNumber is uint32_t"); 1.660 + float key = *aKey; 1.661 + NS_ABORT_IF_FALSE(0.0f <= key && key <= 1.0f, "out of range"); 1.662 + return PLDHashNumber(key * UINT32_MAX); 1.663 + } 1.664 + enum { ALLOW_MEMMOVE = true }; 1.665 + 1.666 +private: 1.667 + const float mValue; 1.668 +}; 1.669 + 1.670 +struct KeyframeData { 1.671 + float mKey; 1.672 + uint32_t mIndex; // store original order since sort algorithm is not stable 1.673 + nsCSSKeyframeRule *mRule; 1.674 +}; 1.675 + 1.676 +struct KeyframeDataComparator { 1.677 + bool Equals(const KeyframeData& A, const KeyframeData& B) const { 1.678 + return A.mKey == B.mKey && A.mIndex == B.mIndex; 1.679 + } 1.680 + bool LessThan(const KeyframeData& A, const KeyframeData& B) const { 1.681 + return A.mKey < B.mKey || (A.mKey == B.mKey && A.mIndex < B.mIndex); 1.682 + } 1.683 +}; 1.684 + 1.685 +class ResolvedStyleCache { 1.686 +public: 1.687 + ResolvedStyleCache() : mCache(16) {} 1.688 + nsStyleContext* Get(nsPresContext *aPresContext, 1.689 + nsStyleContext *aParentStyleContext, 1.690 + nsCSSKeyframeRule *aKeyframe); 1.691 + 1.692 +private: 1.693 + nsRefPtrHashtable<nsPtrHashKey<nsCSSKeyframeRule>, nsStyleContext> mCache; 1.694 +}; 1.695 + 1.696 +nsStyleContext* 1.697 +ResolvedStyleCache::Get(nsPresContext *aPresContext, 1.698 + nsStyleContext *aParentStyleContext, 1.699 + nsCSSKeyframeRule *aKeyframe) 1.700 +{ 1.701 + // FIXME (spec): The css3-animations spec isn't very clear about how 1.702 + // properties are resolved when they have values that depend on other 1.703 + // properties (e.g., values in 'em'). I presume that they're resolved 1.704 + // relative to the other styles of the element. The question is 1.705 + // whether they are resolved relative to other animations: I assume 1.706 + // that they're not, since that would prevent us from caching a lot of 1.707 + // data that we'd really like to cache (in particular, the 1.708 + // nsStyleAnimation::Value values in AnimationPropertySegment). 1.709 + nsStyleContext *result = mCache.GetWeak(aKeyframe); 1.710 + if (!result) { 1.711 + nsCOMArray<nsIStyleRule> rules; 1.712 + rules.AppendObject(aKeyframe); 1.713 + nsRefPtr<nsStyleContext> resultStrong = aPresContext->StyleSet()-> 1.714 + ResolveStyleByAddingRules(aParentStyleContext, rules); 1.715 + mCache.Put(aKeyframe, resultStrong); 1.716 + result = resultStrong; 1.717 + } 1.718 + return result; 1.719 +} 1.720 + 1.721 +void 1.722 +nsAnimationManager::BuildAnimations(nsStyleContext* aStyleContext, 1.723 + InfallibleTArray<StyleAnimation>& 1.724 + aAnimations) 1.725 +{ 1.726 + NS_ABORT_IF_FALSE(aAnimations.IsEmpty(), "expect empty array"); 1.727 + 1.728 + ResolvedStyleCache resolvedStyles; 1.729 + 1.730 + const nsStyleDisplay *disp = aStyleContext->StyleDisplay(); 1.731 + TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh(); 1.732 + for (uint32_t animIdx = 0, animEnd = disp->mAnimationNameCount; 1.733 + animIdx != animEnd; ++animIdx) { 1.734 + const nsAnimation& aSrc = disp->mAnimations[animIdx]; 1.735 + StyleAnimation& aDest = *aAnimations.AppendElement(); 1.736 + 1.737 + aDest.mName = aSrc.GetName(); 1.738 + aDest.mIterationCount = aSrc.GetIterationCount(); 1.739 + aDest.mDirection = aSrc.GetDirection(); 1.740 + aDest.mFillMode = aSrc.GetFillMode(); 1.741 + aDest.mPlayState = aSrc.GetPlayState(); 1.742 + 1.743 + aDest.mDelay = TimeDuration::FromMilliseconds(aSrc.GetDelay()); 1.744 + aDest.mStartTime = now; 1.745 + if (aDest.IsPaused()) { 1.746 + aDest.mPauseStart = now; 1.747 + } else { 1.748 + aDest.mPauseStart = TimeStamp(); 1.749 + } 1.750 + 1.751 + aDest.mIterationDuration = TimeDuration::FromMilliseconds(aSrc.GetDuration()); 1.752 + 1.753 + nsCSSKeyframesRule* rule = 1.754 + mPresContext->StyleSet()->KeyframesRuleForName(mPresContext, aDest.mName); 1.755 + if (!rule) { 1.756 + // no segments 1.757 + continue; 1.758 + } 1.759 + 1.760 + // While current drafts of css3-animations say that later keyframes 1.761 + // with the same key entirely replace earlier ones (no cascading), 1.762 + // this is a bad idea and contradictory to the rest of CSS. So 1.763 + // we're going to keep all the keyframes for each key and then do 1.764 + // the replacement on a per-property basis rather than a per-rule 1.765 + // basis, just like everything else in CSS. 1.766 + 1.767 + AutoInfallibleTArray<KeyframeData, 16> sortedKeyframes; 1.768 + 1.769 + for (uint32_t ruleIdx = 0, ruleEnd = rule->StyleRuleCount(); 1.770 + ruleIdx != ruleEnd; ++ruleIdx) { 1.771 + css::Rule* cssRule = rule->GetStyleRuleAt(ruleIdx); 1.772 + NS_ABORT_IF_FALSE(cssRule, "must have rule"); 1.773 + NS_ABORT_IF_FALSE(cssRule->GetType() == css::Rule::KEYFRAME_RULE, 1.774 + "must be keyframe rule"); 1.775 + nsCSSKeyframeRule *kfRule = static_cast<nsCSSKeyframeRule*>(cssRule); 1.776 + 1.777 + const nsTArray<float> &keys = kfRule->GetKeys(); 1.778 + for (uint32_t keyIdx = 0, keyEnd = keys.Length(); 1.779 + keyIdx != keyEnd; ++keyIdx) { 1.780 + float key = keys[keyIdx]; 1.781 + // FIXME (spec): The spec doesn't say what to do with 1.782 + // out-of-range keyframes. We'll ignore them. 1.783 + // (And PercentageHashKey currently assumes we either ignore or 1.784 + // clamp them.) 1.785 + if (0.0f <= key && key <= 1.0f) { 1.786 + KeyframeData *data = sortedKeyframes.AppendElement(); 1.787 + data->mKey = key; 1.788 + data->mIndex = ruleIdx; 1.789 + data->mRule = kfRule; 1.790 + } 1.791 + } 1.792 + } 1.793 + 1.794 + sortedKeyframes.Sort(KeyframeDataComparator()); 1.795 + 1.796 + if (sortedKeyframes.Length() == 0) { 1.797 + // no segments 1.798 + continue; 1.799 + } 1.800 + 1.801 + // Record the properties that are present in any keyframe rules we 1.802 + // are using. 1.803 + nsCSSPropertySet properties; 1.804 + 1.805 + for (uint32_t kfIdx = 0, kfEnd = sortedKeyframes.Length(); 1.806 + kfIdx != kfEnd; ++kfIdx) { 1.807 + css::Declaration *decl = sortedKeyframes[kfIdx].mRule->Declaration(); 1.808 + for (uint32_t propIdx = 0, propEnd = decl->Count(); 1.809 + propIdx != propEnd; ++propIdx) { 1.810 + nsCSSProperty prop = decl->GetPropertyAt(propIdx); 1.811 + if (prop != eCSSPropertyExtra_variable) { 1.812 + // CSS Variables are not animatable 1.813 + properties.AddProperty(prop); 1.814 + } 1.815 + } 1.816 + } 1.817 + 1.818 + for (nsCSSProperty prop = nsCSSProperty(0); 1.819 + prop < eCSSProperty_COUNT_no_shorthands; 1.820 + prop = nsCSSProperty(prop + 1)) { 1.821 + if (!properties.HasProperty(prop) || 1.822 + nsCSSProps::kAnimTypeTable[prop] == eStyleAnimType_None) { 1.823 + continue; 1.824 + } 1.825 + 1.826 + // Build a list of the keyframes to use for this property. This 1.827 + // means we need every keyframe with the property in it, except 1.828 + // for those keyframes where a later keyframe with the *same key* 1.829 + // also has the property. 1.830 + AutoInfallibleTArray<uint32_t, 16> keyframesWithProperty; 1.831 + float lastKey = 100.0f; // an invalid key 1.832 + for (uint32_t kfIdx = 0, kfEnd = sortedKeyframes.Length(); 1.833 + kfIdx != kfEnd; ++kfIdx) { 1.834 + KeyframeData &kf = sortedKeyframes[kfIdx]; 1.835 + if (!kf.mRule->Declaration()->HasProperty(prop)) { 1.836 + continue; 1.837 + } 1.838 + if (kf.mKey == lastKey) { 1.839 + // Replace previous occurrence of same key. 1.840 + keyframesWithProperty[keyframesWithProperty.Length() - 1] = kfIdx; 1.841 + } else { 1.842 + keyframesWithProperty.AppendElement(kfIdx); 1.843 + } 1.844 + lastKey = kf.mKey; 1.845 + } 1.846 + 1.847 + AnimationProperty &propData = *aDest.mProperties.AppendElement(); 1.848 + propData.mProperty = prop; 1.849 + 1.850 + KeyframeData *fromKeyframe = nullptr; 1.851 + nsRefPtr<nsStyleContext> fromContext; 1.852 + bool interpolated = true; 1.853 + for (uint32_t wpIdx = 0, wpEnd = keyframesWithProperty.Length(); 1.854 + wpIdx != wpEnd; ++wpIdx) { 1.855 + uint32_t kfIdx = keyframesWithProperty[wpIdx]; 1.856 + KeyframeData &toKeyframe = sortedKeyframes[kfIdx]; 1.857 + 1.858 + nsRefPtr<nsStyleContext> toContext = 1.859 + resolvedStyles.Get(mPresContext, aStyleContext, toKeyframe.mRule); 1.860 + 1.861 + if (fromKeyframe) { 1.862 + interpolated = interpolated && 1.863 + BuildSegment(propData.mSegments, prop, aSrc, 1.864 + fromKeyframe->mKey, fromContext, 1.865 + fromKeyframe->mRule->Declaration(), 1.866 + toKeyframe.mKey, toContext); 1.867 + } else { 1.868 + if (toKeyframe.mKey != 0.0f) { 1.869 + // There's no data for this property at 0%, so use the 1.870 + // cascaded value above us. 1.871 + interpolated = interpolated && 1.872 + BuildSegment(propData.mSegments, prop, aSrc, 1.873 + 0.0f, aStyleContext, nullptr, 1.874 + toKeyframe.mKey, toContext); 1.875 + } 1.876 + } 1.877 + 1.878 + fromContext = toContext; 1.879 + fromKeyframe = &toKeyframe; 1.880 + } 1.881 + 1.882 + if (fromKeyframe->mKey != 1.0f) { 1.883 + // There's no data for this property at 100%, so use the 1.884 + // cascaded value above us. 1.885 + interpolated = interpolated && 1.886 + BuildSegment(propData.mSegments, prop, aSrc, 1.887 + fromKeyframe->mKey, fromContext, 1.888 + fromKeyframe->mRule->Declaration(), 1.889 + 1.0f, aStyleContext); 1.890 + } 1.891 + 1.892 + // If we failed to build any segments due to inability to 1.893 + // interpolate, remove the property from the animation. (It's not 1.894 + // clear if this is the right thing to do -- we could run some of 1.895 + // the segments, but it's really not clear whether we should skip 1.896 + // values (which?) or skip segments, so best to skip the whole 1.897 + // thing for now.) 1.898 + if (!interpolated) { 1.899 + aDest.mProperties.RemoveElementAt(aDest.mProperties.Length() - 1); 1.900 + } 1.901 + } 1.902 + } 1.903 +} 1.904 + 1.905 +bool 1.906 +nsAnimationManager::BuildSegment(InfallibleTArray<AnimationPropertySegment>& 1.907 + aSegments, 1.908 + nsCSSProperty aProperty, 1.909 + const nsAnimation& aAnimation, 1.910 + float aFromKey, nsStyleContext* aFromContext, 1.911 + mozilla::css::Declaration* aFromDeclaration, 1.912 + float aToKey, nsStyleContext* aToContext) 1.913 +{ 1.914 + nsStyleAnimation::Value fromValue, toValue, dummyValue; 1.915 + if (!ExtractComputedValueForTransition(aProperty, aFromContext, fromValue) || 1.916 + !ExtractComputedValueForTransition(aProperty, aToContext, toValue) || 1.917 + // Check that we can interpolate between these values 1.918 + // (If this is ever a performance problem, we could add a 1.919 + // CanInterpolate method, but it seems fine for now.) 1.920 + !nsStyleAnimation::Interpolate(aProperty, fromValue, toValue, 1.921 + 0.5, dummyValue)) { 1.922 + return false; 1.923 + } 1.924 + 1.925 + AnimationPropertySegment &segment = *aSegments.AppendElement(); 1.926 + 1.927 + segment.mFromValue = fromValue; 1.928 + segment.mToValue = toValue; 1.929 + segment.mFromKey = aFromKey; 1.930 + segment.mToKey = aToKey; 1.931 + const nsTimingFunction *tf; 1.932 + if (aFromDeclaration && 1.933 + aFromDeclaration->HasProperty(eCSSProperty_animation_timing_function)) { 1.934 + tf = &aFromContext->StyleDisplay()->mAnimations[0].GetTimingFunction(); 1.935 + } else { 1.936 + tf = &aAnimation.GetTimingFunction(); 1.937 + } 1.938 + segment.mTimingFunction.Init(*tf); 1.939 + 1.940 + return true; 1.941 +} 1.942 + 1.943 +nsIStyleRule* 1.944 +nsAnimationManager::GetAnimationRule(mozilla::dom::Element* aElement, 1.945 + nsCSSPseudoElements::Type aPseudoType) 1.946 +{ 1.947 + NS_ABORT_IF_FALSE( 1.948 + aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement || 1.949 + aPseudoType == nsCSSPseudoElements::ePseudo_before || 1.950 + aPseudoType == nsCSSPseudoElements::ePseudo_after, 1.951 + "forbidden pseudo type"); 1.952 + 1.953 + if (!mPresContext->IsDynamic()) { 1.954 + // For print or print preview, ignore animations. 1.955 + return nullptr; 1.956 + } 1.957 + 1.958 + ElementAnimations *ea = 1.959 + GetElementAnimations(aElement, aPseudoType, false); 1.960 + if (!ea) { 1.961 + return nullptr; 1.962 + } 1.963 + 1.964 + if (mPresContext->IsProcessingRestyles() && 1.965 + !mPresContext->IsProcessingAnimationStyleChange()) { 1.966 + // During the non-animation part of processing restyles, we don't 1.967 + // add the animation rule. 1.968 + 1.969 + if (ea->mStyleRule) { 1.970 + ea->PostRestyleForAnimation(mPresContext); 1.971 + } 1.972 + 1.973 + return nullptr; 1.974 + } 1.975 + 1.976 + NS_WARN_IF_FALSE(!ea->mNeedsRefreshes || 1.977 + ea->mStyleRuleRefreshTime == 1.978 + mPresContext->RefreshDriver()->MostRecentRefresh(), 1.979 + "should already have refreshed style rule"); 1.980 + 1.981 + return ea->mStyleRule; 1.982 +} 1.983 + 1.984 +/* virtual */ void 1.985 +nsAnimationManager::WillRefresh(mozilla::TimeStamp aTime) 1.986 +{ 1.987 + NS_ABORT_IF_FALSE(mPresContext, 1.988 + "refresh driver should not notify additional observers " 1.989 + "after pres context has been destroyed"); 1.990 + if (!mPresContext->GetPresShell()) { 1.991 + // Someone might be keeping mPresContext alive past the point 1.992 + // where it has been torn down; don't bother doing anything in 1.993 + // this case. But do get rid of all our transitions so we stop 1.994 + // triggering refreshes. 1.995 + RemoveAllElementData(); 1.996 + return; 1.997 + } 1.998 + 1.999 + FlushAnimations(Can_Throttle); 1.1000 +} 1.1001 + 1.1002 +void 1.1003 +nsAnimationManager::AddElementData(CommonElementAnimationData* aData) 1.1004 +{ 1.1005 + if (!mObservingRefreshDriver) { 1.1006 + NS_ASSERTION(static_cast<ElementAnimations*>(aData)->mNeedsRefreshes, 1.1007 + "Added data which doesn't need refreshing?"); 1.1008 + // We need to observe the refresh driver. 1.1009 + mPresContext->RefreshDriver()->AddRefreshObserver(this, Flush_Style); 1.1010 + mObservingRefreshDriver = true; 1.1011 + } 1.1012 + 1.1013 + PR_INSERT_BEFORE(aData, &mElementData); 1.1014 +} 1.1015 + 1.1016 +void 1.1017 +nsAnimationManager::CheckNeedsRefresh() 1.1018 +{ 1.1019 + for (PRCList *l = PR_LIST_HEAD(&mElementData); l != &mElementData; 1.1020 + l = PR_NEXT_LINK(l)) { 1.1021 + if (static_cast<ElementAnimations*>(l)->mNeedsRefreshes) { 1.1022 + if (!mObservingRefreshDriver) { 1.1023 + mPresContext->RefreshDriver()->AddRefreshObserver(this, Flush_Style); 1.1024 + mObservingRefreshDriver = true; 1.1025 + } 1.1026 + return; 1.1027 + } 1.1028 + } 1.1029 + if (mObservingRefreshDriver) { 1.1030 + mObservingRefreshDriver = false; 1.1031 + mPresContext->RefreshDriver()->RemoveRefreshObserver(this, Flush_Style); 1.1032 + } 1.1033 +} 1.1034 + 1.1035 +void 1.1036 +nsAnimationManager::FlushAnimations(FlushFlags aFlags) 1.1037 +{ 1.1038 + // FIXME: check that there's at least one style rule that's not 1.1039 + // in its "done" state, and if there isn't, remove ourselves from 1.1040 + // the refresh driver (but leave the animations!). 1.1041 + TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh(); 1.1042 + bool didThrottle = false; 1.1043 + for (PRCList *l = PR_LIST_HEAD(&mElementData); l != &mElementData; 1.1044 + l = PR_NEXT_LINK(l)) { 1.1045 + ElementAnimations *ea = static_cast<ElementAnimations*>(l); 1.1046 + bool canThrottleTick = aFlags == Can_Throttle && 1.1047 + ea->CanPerformOnCompositorThread( 1.1048 + CommonElementAnimationData::CanAnimateFlags(0)) && 1.1049 + ea->CanThrottleAnimation(now); 1.1050 + 1.1051 + nsRefPtr<css::AnimValuesStyleRule> oldStyleRule = ea->mStyleRule; 1.1052 + ea->EnsureStyleRuleFor(now, mPendingEvents, canThrottleTick); 1.1053 + CheckNeedsRefresh(); 1.1054 + if (oldStyleRule != ea->mStyleRule) { 1.1055 + ea->PostRestyleForAnimation(mPresContext); 1.1056 + } else { 1.1057 + didThrottle = true; 1.1058 + } 1.1059 + } 1.1060 + 1.1061 + if (didThrottle) { 1.1062 + mPresContext->Document()->SetNeedStyleFlush(); 1.1063 + } 1.1064 + 1.1065 + DispatchEvents(); // may destroy us 1.1066 +} 1.1067 + 1.1068 +void 1.1069 +nsAnimationManager::DoDispatchEvents() 1.1070 +{ 1.1071 + EventArray events; 1.1072 + mPendingEvents.SwapElements(events); 1.1073 + for (uint32_t i = 0, i_end = events.Length(); i < i_end; ++i) { 1.1074 + AnimationEventInfo &info = events[i]; 1.1075 + EventDispatcher::Dispatch(info.mElement, mPresContext, &info.mEvent); 1.1076 + 1.1077 + if (!mPresContext) { 1.1078 + break; 1.1079 + } 1.1080 + } 1.1081 +} 1.1082 + 1.1083 +void 1.1084 +nsAnimationManager::UpdateThrottledStylesForSubtree(nsIContent* aContent, 1.1085 + nsStyleContext* aParentStyle, 1.1086 + nsStyleChangeList& aChangeList) 1.1087 +{ 1.1088 + dom::Element* element; 1.1089 + if (aContent->IsElement()) { 1.1090 + element = aContent->AsElement(); 1.1091 + } else { 1.1092 + element = nullptr; 1.1093 + } 1.1094 + 1.1095 + nsRefPtr<nsStyleContext> newStyle; 1.1096 + 1.1097 + ElementAnimations* ea; 1.1098 + if (element && 1.1099 + (ea = GetElementAnimations(element, 1.1100 + nsCSSPseudoElements::ePseudo_NotPseudoElement, 1.1101 + false))) { 1.1102 + // re-resolve our style 1.1103 + newStyle = UpdateThrottledStyle(element, aParentStyle, aChangeList); 1.1104 + // remove the current transition from the working set 1.1105 + ea->mFlushGeneration = mPresContext->RefreshDriver()->MostRecentRefresh(); 1.1106 + } else { 1.1107 + newStyle = ReparentContent(aContent, aParentStyle); 1.1108 + } 1.1109 + 1.1110 + // walk the children 1.1111 + if (newStyle) { 1.1112 + for (nsIContent *child = aContent->GetFirstChild(); child; 1.1113 + child = child->GetNextSibling()) { 1.1114 + UpdateThrottledStylesForSubtree(child, newStyle, aChangeList); 1.1115 + } 1.1116 + } 1.1117 +} 1.1118 + 1.1119 +IMPL_UPDATE_ALL_THROTTLED_STYLES_INTERNAL(nsAnimationManager, 1.1120 + GetElementAnimations) 1.1121 + 1.1122 +void 1.1123 +nsAnimationManager::UpdateAllThrottledStyles() 1.1124 +{ 1.1125 + if (PR_CLIST_IS_EMPTY(&mElementData)) { 1.1126 + // no throttled animations, leave early 1.1127 + mPresContext->TickLastUpdateThrottledAnimationStyle(); 1.1128 + return; 1.1129 + } 1.1130 + 1.1131 + if (mPresContext->ThrottledAnimationStyleIsUpToDate()) { 1.1132 + // throttled transitions are up to date, leave early 1.1133 + return; 1.1134 + } 1.1135 + 1.1136 + mPresContext->TickLastUpdateThrottledAnimationStyle(); 1.1137 + 1.1138 + UpdateAllThrottledStylesInternal(); 1.1139 +} 1.1140 +