Bug 1354947 - Fill in missing keyframe values. r?birtles draft
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Sat, 13 May 2017 16:34:38 +0900
changeset 577321 f9b43acf73fcf3c3a1415990349357a5cb93bfd0
parent 577320 5f5bccab920952be291ffc0b1e28892f216652d4
child 577322 9ab5850523c7d11da908411572b41fc3674d6f85
push id58655
push userhikezoe@mozilla.com
push dateSat, 13 May 2017 07:47:48 +0000
reviewersbirtles
bugs1354947
milestone55.0a1
Bug 1354947 - Fill in missing keyframe values. r?birtles This is mostly a mimic of what we do in GeckoCSSAnimationBuilder::FillInMissingKeyframeValues(). In Gecko we iterate over the properties just once because we can take the index for both the synthesized start and end keyframe and easily look them up as needed. However, in this patch we synthesize the start and end keyframes separately and iterate over the properties twice because that's easier than getting two indices and then later calling another FFI to dereference each of them, and neater than getting back two pointers MozReview-Commit-ID: 1e0R9AKzgaG
js/src/devtools/rootAnalysis/analyzeHeapWrites.js
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
--- a/js/src/devtools/rootAnalysis/analyzeHeapWrites.js
+++ b/js/src/devtools/rootAnalysis/analyzeHeapWrites.js
@@ -189,16 +189,18 @@ function treatAsSafeArgument(entry, varN
         ["Gecko_ClearPODTArray", "aArray", null],
         ["Gecko_ClearAndResizeStyleContents", "aContent", null],
         [/Gecko_ClearAndResizeCounter/, "aContent", null],
         [/Gecko_CopyCounter.*?From/, "aContent", null],
         [/Gecko_SetContentData/, "aContent", null],
         [/Gecko_EnsureStyle.*?ArrayLength/, "aArray", null],
         ["Gecko_AnimationAppendKeyframe", "aKeyframes", null],
         ["Gecko_GetOrCreateKeyframeAtStart", "aKeyframes", null],
+        ["Gecko_GetOrCreateInitialKeyframe", "aKeyframes", null],
+        ["Gecko_GetOrCreateFinalKeyframe", "aKeyframes", null],
         ["Gecko_SetStyleCoordCalcValue", null, null],
         ["Gecko_StyleClipPath_SetURLValue", "aClip", null],
         ["Gecko_nsStyleFilter_SetURLValue", "aEffects", null],
         ["Gecko_nsStyleSVGPaint_CopyFrom", "aDest", null],
         ["Gecko_nsStyleSVGPaint_SetURLValue", "aPaint", null],
         ["Gecko_nsStyleSVGPaint_Reset", "aPaint", null],
         ["Gecko_nsStyleSVG_SetDashArrayLength", "aSvg", null],
         ["Gecko_nsStyleSVG_CopyDashArray", "aDst", null],
@@ -371,16 +373,18 @@ function ignoreContents(entry)
         /nsCSSProps::LookupPropertyValue/,
         /nsCSSProps::ValueToKeyword/,
         /nsCSSKeywords::GetStringValue/,
 
         // The analysis can't cope with the indirection used for the objects
         // being initialized here.
         "Gecko_AnimationAppendKeyframe",
         "Gecko_GetOrCreateKeyframeAtStart",
+        "Gecko_GetOrCreateInitialKeyframe",
+        "Gecko_GetOrCreateFinalKeyframe",
         "Gecko_NewStyleQuoteValues",
         "Gecko_NewCSSValueSharedList",
         "Gecko_NewGridTemplateAreasValue",
         /nsCSSValue::SetCalcValue/,
         /CSSValueSerializeCalcOps::Append/,
         "Gecko_CSSValue_SetFunction",
         "Gecko_CSSValue_SetArray",
         "Gecko_EnsureMozBorderColors",
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -1463,48 +1463,112 @@ Gecko_AnimationAppendKeyframe(RawGeckoKe
       aTimingFunction->mType != nsTimingFunction::Type::Linear) {
     keyframe->mTimingFunction.emplace();
     keyframe->mTimingFunction->Init(*aTimingFunction);
   }
 
   return keyframe;
 }
 
