Index: Source/core/animation/LengthInterpolationType.cpp |
diff --git a/Source/core/animation/LengthInterpolationType.cpp b/Source/core/animation/LengthInterpolationType.cpp |
index a872f1de65dbf55429c1c549c8cc90fd829c380e..21214df6aaa246417095aa397b9a21fc56ef643f 100644 |
--- a/Source/core/animation/LengthInterpolationType.cpp |
+++ b/Source/core/animation/LengthInterpolationType.cpp |
@@ -6,30 +6,55 @@ |
#include "core/animation/LengthInterpolationType.h" |
#include "core/animation/LengthPropertyFunctions.h" |
-#include "core/animation/LengthStyleInterpolation.h" |
+#include "core/animation/css/CSSAnimatableValueFactory.h" |
+#include "core/css/CSSCalculationValue.h" |
+#include "core/css/resolver/StyleBuilder.h" |
#include "core/css/resolver/StyleResolverState.h" |
namespace blink { |
+// This class is implemented as a singleton whose instance represents the presence of percentages being used in a Length value |
+// while nullptr represents the absence of any percentages. |
+class LengthNonInterpolableValue : public NonInterpolableValue { |
+public: |
+ ~LengthNonInterpolableValue() override { ASSERT_NOT_REACHED(); } |
+ static PassRefPtr<LengthNonInterpolableValue> create(bool hasPercentage) |
+ { |
+ DEFINE_STATIC_REF_WILL_BE_PERSISTENT(LengthNonInterpolableValue, singleton, adoptRef(new LengthNonInterpolableValue())); |
+ ASSERT(singleton); |
+ return hasPercentage ? singleton : nullptr; |
+ } |
+ static PassRefPtr<LengthNonInterpolableValue> merge(const NonInterpolableValue* a, const NonInterpolableValue* b) |
+ { |
+ return create(hasPercentage(a) || hasPercentage(b)); |
+ } |
+ static bool hasPercentage(const NonInterpolableValue* nonInterpolableValue) |
+ { |
+ ASSERT(!nonInterpolableValue || nonInterpolableValue->type() == LengthNonInterpolableValue::staticType); |
+ return static_cast<bool>(nonInterpolableValue); |
+ } |
+ DEFINE_INLINE_VIRTUAL_TRACE() { NonInterpolableValue::trace(visitor); } |
+ DECLARE_NON_INTERPOLABLE_VALUE_TYPE(); |
+ |
+private: |
+ LengthNonInterpolableValue() { } |
+}; |
+ |
+DEFINE_NON_INTERPOLABLE_VALUE_TYPE(LengthNonInterpolableValue); |
+DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(LengthNonInterpolableValue); |
+ |
LengthInterpolationType::LengthInterpolationType(CSSPropertyID property) |
: InterpolationType(property) |
, m_valueRange(LengthPropertyFunctions::valueRange(property)) |
{ } |
-static PassOwnPtr<InterpolableList> createNeutralValue() |
+static PassOwnPtr<InterpolableList> createNeutralInterpolableValue() |
{ |
- OwnPtr<InterpolableList> listOfValuesAndTypes = InterpolableList::create(2); |
const size_t length = CSSPrimitiveValue::LengthUnitTypeCount; |
- OwnPtr<InterpolableList> listOfValues = InterpolableList::create(length); |
- // TODO(alancutter): Use a NonInterpolableValue to represent the list of types. |
- OwnPtr<InterpolableList> listOfTypes = InterpolableList::create(length); |
- for (size_t i = 0; i < length; i++) { |
- listOfValues->set(i, InterpolableNumber::create(0)); |
- listOfTypes->set(i, InterpolableNumber::create(0)); |
- } |
- listOfValuesAndTypes->set(0, listOfValues.release()); |
- listOfValuesAndTypes->set(1, listOfTypes.release()); |
- return listOfValuesAndTypes.release(); |
+ OwnPtr<InterpolableList> values = InterpolableList::create(length); |
+ for (size_t i = 0; i < length; i++) |
+ values->set(i, InterpolableNumber::create(0)); |
+ return values.release(); |
} |
float LengthInterpolationType::effectiveZoom(const ComputedStyle& style) const |
@@ -43,17 +68,11 @@ PassOwnPtr<InterpolationValue> LengthInterpolationType::maybeConvertLength(const |
return nullptr; |
PixelsAndPercent pixelsAndPercent = length.pixelsAndPercent(); |
- OwnPtr<InterpolableList> valuesAndTypes = createNeutralValue(); |
- |
- InterpolableList& values = toInterpolableList(*valuesAndTypes->get(0)); |
- values.set(CSSPrimitiveValue::UnitTypePixels, InterpolableNumber::create(pixelsAndPercent.pixels / zoom)); |
- values.set(CSSPrimitiveValue::UnitTypePercentage, InterpolableNumber::create(pixelsAndPercent.percent)); |
+ OwnPtr<InterpolableList> values = createNeutralInterpolableValue(); |
+ values->set(CSSPrimitiveValue::UnitTypePixels, InterpolableNumber::create(pixelsAndPercent.pixels / zoom)); |
+ values->set(CSSPrimitiveValue::UnitTypePercentage, InterpolableNumber::create(pixelsAndPercent.percent)); |
- InterpolableList& types = toInterpolableList(*valuesAndTypes->get(1)); |
- types.set(CSSPrimitiveValue::UnitTypePixels, InterpolableNumber::create(pixelsAndPercent.pixels != 0)); |
- types.set(CSSPrimitiveValue::UnitTypePercentage, InterpolableNumber::create(length.hasPercent())); |
- |
- return InterpolationValue::create(*this, valuesAndTypes.release()); |
+ return InterpolationValue::create(*this, values.release(), LengthNonInterpolableValue::create(length.hasPercent())); |
} |
class ParentLengthChecker : public InterpolationType::ConversionChecker { |
@@ -83,7 +102,7 @@ private: |
PassOwnPtr<InterpolationValue> LengthInterpolationType::maybeConvertNeutral() const |
{ |
- return InterpolationValue::create(*this, createNeutralValue()); |
+ return InterpolationValue::create(*this, createNeutralInterpolableValue()); |
} |
PassOwnPtr<InterpolationValue> LengthInterpolationType::maybeConvertInitial() const |
@@ -112,38 +131,31 @@ PassOwnPtr<InterpolationValue> LengthInterpolationType::maybeConvertValue(const |
const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value); |
- OwnPtr<InterpolableList> listOfValuesAndTypes = InterpolableList::create(2); |
- OwnPtr<InterpolableList> listOfValues = InterpolableList::create(CSSPrimitiveValue::LengthUnitTypeCount); |
- OwnPtr<InterpolableList> listOfTypes = InterpolableList::create(CSSPrimitiveValue::LengthUnitTypeCount); |
- |
- CSSLengthArray arrayOfValues; |
- CSSLengthTypeArray arrayOfTypes; |
+ CSSLengthArray valueArray; |
for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) |
- arrayOfValues.append(0); |
- arrayOfTypes.ensureSize(CSSPrimitiveValue::LengthUnitTypeCount); |
+ valueArray.append(0); |
+ bool hasPercentage = false; |
if (primitiveValue.isValueID()) { |
CSSValueID valueID = primitiveValue.getValueID(); |
double pixels; |
if (!LengthPropertyFunctions::getPixelsForKeyword(m_property, valueID, pixels)) |
return nullptr; |
- arrayOfTypes.set(CSSPrimitiveValue::UnitTypePixels); |
- arrayOfValues[CSSPrimitiveValue::UnitTypePixels] = pixels; |
+ valueArray[CSSPrimitiveValue::UnitTypePixels] = pixels; |
} else { |
if (!primitiveValue.isLength() && !primitiveValue.isPercentage() && !primitiveValue.isCalculatedPercentageWithLength()) |
return nullptr; |
- primitiveValue.accumulateLengthArray(arrayOfValues, arrayOfTypes); |
+ CSSLengthTypeArray hasType; |
+ hasType.ensureSize(CSSPrimitiveValue::LengthUnitTypeCount); |
+ primitiveValue.accumulateLengthArray(valueArray, hasType); |
+ hasPercentage = hasType.get(CSSPrimitiveValue::UnitTypePercentage); |
} |
- for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) { |
- listOfValues->set(i, InterpolableNumber::create(arrayOfValues.at(i))); |
- listOfTypes->set(i, InterpolableNumber::create(arrayOfTypes.get(i))); |
- } |
- |
- listOfValuesAndTypes->set(0, listOfValues.release()); |
- listOfValuesAndTypes->set(1, listOfTypes.release()); |
+ OwnPtr<InterpolableList> values = InterpolableList::create(CSSPrimitiveValue::LengthUnitTypeCount); |
+ for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) |
+ values->set(i, InterpolableNumber::create(valueArray.at(i))); |
- return InterpolationValue::create(*this, listOfValuesAndTypes.release()); |
+ return InterpolationValue::create(*this, values.release(), LengthNonInterpolableValue::create(hasPercentage)); |
} |
PassOwnPtr<InterpolationValue> LengthInterpolationType::maybeConvertUnderlyingValue(const StyleResolverState& state) const |
@@ -154,12 +166,113 @@ PassOwnPtr<InterpolationValue> LengthInterpolationType::maybeConvertUnderlyingVa |
return maybeConvertLength(underlyingLength, effectiveZoom(*state.style())); |
} |
-void LengthInterpolationType::apply(const InterpolableValue& interpolableValue, const NonInterpolableValue*, StyleResolverState& state) const |
+PassOwnPtr<PairwisePrimitiveInterpolation> LengthInterpolationType::mergeSingleConversions(InterpolationValue& startValue, InterpolationValue& endValue) const |
+{ |
+ return PairwisePrimitiveInterpolation::create(*this, |
+ startValue.mutableComponent().interpolableValue.release(), |
+ endValue.mutableComponent().interpolableValue.release(), |
+ LengthNonInterpolableValue::merge(startValue.nonInterpolableValue(), endValue.nonInterpolableValue())); |
+} |
+ |
+void LengthInterpolationType::composite(UnderlyingValue& underlyingValue, double underlyingFraction, const InterpolationValue& value) const |
{ |
- // TODO(alancutter): Make all length interpolation functions operate on ValueRanges instead of InterpolationRanges. |
- InterpolationRange range = m_valueRange == ValueRangeNonNegative ? RangeNonNegative : RangeAll; |
- // TODO(alancutter): Set arbitrary property Lengths on ComputedStyle without using cross compilation unit member function getters (Windows runtime doesn't like it). |
- LengthStyleInterpolation::applyInterpolableValue(m_property, interpolableValue, range, state); |
+ InterpolationComponentValue& underlyingComponent = underlyingValue.mutableComponent(); |
+ underlyingComponent.interpolableValue->scaleAndAdd(underlyingFraction, value.interpolableValue()); |
+ underlyingComponent.nonInterpolableValue = LengthNonInterpolableValue::merge(underlyingValue->nonInterpolableValue(), value.nonInterpolableValue()); |
+} |
+ |
+static bool isPixelsOrPercentOnly(const InterpolableList& values) |
+{ |
+ for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) { |
+ if (i == CSSPrimitiveValue::UnitTypePixels || i == CSSPrimitiveValue::UnitTypePercentage) |
+ continue; |
+ if (toInterpolableNumber(values.get(i))->value()) |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+// TODO(alancutter): Move this to Length.h. |
+static double clampToRange(double x, ValueRange range) |
+{ |
+ return (range == ValueRangeNonNegative && x < 0) ? 0 : x; |
+} |
+ |
+static Length createLength(const InterpolableList& values, bool hasPercentage, ValueRange range, double zoom) |
+{ |
+ ASSERT(isPixelsOrPercentOnly(values)); |
+ double pixels = toInterpolableNumber(values.get(CSSPrimitiveValue::UnitTypePixels))->value() * zoom; |
+ double percentage = toInterpolableNumber(values.get(CSSPrimitiveValue::UnitTypePercentage))->value(); |
+ ASSERT(hasPercentage || percentage == 0); |
+ |
+ if (pixels && hasPercentage) |
+ return Length(CalculationValue::create(PixelsAndPercent(pixels, percentage), range)); |
+ if (hasPercentage) |
+ return Length(clampToRange(percentage, range), Percent); |
+ return Length(CSSPrimitiveValue::clampToCSSLengthRange(clampToRange(pixels, range)), Fixed); |
+} |
+ |
+static CSSPrimitiveValue::UnitType toUnitType(int lengthUnitType) |
+{ |
+ return static_cast<CSSPrimitiveValue::UnitType>(CSSPrimitiveValue::lengthUnitTypeToUnitType(static_cast<CSSPrimitiveValue::LengthUnitType>(lengthUnitType))); |
+} |
+ |
+static PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> createCalcExpression(const InterpolableList& values, bool hasPercentage) |
+{ |
+ RefPtrWillBeRawPtr<CSSCalcExpressionNode> result = nullptr; |
+ for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) { |
+ double value = toInterpolableNumber(values.get(i))->value(); |
+ if (value || (i == CSSPrimitiveValue::UnitTypePercentage && hasPercentage)) { |
+ RefPtrWillBeRawPtr<CSSCalcExpressionNode> node = CSSCalcValue::createExpressionNode(CSSPrimitiveValue::create(value, toUnitType(i))); |
+ result = result ? CSSCalcValue::createExpressionNode(result.release(), node.release(), CalcAdd) : node.release(); |
+ } |
+ } |
+ ASSERT(result); |
+ return result.release(); |
+} |
+ |
+static PassRefPtrWillBeRawPtr<CSSValue> createCSSValue(const InterpolableList& values, bool hasPercentage, ValueRange range) |
+{ |
+ RefPtrWillBeRawPtr<CSSPrimitiveValue> result; |
+ size_t firstUnitIndex = CSSPrimitiveValue::LengthUnitTypeCount; |
+ size_t unitTypeCount = 0; |
+ for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) { |
+ if ((hasPercentage && i == CSSPrimitiveValue::UnitTypePercentage) || toInterpolableNumber(values.get(i))->value()) { |
+ unitTypeCount++; |
+ if (unitTypeCount == 1) |
+ firstUnitIndex = i; |
+ } |
+ } |
+ switch (unitTypeCount) { |
+ case 0: |
+ return CSSPrimitiveValue::create(0, CSSPrimitiveValue::UnitType::Pixels); |
+ case 1: { |
+ double value = clampToRange(toInterpolableNumber(values.get(firstUnitIndex))->value(), range); |
+ return CSSPrimitiveValue::create(value, toUnitType(firstUnitIndex)); |
+ } |
+ default: |
+ return CSSPrimitiveValue::create(CSSCalcValue::create(createCalcExpression(values, hasPercentage), range)); |
+ } |
+} |
+ |
+void LengthInterpolationType::apply(const InterpolableValue& interpolableValue, const NonInterpolableValue* nonInterpolableValue, StyleResolverState& state) const |
+{ |
+ const InterpolableList& values = toInterpolableList(interpolableValue); |
+ bool hasPercentage = LengthNonInterpolableValue::hasPercentage(nonInterpolableValue); |
+ if (isPixelsOrPercentOnly(values)) { |
+ Length length = createLength(values, hasPercentage, m_valueRange, effectiveZoom(*state.style())); |
+ if (LengthPropertyFunctions::setLength(m_property, *state.style(), length)) { |
+#if ENABLE(ASSERT) |
+ // Assert that setting the length on ComputedStyle directly is identical to the AnimatableValue code path. |
+ RefPtr<AnimatableValue> before = CSSAnimatableValueFactory::create(m_property, *state.style()); |
+ StyleBuilder::applyProperty(m_property, state, createCSSValue(values, hasPercentage, m_valueRange).get()); |
+ RefPtr<AnimatableValue> after = CSSAnimatableValueFactory::create(m_property, *state.style()); |
+ ASSERT(before->equals(*after)); |
+#endif |
+ return; |
+ } |
+ } |
+ StyleBuilder::applyProperty(m_property, state, createCSSValue(values, hasPercentage, m_valueRange).get()); |
} |
} // namespace blink |