layout/style/nsAnimationManager.cpp

changeset 0
6474c204b198
     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 +

mercurial