Bug 1309752 - Part 3: Corresnponds to writing-mode, text-orientation and direction animation for Gecko. r?birtles draft
authorDaisuke Akatsuka <dakatsuka@mozilla.com>
Wed, 26 Apr 2017 17:46:55 +0900
changeset 568590 6fd5b07c042e2d1f10162b5f5cd6c079a0184b1f
parent 568589 26bae8982d640850a51ab0f6f44347cede67a3e9
child 568591 f00929df4c31b8c7e1d2d935046efac477d15a83
push id55908
push userbmo:dakatsuka@mozilla.com
push dateWed, 26 Apr 2017 09:58:37 +0000
reviewersbirtles
bugs1309752
milestone55.0a1
Bug 1309752 - Part 3: Corresnponds to writing-mode, text-orientation and direction animation for Gecko. r?birtles The properties that decide a physical property which is indecated by a logical property can animate discretely. Due to change those properties, physical property might change. In this patch, add a mechanism to animate correct physical property according to changing of writing-mode, text-orientation and direction. MozReview-Commit-ID: 9GpPPSIp33g
dom/animation/KeyframeEffectReadOnly.cpp
dom/animation/KeyframeEffectReadOnly.h
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -320,38 +320,255 @@ KeyframeEffectReadOnly::UpdateProperties
 
 void
 KeyframeEffectReadOnly::UpdateProperties(
   const ServoComputedValuesWithParent& aServoValues)
 {
   DoUpdateProperties(aServoValues);
 }
 