-Keyframe*
-Gecko_GetOrCreateKeyframeAtStart(nsTArray<Keyframe>* aKeyframes,
-                                 float aOffset,
-                                 const nsTimingFunction* aTimingFunction)
+enum class KeyframeSearchDirection {
+  Forwards,
+  Backwards,
+};
+
+enum class KeyframeInsertPosition {
+  Prepend,
+  LastForOffset,
+};
+
+static Keyframe*
+GetOrCreateKeyframe(nsTArray<Keyframe>* aKeyframes,
+                    float aOffset,
+                    const nsTimingFunction* aTimingFunction,
+                    KeyframeSearchDirection aSearchDirection,
+                    KeyframeInsertPosition aInsertPosition)
 {
   MOZ_ASSERT(aKeyframes, "The keyframe array should be valid");
   MOZ_ASSERT(aTimingFunction, "The timing function should be valid");
   MOZ_ASSERT(aOffset >= 0. && aOffset <= 1.,
              "The offset should be in the range of [0.0, 1.0]");
-  MOZ_ASSERT(aKeyframes->IsEmpty() ||
-             aKeyframes->ElementAt(0).mOffset.value() >= aOffset,
-             "The offset should be less than or equal to the first keyframe's "
-             "offset if there are exisiting keyframes");
 
   size_t keyframeIndex;
-  if (nsAnimationManager::FindMatchingKeyframe(*aKeyframes,
-                                               aOffset,
-                                               *aTimingFunction,
-                                               keyframeIndex)) {
-    return &(*aKeyframes)[keyframeIndex];
+  switch (aSearchDirection) {
+    case KeyframeSearchDirection::Forwards:
+      if (nsAnimationManager::FindMatchingKeyframe(*aKeyframes,
+                                                   aOffset,
+                                                   *aTimingFunction,
+                                                   keyframeIndex)) {
+        return &(*aKeyframes)[keyframeIndex];
+      }
+      break;
+    case KeyframeSearchDirection::Backwards:
+      if (nsAnimationManager::FindMatchingKeyframe(Reversed(*aKeyframes),
+                                                   aOffset,
+                                                   *aTimingFunction,
+                                                   keyframeIndex)) {
+        return &(*aKeyframes)[aKeyframes->Length() - 1 - keyframeIndex];
+      }
+      keyframeIndex = aKeyframes->Length() - 1;
+      break;
   }
 
-  Keyframe* keyframe = aKeyframes->InsertElementAt(0);
+  Keyframe* keyframe =
+    aKeyframes->InsertElementAt(
+      aInsertPosition == KeyframeInsertPosition::Prepend
+                         ? 0
+                         : keyframeIndex);
   keyframe->mOffset.emplace(aOffset);
   if (aTimingFunction->mType != nsTimingFunction::Type::Linear) {
     keyframe->mTimingFunction.emplace();
     keyframe->mTimingFunction->Init(*aTimingFunction);
   }
 
   return keyframe;
 }
 
+Keyframe*
+Gecko_GetOrCreateKeyframeAtStart(nsTArray<Keyframe>* aKeyframes,
+                                 float aOffset,
+                                 const nsTimingFunction* aTimingFunction)
+{
+  MOZ_ASSERT(aKeyframes->IsEmpty() ||
+             aKeyframes->ElementAt(0).mOffset.value() >= aOffset,
+             "The offset should be less than or equal to the first keyframe's "
+             "offset if there are exisiting keyframes");
+
+  return GetOrCreateKeyframe(aKeyframes,
+                             aOffset,
+                             aTimingFunction,
+                             KeyframeSearchDirection::Forwards,
+                             KeyframeInsertPosition::Prepend);
+}
+
+Keyframe*
+Gecko_GetOrCreateInitialKeyframe(nsTArray<Keyframe>* aKeyframes,
+                                 const nsTimingFunction* aTimingFunction)
+{
+  return GetOrCreateKeyframe(aKeyframes,
+                             0.,
+                             aTimingFunction,
+                             KeyframeSearchDirection::Forwards,
+                             KeyframeInsertPosition::LastForOffset);
+}
+
+Keyframe*
+Gecko_GetOrCreateFinalKeyframe(nsTArray<Keyframe>* aKeyframes,
+                               const nsTimingFunction* aTimingFunction)
+{
+  return GetOrCreateKeyframe(aKeyframes,
+                             1.,
+                             aTimingFunction,
+                             KeyframeSearchDirection::Backwards,
+                             KeyframeInsertPosition::LastForOffset);
+}
+
 void
 Gecko_ResetStyleCoord(nsStyleUnit* aUnit, nsStyleUnion* aValue)
 {
   nsStyleCoord::Reset(*aUnit, *aValue);
 }
 
 void
 Gecko_SetStyleCoordCalcValue(nsStyleUnit* aUnit, nsStyleUnion* aValue, nsStyleCoord::CalcValue aCalc)
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -387,16 +387,33 @@ mozilla::Keyframe* Gecko_AnimationAppend
 //                        Keyframe is found, to set on the created Keyframe.
 //
 // @returns  The matching or created Keyframe.
 mozilla::Keyframe* Gecko_GetOrCreateKeyframeAtStart(
   RawGeckoKeyframeListBorrowedMut keyframes,
   float offset,
   const nsTimingFunction* timingFunction);
 
+// As with Gecko_GetOrCreateKeyframeAtStart except that this method will search
+// from the beginning of |keyframes| for a Keyframe with matching timing
+// function and an offset of 0.0.
+// Furthermore, if a matching Keyframe is not found, a new Keyframe will be
+// inserted after the *last* Keyframe in |keyframes| with offset 0.0.
+mozilla::Keyframe* Gecko_GetOrCreateInitialKeyframe(
+  RawGeckoKeyframeListBorrowedMut keyframes,
+  const nsTimingFunction* timingFunction);
+
+// As with Gecko_GetOrCreateKeyframeAtStart except that this method will search
+// from the *end* of |keyframes| for a Keyframe with matching timing function
+// and an offset of 1.0. If a matching Keyframe is not found, a new Keyframe
+// will be appended to the end of |keyframes|.
+mozilla::Keyframe* Gecko_GetOrCreateFinalKeyframe(
+  RawGeckoKeyframeListBorrowedMut keyframes,
+  const nsTimingFunction* timingFunction);
+
 // Clean up pointer-based coordinates
 void Gecko_ResetStyleCoord(nsStyleUnit* unit, nsStyleUnion* value);
 
 // Set an nsStyleCoord to a computed `calc()` value
 void Gecko_SetStyleCoordCalcValue(nsStyleUnit* unit, nsStyleUnion* value, nsStyleCoord::CalcValue calc);
 
 void Gecko_CopyShapeSourceFrom(mozilla::StyleShapeSource* dst, const mozilla::StyleShapeSource* src);