void
KeyframeEffectReadOnly::UpdateProperties(nsStyleContext* aStyleContext)
{
  MOZ_ASSERT(aStyleContext);

  nsTArray<AnimationProperty> properties = BuildProperties(aStyleContext);

  if (mProperties == properties) {
    return;
  }

  // Preserve the state of the mIsRunningOnCompositor flag.
  nsCSSPropertyIDSet runningOnCompositorProperties;

  for (const AnimationProperty& property : mProperties) {
    if (property.mIsRunningOnCompositor) {
      runningOnCompositorProperties.AddProperty(property.mProperty);
    }
  }

  mProperties = Move(properties);

  for (AnimationProperty& property : mProperties) {
    property.mIsRunningOnCompositor =
      runningOnCompositorProperties.HasProperty(property.mProperty);
  }

  // FIXME (bug 1303235): Do this for Servo too
  if (aStyleContext->PresContext()->StyleSet()->IsGecko()) {
    CalculateCumulativeChangeHint(aStyleContext);
  }

  MarkCascadeNeedsUpdate();

  RequestRestyle(EffectCompositor::RestyleType::Layer);
}
void
KeyframeEffectReadOnly::UpdateProperties(nsStyleContext* aStyleContext)
{
  MOZ_ASSERT(aStyleContext);

  nsTArray<AnimationProperty> properties;
  if (mTarget) {
    // When GetComputedKeyframeValues or GetAnimationPropertiesFromKeyframes
    // calculate computed values from |mKeyframes|, they could possibly
    // trigger a subsequent restyle in which we rebuild animations. If that
    // happens we could find that |mKeyframes| is overwritten while it is
    // being iterated over. Normally that shouldn't happen but just in case we
    // make a copy of |mKeyframes| first and iterate over that instead.
    auto keyframesCopy(mKeyframes);

    nsTArray<ComputedKeyframeValues> computedValues =
      KeyframeUtils::GetComputedKeyframeValues(keyframesCopy,
                                               mTarget->mElement,
                                               aStyleContext);

    if (mEffectOptions.mSpacingMode == SpacingMode::paced) {
      KeyframeUtils::ApplySpacing(keyframesCopy, SpacingMode::paced,
                                  mEffectOptions.mPacedProperty,
                                  computedValues);
    }

    properties =
      KeyframeUtils::GetAnimationPropertiesFromKeyframes(keyframesCopy,
                                                         computedValues,
                                                         aStyleContext);

#ifdef DEBUG
    MOZ_ASSERT(SpecifiedKeyframeArraysAreEqual(mKeyframes, keyframesCopy),
               "Apart from the computed offset members, the keyframes array"
               " should not be modified");
#endif

    mKeyframes.SwapElements(keyframesCopy);
  }

  if (mProperties == properties) {
    return;
  }

  // Preserve the state of mWinsInCascade and mIsRunningOnCompositor flags.
  nsCSSPropertyIDSet winningInCascadeProperties;
  nsCSSPropertyIDSet runningOnCompositorProperties;

  for (const AnimationProperty& property : mProperties) {
    if (property.mWinsInCascade) {
      winningInCascadeProperties.AddProperty(property.mProperty);
    }
    if (property.mIsRunningOnCompositor) {
      runningOnCompositorProperties.AddProperty(property.mProperty);
    }
  }

  mProperties = Move(properties);

  for (AnimationProperty& property : mProperties) {
    property.mWinsInCascade =
      winningInCascadeProperties.HasProperty(property.mProperty);
    property.mIsRunningOnCompositor =
      runningOnCompositorProperties.HasProperty(property.mProperty);
  }

  CalculateCumulativeChangeHint(aStyleContext);

  MarkCascadeNeedsUpdate();

  RequestRestyle(EffectCompositor::RestyleType::Layer);
}