Bug 1216842 - Part 3: Change ComputedTimingFunction* to Maybe<ComputedTimingFunction>. r=cam
authorHiroyuki Ikezoe <hiikezoe@mozilla-japan.org>
Fri, 29 Jan 2016 14:44:00 +0100
changeset 327580 eabcf625bf8e8bb5d9dc887ddc3bef9a9fdeeee1
parent 327579 346880930a7817908cf484730e453bffef0f880d
child 327581 28618bbe4f248d332abc2b499b75b9cddaf7ebd2
push id10262
push userarmenzg@mozilla.com
push dateMon, 01 Feb 2016 14:48:06 +0000
reviewerscam
bugs1216842
milestone47.0a1
Bug 1216842 - Part 3: Change ComputedTimingFunction* to Maybe<ComputedTimingFunction>. r=cam Nothing() represents linear function, i.e. skip calculation. ParseEasing is changed to return a Maybe<ComputedTimingFunction>, if timing function is linear function, ParseEasing returns Nothing().
dom/animation/KeyframeEffect.cpp
dom/animation/KeyframeEffect.h
layout/base/nsDisplayList.cpp
layout/style/nsAnimationManager.cpp
layout/style/nsTransitionManager.cpp
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -534,17 +534,18 @@ KeyframeEffectReadOnly::ComposeStyle(Ref
       // Allocate the style rule now that we know we have animation data.
       aStyleRule = new AnimValuesStyleRule();
     }
 
     double positionInSegment =
       (computedTiming.mProgress.Value() - segment->mFromKey) /
       (segment->mToKey - segment->mFromKey);
     double valuePosition =