-static void
-PhysicalizeLogicalProperties(nsTArray<AnimationProperty>& aProperties,
-                             nsStyleContext* aStyleContext)
+static void WritingModeStyles(uint8_t& aWritingMode,
+                              uint8_t& aTextOrientation,
+                              uint8_t& aDirection,
+                              nsStyleContext* aStyleContext)
 {
-  uint8_t writingMode = aStyleContext->StyleVisibility()->mWritingMode;
-  uint8_t textOrientation = aStyleContext->StyleVisibility()->mTextOrientation;
-  uint8_t direction = aStyleContext->StyleVisibility()->mDirection;
+  aWritingMode = aStyleContext->StyleVisibility()->mWritingMode;
+  aTextOrientation = aStyleContext->StyleVisibility()->mTextOrientation;
+  aDirection = aStyleContext->StyleVisibility()->mDirection;
+}
+
+static void WritingModeStyles(uint8_t& aWritingMode,
+                              uint8_t& aTextOrientation,
+                              uint8_t& aDirection,
+                              const ServoComputedValuesWithParent& aServoValues)
+{
+  MOZ_ASSERT(false,
+             "WritingModeStyles does not support for stylo yet");
+}
+
+static void
+FindAnimationProperty(const nsCSSPropertyID aProperty,
+                      nsTArray<AnimationProperty>& aProperties,
+                      AnimationProperty*& aResult)
+{
   for (AnimationProperty& property : aProperties) {
-    if (nsCSSProps::PropHasFlags(property.mProperty, CSS_PROPERTY_LOGICAL)) {
-      property.mProperty =
-        nsCSSProps::PhysicalProperty(property.mProperty, writingMode,
-                                     textOrientation, direction);
+    if (property.mProperty == aProperty) {
+      aResult = &property;
+      return;
     }
   }
 }
 
 static void
-PhysicalizeLogicalProperties(nsTArray<AnimationProperty>& aProperties,
-                             const ServoComputedValuesWithParent& aServoValues)
+HoldLogicalProperties(nsTArray<AnimationProperty>& aProperties,
+                      nsTArray<nsCSSPropertyID>& aLogicalProperties)
+{
+  aLogicalProperties.Clear();
+  for (const AnimationProperty& property : aProperties) {
+    if (nsCSSProps::PropHasFlags(property.mProperty, CSS_PROPERTY_LOGICAL)) {
+      aLogicalProperties.AppendElement(property.mProperty);
+    }
+  }
+}
+
+static void GetSegmentAt(const AnimationProperty& aProperty,
+                         const ComputedTiming& aComputedTiming,
+                         AnimationPropertySegment& aResult)
+{
+  // FIXME: Maybe cache the current segment?
+  const AnimationPropertySegment *segment = aProperty.mSegments.Elements(),
+    *segmentEnd = segment + aProperty.mSegments.Length();
+  while (segment->mToKey <= aComputedTiming.mProgress.Value()) {
+    MOZ_ASSERT(segment->mFromKey <= segment->mToKey, "incorrect keys");
+    if ((segment+1) == segmentEnd) {
+      break;
+    }
+    ++segment;
+    MOZ_ASSERT(segment->mFromKey == (segment-1)->mToKey, "incorrect keys");
+  }
+
+  MOZ_ASSERT(segment->mFromKey <= segment->mToKey, "incorrect keys");
+  MOZ_ASSERT(segment >= aProperty.mSegments.Elements() &&
+             size_t(segment - aProperty.mSegments.Elements()) <
+             aProperty.mSegments.Length(),
+             "out of array bounds");
+  aResult = *segment;
+}
+
+void
+KeyframeEffectReadOnly::WritingModeStyleAt(
+                          const AnimationProperty& aProp,
+                          const ComputedTiming& aComputedTiming,
+                          uint8_t& aResult)
+{
+  MOZ_ASSERT(aProp.mProperty == eCSSProperty_writing_mode ||
+             aProp.mProperty == eCSSProperty_text_orientation ||
+             aProp.mProperty == eCSSProperty_direction,
+             "aProp.mProperty should be writing-mode or "
+             "text-orientation or direction");
+
+  AnimationPropertySegment segment;
+  GetSegmentAt(aProp, aComputedTiming, segment);
+  StyleAnimationValue value;
+  CalculateAnimationValue(nullptr, aProp, segment, aComputedTiming, value);
+  aResult = value.GetCSSValueValue()->GetIntValue();
+}
+
+void
+KeyframeEffectReadOnly::WritingModeStylesAt(
+                          const ComputedTiming& aComputedTiming,
+                          uint8_t& aWritingMode,
+                          uint8_t& aTextOrientation,
+                          uint8_t& aDirection)
+{
+  aWritingMode = mCurrentWritingMode;
+  aTextOrientation = mCurrentTextOrientation;
+  aDirection = mCurrentDirection;
+  for (const AnimationProperty& prop : mProperties) {
+    if (prop.mProperty == eCSSProperty_writing_mode) {
+      WritingModeStyleAt(prop, aComputedTiming, aWritingMode);
+    } else if (prop.mProperty == eCSSProperty_text_orientation) {
+      WritingModeStyleAt(prop, aComputedTiming, aTextOrientation);
+    } else if (prop.mProperty == eCSSProperty_direction) {
+      WritingModeStyleAt(prop, aComputedTiming, aDirection);
+    }
+  }
+}
+
+nsCSSPropertyID
+KeyframeEffectReadOnly::PhysicalPropertyAt(
+                          const nsCSSPropertyID aLogicalProperty,
+                          const ComputedTiming& aComputedTiming)
+{
+  MOZ_ASSERT(nsCSSProps::PropHasFlags(aLogicalProperty, CSS_PROPERTY_LOGICAL),
+              "aLogicalProperty should be a logical longhand property");
+  uint8_t writingMode, textOrientation, direction;
+  WritingModeStylesAt(aComputedTiming, writingMode, textOrientation, direction);
+  return nsCSSProps::PhysicalProperty(aLogicalProperty, writingMode,
+                                      textOrientation, direction);
+}
+
+void
+KeyframeEffectReadOnly::CalculateAnimationValue(
+                          const RefPtr<AnimValuesStyleRule> aStyleRule,
+                          const AnimationProperty& aProperty,
+                          const AnimationPropertySegment& aSegment,
+                          const ComputedTiming& aComputedTiming,
+                          StyleAnimationValue& aResult)
 {
-  MOZ_ASSERT(false,
-             "PhysicalizeLogicalProperties does not support for stylo yet");
+   StyleAnimationValue fromValue =
+     CompositeValue(aProperty.mProperty, aStyleRule,
+                    aSegment.mFromValue.mGecko,
+                    aSegment.mFromComposite);
+   StyleAnimationValue toValue =
+     CompositeValue(aProperty.mProperty, aStyleRule,
+                    aSegment.mToValue.mGecko,
+                    aSegment.mToComposite);
+   if (fromValue.IsNull() || toValue.IsNull()) {
+     return;
+   }
+
+  // Iteration composition for accumulate
+  if (mEffectOptions.mIterationComposite ==
+      IterationCompositeOperation::Accumulate &&
+      aComputedTiming.mCurrentIteration > 0) {
+    const AnimationPropertySegment& lastSegment =
+      aProperty.mSegments.LastElement();
+    // FIXME: Bug 1293492: Add a utility function to calculate both of
+    // below StyleAnimationValues.
+    StyleAnimationValue lastValue = lastSegment.mToValue.mGecko.IsNull()
+      ? GetUnderlyingStyle(aProperty.mProperty, aStyleRule)
+      : lastSegment.mToValue.mGecko;
+    fromValue =
+      StyleAnimationValue::Accumulate(aProperty.mProperty,
+                                      lastValue,
+                                      Move(fromValue),
+                                      aComputedTiming.mCurrentIteration);
+    toValue =
+      StyleAnimationValue::Accumulate(aProperty.mProperty,
+                                      lastValue,
+                                      Move(toValue),
+                                      aComputedTiming.mCurrentIteration);
+  }
+
+   // Special handling for zero-length segments
+   if (aSegment.mToKey == aSegment.mFromKey) {
+     aResult =
+       aComputedTiming.mProgress.Value() < 0 ? Move(fromValue) : Move(toValue);
+     return;
+   }
+
+   double positionInSegment =
+     (aComputedTiming.mProgress.Value() - aSegment.mFromKey) /
+     (aSegment.mToKey - aSegment.mFromKey);
+   double valuePosition =
+     ComputedTimingFunction::GetPortion(aSegment.mTimingFunction,
+                                        positionInSegment,
+                                        aComputedTiming.mBeforeFlag);
+
+   MOZ_ASSERT(IsFinite(valuePosition), "Position value should be finite");
+
+  if (!StyleAnimationValue::Interpolate(aProperty.mProperty,
+                                        fromValue, toValue,
+                                        valuePosition, aResult)) {
+    aResult = valuePosition < 0.5 ? Move(fromValue) : Move(toValue);
+  }
+}
+
+void
+KeyframeEffectReadOnly::UpdateLogicalProperties(
+                          nsTArray<AnimationProperty>& aProperties)
+{
+  ComputedTiming computedTiming = GetComputedTiming();
+  uint8_t currentWritingMode, currentTextOrientation, currentDirection;
+  WritingModeStylesAt(computedTiming, currentWritingMode,
+                      currentTextOrientation, currentDirection);
+
+  for (const nsCSSPropertyID logicalID : mLogicalProperties) {
+    AnimationProperty* currentProperty = nullptr;
+    FindAnimationProperty(logicalID, aProperties, currentProperty);
+    if (!currentProperty) {
+      nsCSSPropertyID previousID =
+        nsCSSProps::PhysicalProperty(logicalID,
+                                     mCurrentWritingMode,
+                                     mCurrentTextOrientation,
+                                     mCurrentDirection);
+      FindAnimationProperty(previousID, aProperties, currentProperty);
+    }
+    MOZ_ASSERT(currentProperty, "currentProperty should exist in aProperties");
+
+    currentProperty->mProperty =
+      nsCSSProps::PhysicalProperty(logicalID,
+                                   mCurrentWritingMode,
+                                   mCurrentTextOrientation,
+                                   mCurrentDirection);
+    for (AnimationPropertySegment& segment : currentProperty->mSegments) {
+      ComputedTiming timing(computedTiming);
+      timing.mProgress.SetValue(segment.mFromKey);
+      nsCSSPropertyID fromID =
+        PhysicalPropertyAt(logicalID, timing);
+
+      timing.mProgress.SetValue(segment.mToKey);
+      nsCSSPropertyID toID =
+        PhysicalPropertyAt(logicalID, timing);
+
+      if (fromID != currentProperty->mProperty) {
+        AnimationValue neutral;
+        segment.mFromValue = neutral;
+      }
+      if (toID != currentProperty->mProperty) {
+        AnimationValue neutral;
+        segment.mToValue = neutral;
+      }
+    }
+  }
+
+  mCurrentWritingMode = currentWritingMode;
+  mCurrentTextOrientation = currentTextOrientation;
+  mCurrentDirection = currentDirection;
 }
 
 template<typename StyleType>
 void
 KeyframeEffectReadOnly::DoUpdateProperties(StyleType&& aStyle)
 {
   MOZ_ASSERT_IF(IsPointer<StyleType>::value, aStyle);
 
@@ -362,17 +579,20 @@ KeyframeEffectReadOnly::DoUpdateProperti
                         "Should not be called while processing ComposeStyle()");
   if (mIsComposingStyle) {
     return;
   }
 
   nsTArray<AnimationProperty> properties =
     BuildProperties(Forward<StyleType>(aStyle));
 
-  PhysicalizeLogicalProperties(properties, aStyle);
+  HoldLogicalProperties(properties, mLogicalProperties);
+  WritingModeStyles(mCurrentWritingMode, mCurrentTextOrientation,
+                    mCurrentDirection, aStyle);
+  UpdateLogicalProperties(properties);
 
   // We need to update base styles even if any properties are not changed at all
   // since base styles might have been changed due to parent style changes, etc.
   EnsureBaseStyles(aStyle, properties);
 
   if (mProperties == properties) {
     return;
   }
@@ -525,16 +745,33 @@ KeyframeEffectReadOnly::EnsureBaseStyle(
   DebugOnly<bool> success =
     StyleAnimationValue::ExtractComputedValue(aProperty,
                                               aCachedBaseStyleContext,
                                               result);
 
   MOZ_ASSERT(success, "Should be able to extract computed animation value");
   MOZ_ASSERT(!result.IsNull(), "Should have a valid StyleAnimationValue");
 
+  // Special handling for writnig-mode, text-orientation and direction.
+  // We need valid value for those to calculate logical properties.
+  // However, ExtractComputedValue might return 'unset' value,
+  // if the keyframe is misisng.
+  // Therefor, get and set the valid value from nsStyleContext directory.
+  if (aProperty == eCSSProperty_writing_mode) {
+    uint8_t value = aCachedBaseStyleContext->StyleVisibility()->mWritingMode;
+    result.GetCSSValueValue()->SetIntValue(value, eCSSUnit_Enumerated);
+  } else if (aProperty == eCSSProperty_text_orientation) {
+    uint8_t value =
+      aCachedBaseStyleContext->StyleVisibility()->mTextOrientation;
+    result.GetCSSValueValue()->SetIntValue(value, eCSSUnit_Enumerated);
+  } else if (aProperty == eCSSProperty_direction) {
+    uint8_t value = aCachedBaseStyleContext->StyleVisibility()->mDirection;
+    result.GetCSSValueValue()->SetIntValue(value, eCSSUnit_Enumerated);
+  }
+
   mBaseStyleValues.Put(aProperty, result);
 }
 
 void
 KeyframeEffectReadOnly::EnsureBaseStyles(
   const ServoComputedValuesWithParent& aServoValues,
   const nsTArray<AnimationProperty>& aProperties)
 {
@@ -595,96 +832,53 @@ KeyframeEffectReadOnly::EnsureBaseStyle(
 }
 
 void
 KeyframeEffectReadOnly::WillComposeStyle()
 {
   ComputedTiming computedTiming = GetComputedTiming();
   mProgressOnLastCompose = computedTiming.mProgress;
   mCurrentIterationOnLastCompose = computedTiming.mCurrentIteration;
+
+  if (mLogicalProperties.IsEmpty()) {
+    return;
+  }
+
+  uint8_t currentWritingMode, currentTextOrientation, currentDirection;
+  WritingModeStylesAt(computedTiming, currentWritingMode,
+                      currentTextOrientation, currentDirection);
+  if (mCurrentWritingMode == currentWritingMode &&
+      mCurrentTextOrientation == currentTextOrientation &&
+      mCurrentDirection == currentDirection) {
+    return;
+  }
+
+  UpdateLogicalProperties(mProperties);
 }
 
 void
 KeyframeEffectReadOnly::ComposeStyleRule(
   RefPtr<AnimValuesStyleRule>& aStyleRule,
   const AnimationProperty& aProperty,
   const AnimationPropertySegment& aSegment,
   const ComputedTiming& aComputedTiming)
 {
-  StyleAnimationValue fromValue =
-    CompositeValue(aProperty.mProperty, aStyleRule,
-                   aSegment.mFromValue.mGecko,
-                   aSegment.mFromComposite);
-  StyleAnimationValue toValue =
-    CompositeValue(aProperty.mProperty, aStyleRule,
-                   aSegment.mToValue.mGecko,
-                   aSegment.mToComposite);
-  if (fromValue.IsNull() || toValue.IsNull()) {
+  StyleAnimationValue animationValue;
+  CalculateAnimationValue(aStyleRule, aProperty, aSegment,
+                          aComputedTiming, animationValue);
+
+  if (animationValue.IsNull()) {
     return;
   }
 
   if (!aStyleRule) {
     // Allocate the style rule now that we know we have animation data.
     aStyleRule = new AnimValuesStyleRule();
   }
-
-  // Iteration composition for accumulate
-  if (mEffectOptions.mIterationComposite ==
-      IterationCompositeOperation::Accumulate &&
-      aComputedTiming.mCurrentIteration > 0) {
-    const AnimationPropertySegment& lastSegment =
-      aProperty.mSegments.LastElement();
-    // FIXME: Bug 1293492: Add a utility function to calculate both of
-    // below StyleAnimationValues.
-    StyleAnimationValue lastValue = lastSegment.mToValue.mGecko.IsNull()
-      ? GetUnderlyingStyle(aProperty.mProperty, aStyleRule)
-      : lastSegment.mToValue.mGecko;
-    fromValue =
-      StyleAnimationValue::Accumulate(aProperty.mProperty,
-                                      lastValue,
-                                      Move(fromValue),
-                                      aComputedTiming.mCurrentIteration);
-    toValue =
-      StyleAnimationValue::Accumulate(aProperty.mProperty,
-                                      lastValue,
-                                      Move(toValue),
-                                      aComputedTiming.mCurrentIteration);
-  }
-
-  // Special handling for zero-length segments
-  if (aSegment.mToKey == aSegment.mFromKey) {
-    if (aComputedTiming.mProgress.Value() < 0) {
-      aStyleRule->AddValue(aProperty.mProperty, Move(fromValue));
-    } else {
-      aStyleRule->AddValue(aProperty.mProperty, Move(toValue));
-    }
-    return;
-  }
-
-  double positionInSegment =
-    (aComputedTiming.mProgress.Value() - aSegment.mFromKey) /
-    (aSegment.mToKey - aSegment.mFromKey);
-  double valuePosition =
-    ComputedTimingFunction::GetPortion(aSegment.mTimingFunction,
-                                       positionInSegment,
-                                       aComputedTiming.mBeforeFlag);
-
-  MOZ_ASSERT(IsFinite(valuePosition), "Position value should be finite");
-
-  StyleAnimationValue val;
-  if (StyleAnimationValue::Interpolate(aProperty.mProperty,
-                                       fromValue,
-                                       toValue,
-                                       valuePosition, val)) {
-    aStyleRule->AddValue(aProperty.mProperty, Move(val));
-  } else if (valuePosition < 0.5) {
-    aStyleRule->AddValue(aProperty.mProperty, Move(fromValue));
-  } else {
-    aStyleRule->AddValue(aProperty.mProperty, Move(toValue));
-  }
+  aStyleRule->AddValue(aProperty.mProperty, Move(animationValue));
 }
 
 // Bug 1333311 - We use two branches for Gecko and Stylo. However, it's
 // better to remove the duplicated code.
 void
 KeyframeEffectReadOnly::ComposeStyleRule(
   const RawServoAnimationValueMap& aAnimationValues,
   const AnimationProperty& aProperty,
--- a/dom/animation/KeyframeEffectReadOnly.h
+++ b/dom/animation/KeyframeEffectReadOnly.h
@@ -458,14 +458,39 @@ private:
     const nsIFrame* aFrame,
     AnimationPerformanceWarning::Type& aPerformanceWarning);
   static bool IsGeometricProperty(const nsCSSPropertyID aProperty);
 
   static const TimeDuration OverflowRegionRefreshInterval();
 
   // FIXME: This flag will be removed in bug 1324966.
   bool mIsComposingStyle = false;
+
+  nsTArray<nsCSSPropertyID> mLogicalProperties;
+  uint8_t mCurrentWritingMode;
+  uint8_t mCurrentTextOrientation;
+  uint8_t mCurrentDirection;
+
+  void CalculateAnimationValue(const RefPtr<AnimValuesStyleRule> aStyleRule,
+                               const AnimationProperty& aProperty,
+                               const AnimationPropertySegment& aSegment,
+                               const ComputedTiming& aComputedTiming,
+                               StyleAnimationValue& aResult);
+
+  void WritingModeStylesAt(const ComputedTiming& aComputedTiming,
+                           uint8_t& aWritingMode,
+                           uint8_t& aTextOrientation,
+                           uint8_t& aDirection);
+
+  void WritingModeStyleAt(const AnimationProperty& aProperty,
+                          const ComputedTiming& aComputedTiming,
+                          uint8_t& aResult);
+
+  nsCSSPropertyID PhysicalPropertyAt(const nsCSSPropertyID aLogicalProperty,
+                                     const ComputedTiming& aComputedTiming);
+
+  void UpdateLogicalProperties(nsTArray<AnimationProperty>& aProperties);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_KeyframeEffectReadOnly_h