-      segment->mTimingFunction.GetValue(positionInSegment);
+      ComputedTimingFunction::GetPortion(segment->mTimingFunction,
+                                         positionInSegment);
 
     StyleAnimationValue *val = aStyleRule->AddEmptyValue(prop.mProperty);
 
 #ifdef DEBUG
     bool result =
 #endif
       StyleAnimationValue::Interpolate(prop.mProperty,
                                        segment->mFromValue,
@@ -738,17 +739,17 @@ struct OrderedKeyframeValueEntry : Keyfr
  *
  * KeyframeValueEntry is used in BuildAnimationPropertyListFromKeyframeSequence
  * to gather data for each individual segment described by an author-supplied
  * an IDL sequence<Keyframe> value so that they can be parsed into mProperties.
  */
 struct KeyframeValueEntry : KeyframeValue
 {
   float mOffset;
-  ComputedTimingFunction mTimingFunction;
+  Maybe<ComputedTimingFunction> mTimingFunction;
 
   struct PropertyOffsetComparator
   {
     static bool Equals(const KeyframeValueEntry& aLhs,
                        const KeyframeValueEntry& aRhs)
     {
       return aLhs.mProperty == aRhs.mProperty &&
              aLhs.mOffset == aRhs.mOffset;
@@ -860,23 +861,22 @@ struct PropertyValuesPair
 struct OffsetIndexedKeyframe
 {
   binding_detail::FastKeyframe mKeyframeDict;
   nsTArray<PropertyValuesPair> mPropertyValuePairs;
 };
 
 /**
  * Parses a CSS <single-transition-timing-function> value from
- * aEasing into a ComputedTimingFunction.  If parsing fails, aResult will
- * be set to 'linear'.
+ * aEasing into a ComputedTimingFunction.  If parsing fails, Nothing() will
+ * be returned.
  */
-static void
+static Maybe<ComputedTimingFunction>
 ParseEasing(Element* aTarget,
-            const nsAString& aEasing,
-            ComputedTimingFunction& aResult)
+            const nsAString& aEasing)
 {
   nsIDocument* doc = aTarget->OwnerDoc();
 
   nsCSSValue value;
   nsCSSParser parser;
   parser.ParseLonghandProperty(eCSSProperty_animation_timing_function,
                                aEasing,
                                doc->GetDocumentURI(),
@@ -892,18 +892,19 @@ ParseEasing(Element* aTarget,
         break;
       }
       switch (list->mValue.GetUnit()) {
         case eCSSUnit_Enumerated:
         case eCSSUnit_Cubic_Bezier:
         case eCSSUnit_Steps: {
           nsTimingFunction timingFunction;
           nsRuleNode::ComputeTimingFunction(list->mValue, timingFunction);
-          aResult.Init(timingFunction);
-          return;
+          ComputedTimingFunction computedTimingFunction;
+          computedTimingFunction.Init(timingFunction);
+          return Some(computedTimingFunction);
         }
         default:
           MOZ_ASSERT_UNREACHABLE("unexpected animation-timing-function list "
                                  "item unit");
         break;
       }
       break;
     }
@@ -912,18 +913,17 @@ ParseEasing(Element* aTarget,
     case eCSSUnit_Initial:
     case eCSSUnit_Unset:
     case eCSSUnit_TokenStream:
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("unexpected animation-timing-function unit");
       break;
   }
-
-  aResult.Init(nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR));
+  return Nothing();
 }
 
 /**
  * An additional property (for a property-values pair) found on a Keyframe
  * or PropertyIndexedKeyframes object.
  */
 struct AdditionalProperty
 {
@@ -1204,18 +1204,18 @@ GenerateValueEntries(Element* aTarget,
                      ErrorResult& aRv)
 {
   nsCSSPropertySet properties;              // All properties encountered.
   nsCSSPropertySet propertiesWithFromValue; // Those with a defined 0% value.
   nsCSSPropertySet propertiesWithToValue;   // Those with a defined 100% value.
 
   for (OffsetIndexedKeyframe& keyframe : aKeyframes) {
     float offset = float(keyframe.mKeyframeDict.mOffset.Value());
-    ComputedTimingFunction easing;
-    ParseEasing(aTarget, keyframe.mKeyframeDict.mEasing, easing);
+    Maybe<ComputedTimingFunction> easing =
+      ParseEasing(aTarget, keyframe.mKeyframeDict.mEasing);
     // We ignore keyframe.mKeyframeDict.mComposite since we don't support
     // composite modes on keyframes yet.
 
     // keyframe.mPropertyValuePairs is currently sorted by CSS property IDL
     // name, since that was the order we read the properties from the JS
     // object.  Re-sort the list so that longhand properties appear before
     // shorthands, and with shorthands all appearing in increasing order of
     // number of components.  For two longhand properties, or two shorthands
@@ -1466,18 +1466,18 @@ BuildAnimationPropertyListFromPropertyIn
   // get its explicit dictionary members.
   binding_detail::FastPropertyIndexedKeyframes keyframes;
   if (!keyframes.Init(aCx, aValue, "PropertyIndexedKeyframes argument",
                       false)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  ComputedTimingFunction easing;
-  ParseEasing(aTarget, keyframes.mEasing, easing);
+  Maybe<ComputedTimingFunction> easing =
+    ParseEasing(aTarget, keyframes.mEasing);
 
   // We ignore easing.mComposite since we don't support composite modes on
   // keyframes yet.
 
   // Get all the property--value-list pairs off the object.
   JS::Rooted<JSObject*> object(aCx, &aValue.toObject());
   nsTArray<PropertyValuesPair> propertyValuesPairs;
   if (!GetPropertyValuesPairs(aCx, object, ListAllowance::eAllow,
@@ -1700,16 +1700,24 @@ KeyframeEffectReadOnly::Constructor(
 
 void
 KeyframeEffectReadOnly::GetFrames(JSContext*& aCx,
                                   nsTArray<JSObject*>& aResult,
                                   ErrorResult& aRv)
 {
   nsTArray<OrderedKeyframeValueEntry> entries;
 
+  // We need a linear function here to sort key frames correctly.
+  // mTimingFunction in AnimationPropertySegment is Nothing() in case of
+  // the timing function is 'linear'. So if the mTimingFunction is
+  // Nothing(), we need a dummy ComputedTimingFunction to be passed to
+  // ComputedTimingFunction::Compare.
+  ComputedTimingFunction linear;
+  linear.Init(nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR));
+
   for (const AnimationProperty& property : mProperties) {
     for (size_t i = 0, n = property.mSegments.Length(); i < n; i++) {
       const AnimationPropertySegment& segment = property.mSegments[i];
 
       // We append the mFromValue for each segment.  If the mToValue
       // differs from the following segment's mFromValue, or if we're on
       // the last segment, then we append the mToValue as well.
       //
@@ -1722,30 +1730,31 @@ KeyframeEffectReadOnly::GetFrames(JSCont
       // filling.  These annotations are used to ensure multiple values for a
       // given property are sorted correctly and that we do not merge Keyframes
       // with different values for the same offset.
 
       OrderedKeyframeValueEntry* entry = entries.AppendElement();
       entry->mProperty = property.mProperty;
       entry->mValue = segment.mFromValue;
       entry->mOffset = segment.mFromKey;
-      entry->mTimingFunction = &segment.mTimingFunction;
+      entry->mTimingFunction = segment.mTimingFunction.ptrOr(&linear);
       entry->mPosition =
         segment.mFromKey == segment.mToKey && segment.mFromKey == 0.0f ?
           ValuePosition::First :
           ValuePosition::Right;
 
       if (i == n - 1 ||
           segment.mToValue != property.mSegments[i + 1].mFromValue) {
         entry = entries.AppendElement();
         entry->mProperty = property.mProperty;
         entry->mValue = segment.mToValue;
         entry->mOffset = segment.mToKey;
-        entry->mTimingFunction =
-          segment.mToKey == 1.0f ? nullptr : &segment.mTimingFunction;
+        entry->mTimingFunction = segment.mToKey == 1.0f ?
+          nullptr :
+          segment.mTimingFunction.ptrOr(&linear);
         entry->mPosition =
           segment.mFromKey == segment.mToKey && segment.mToKey == 1.0f ?
             ValuePosition::Last :
             ValuePosition::Left;
       }
     }
   }
 
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -89,17 +89,17 @@ struct ComputedTiming
   };
   AnimationPhase      mPhase = AnimationPhase::Null;
 };
 
 struct AnimationPropertySegment
 {
   float mFromKey, mToKey;
   StyleAnimationValue mFromValue, mToValue;
-  ComputedTimingFunction mTimingFunction;
+  Maybe<ComputedTimingFunction> mTimingFunction;
 
   bool operator==(const AnimationPropertySegment& aOther) const {
     return mFromKey == aOther.mFromKey &&
            mToKey == aOther.mToKey &&
            mFromValue == aOther.mFromValue &&
            mToValue == aOther.mToValue &&
            mTimingFunction == aOther.mTimingFunction;
   }
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -420,17 +420,17 @@ AddAnimationForProperty(nsIFrame* aFrame
                             animSegment->endState().get_ArrayOfTransformFunction());
     } else if (aProperty.mProperty == eCSSProperty_opacity) {
       animSegment->startState() = segment.mFromValue.GetFloatValue();
       animSegment->endState() = segment.mToValue.GetFloatValue();
     }
 
     animSegment->startPortion() = segment.mFromKey;
     animSegment->endPortion() = segment.mToKey;
-    animSegment->sampleFn() = ToTimingFunction(Some(segment.mTimingFunction));
+    animSegment->sampleFn() = ToTimingFunction(segment.mTimingFunction);
   }
 }
 
 static void
 AddAnimationsForProperty(nsIFrame* aFrame, nsCSSProperty aProperty,
                          AnimationPtrArray& aAnimations,
                          Layer* aLayer, AnimationData& aData,
                          bool aPending)
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -805,12 +805,16 @@ nsAnimationManager::BuildSegment(Infalli
   segment.mToKey = aToKey;
   const nsTimingFunction *tf;
   if (aFromDeclaration &&
       aFromDeclaration->HasProperty(eCSSProperty_animation_timing_function)) {
     tf = &aFromContext->StyleDisplay()->mAnimations[0].GetTimingFunction();
   } else {
     tf = &aAnimation.GetTimingFunction();
   }
-  segment.mTimingFunction.Init(*tf);
+  if (tf->mType != nsTimingFunction::Type::Linear) {
+    ComputedTimingFunction computedTimingFunction;
+    computedTimingFunction.Init(*tf);
+    segment.mTimingFunction = Some(computedTimingFunction);
+  }
 
   return true;
 }
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -60,18 +60,19 @@ ElementPropertyTransition::CurrentValueP
   ComputedTiming computedTiming = GetComputedTiming(&timingToUse);
 
   MOZ_ASSERT(!computedTiming.mProgress.IsNull(),
              "Got a null progress for a fill mode of 'both'");
   MOZ_ASSERT(mProperties.Length() == 1,
              "Should have one animation property for a transition");
   MOZ_ASSERT(mProperties[0].mSegments.Length() == 1,
              "Animation property should have one segment for a transition");
-  return mProperties[0].mSegments[0].mTimingFunction
-         .GetValue(computedTiming.mProgress.Value());
+  return ComputedTimingFunction::GetPortion(
+           mProperties[0].mSegments[0].mTimingFunction,
+           computedTiming.mProgress.Value());
 }
 
 ////////////////////////// CSSTransition ////////////////////////////
 
 JSObject*
 CSSTransition::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return dom::CSSTransitionBinding::Wrap(aCx, this, aGivenProto);
@@ -663,17 +664,21 @@ nsTransitionManager::ConsiderStartingTra
   AnimationProperty& prop = *pt->Properties().AppendElement();
   prop.mProperty = aProperty;
 
   AnimationPropertySegment& segment = *prop.mSegments.AppendElement();
   segment.mFromValue = startValue;
   segment.mToValue = endValue;
   segment.mFromKey = 0;
   segment.mToKey = 1;
-  segment.mTimingFunction.Init(tf);
+  if (tf.mType != nsTimingFunction::Type::Linear) {
+    ComputedTimingFunction computedTimingFunction;
+    computedTimingFunction.Init(tf);
+    segment.mTimingFunction = Some(computedTimingFunction);
+  }
 
   RefPtr<CSSTransition> animation =
     new CSSTransition(mPresContext->Document()->GetScopeObject());
   animation->SetOwningElement(
     OwningElementRef(*aElement, aNewStyleContext->GetPseudoType()));
   animation->SetTimeline(timeline);
   animation->SetCreationSequence(
     mPresContext->RestyleManager()->GetAnimationGeneration());