| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "core/animation/CSSLengthInterpolationType.h" | 5 #include "core/animation/CSSLengthInterpolationType.h" |
| 6 | 6 |
| 7 #include "core/animation/LengthInterpolationFunctions.h" |
| 7 #include "core/animation/LengthPropertyFunctions.h" | 8 #include "core/animation/LengthPropertyFunctions.h" |
| 8 #include "core/animation/css/CSSAnimatableValueFactory.h" | 9 #include "core/animation/css/CSSAnimatableValueFactory.h" |
| 9 #include "core/css/CSSCalculationValue.h" | 10 #include "core/css/CSSCalculationValue.h" |
| 10 #include "core/css/resolver/StyleBuilder.h" | 11 #include "core/css/resolver/StyleBuilder.h" |
| 11 #include "core/css/resolver/StyleResolverState.h" | 12 #include "core/css/resolver/StyleResolverState.h" |
| 12 #include "wtf/PtrUtil.h" | 13 #include "wtf/PtrUtil.h" |
| 13 #include <memory> | 14 #include <memory> |
| 14 | 15 |
| 15 namespace blink { | 16 namespace blink { |
| 16 | 17 |
| 17 // This class is implemented as a singleton whose instance represents the presen
ce of percentages being used in a Length value | |
| 18 // while nullptr represents the absence of any percentages. | |
| 19 class CSSLengthNonInterpolableValue : public NonInterpolableValue { | |
| 20 public: | |
| 21 ~CSSLengthNonInterpolableValue() final { NOTREACHED(); } | |
| 22 static PassRefPtr<CSSLengthNonInterpolableValue> create(bool hasPercentage) | |
| 23 { | |
| 24 DEFINE_STATIC_REF(CSSLengthNonInterpolableValue, singleton, adoptRef(new
CSSLengthNonInterpolableValue())); | |
| 25 DCHECK(singleton); | |
| 26 return hasPercentage ? singleton : nullptr; | |
| 27 } | |
| 28 static PassRefPtr<CSSLengthNonInterpolableValue> merge(const NonInterpolable
Value* a, const NonInterpolableValue* b) | |
| 29 { | |
| 30 return create(hasPercentage(a) || hasPercentage(b)); | |
| 31 } | |
| 32 static bool hasPercentage(const NonInterpolableValue*); | |
| 33 | |
| 34 DECLARE_NON_INTERPOLABLE_VALUE_TYPE(); | |
| 35 | |
| 36 private: | |
| 37 CSSLengthNonInterpolableValue() { } | |
| 38 }; | |
| 39 | |
| 40 DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSLengthNonInterpolableValue); | |
| 41 DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSLengthNonInterpolableValue); | |
| 42 | |
| 43 bool CSSLengthNonInterpolableValue::hasPercentage(const NonInterpolableValue* no
nInterpolableValue) | |
| 44 { | |
| 45 DCHECK(isCSSLengthNonInterpolableValue(nonInterpolableValue)); | |
| 46 return static_cast<bool>(nonInterpolableValue); | |
| 47 } | |
| 48 | |
| 49 CSSLengthInterpolationType::CSSLengthInterpolationType(CSSPropertyID property) | 18 CSSLengthInterpolationType::CSSLengthInterpolationType(CSSPropertyID property) |
| 50 : CSSInterpolationType(property) | 19 : CSSInterpolationType(property) |
| 51 , m_valueRange(LengthPropertyFunctions::getValueRange(property)) | 20 , m_valueRange(LengthPropertyFunctions::getValueRange(property)) |
| 52 { } | 21 { } |
| 53 | 22 |
| 54 float CSSLengthInterpolationType::effectiveZoom(const ComputedStyle& style) cons
t | 23 float CSSLengthInterpolationType::effectiveZoom(const ComputedStyle& style) cons
t |
| 55 { | 24 { |
| 56 return LengthPropertyFunctions::isZoomedLength(cssProperty()) ? style.effect
iveZoom() : 1; | 25 return LengthPropertyFunctions::isZoomedLength(cssProperty()) ? style.effect
iveZoom() : 1; |
| 57 } | 26 } |
| 58 | 27 |
| 59 std::unique_ptr<InterpolableValue> CSSLengthInterpolationType::createInterpolabl
ePixels(double pixels) | |
| 60 { | |
| 61 std::unique_ptr<InterpolableList> interpolableList = createNeutralInterpolab
leValue(); | |
| 62 interpolableList->set(CSSPrimitiveValue::UnitTypePixels, InterpolableNumber:
:create(pixels)); | |
| 63 return std::move(interpolableList); | |
| 64 } | |
| 65 | |
| 66 InterpolationValue CSSLengthInterpolationType::createInterpolablePercent(double
percent) | |
| 67 { | |
| 68 std::unique_ptr<InterpolableList> interpolableList = createNeutralInterpolab
leValue(); | |
| 69 interpolableList->set(CSSPrimitiveValue::UnitTypePercentage, InterpolableNum
ber::create(percent)); | |
| 70 return InterpolationValue(std::move(interpolableList), CSSLengthNonInterpola
bleValue::create(true)); | |
| 71 } | |
| 72 | |
| 73 InterpolationValue CSSLengthInterpolationType::maybeConvertLength(const Length&
length, float zoom) | |
| 74 { | |
| 75 if (!length.isSpecified()) | |
| 76 return nullptr; | |
| 77 | |
| 78 PixelsAndPercent pixelsAndPercent = length.getPixelsAndPercent(); | |
| 79 std::unique_ptr<InterpolableList> values = createNeutralInterpolableValue(); | |
| 80 values->set(CSSPrimitiveValue::UnitTypePixels, InterpolableNumber::create(pi
xelsAndPercent.pixels / zoom)); | |
| 81 values->set(CSSPrimitiveValue::UnitTypePercentage, InterpolableNumber::creat
e(pixelsAndPercent.percent)); | |
| 82 | |
| 83 return InterpolationValue(std::move(values), CSSLengthNonInterpolableValue::
create(length.isPercentOrCalc())); | |
| 84 } | |
| 85 | |
| 86 std::unique_ptr<InterpolableList> CSSLengthInterpolationType::createNeutralInter
polableValue() | |
| 87 { | |
| 88 const size_t length = CSSPrimitiveValue::LengthUnitTypeCount; | |
| 89 std::unique_ptr<InterpolableList> values = InterpolableList::create(length); | |
| 90 for (size_t i = 0; i < length; i++) | |
| 91 values->set(i, InterpolableNumber::create(0)); | |
| 92 return values; | |
| 93 } | |
| 94 | |
| 95 PairwiseInterpolationValue CSSLengthInterpolationType::staticMergeSingleConversi
ons(InterpolationValue&& start, InterpolationValue&& end) | |
| 96 { | |
| 97 return PairwiseInterpolationValue( | |
| 98 std::move(start.interpolableValue), | |
| 99 std::move(end.interpolableValue), | |
| 100 CSSLengthNonInterpolableValue::merge(start.nonInterpolableValue.get(), e
nd.nonInterpolableValue.get())); | |
| 101 } | |
| 102 | |
| 103 bool CSSLengthInterpolationType::nonInterpolableValuesAreCompatible(const NonInt
erpolableValue* a, const NonInterpolableValue* b) | |
| 104 { | |
| 105 DCHECK(isCSSLengthNonInterpolableValue(a)); | |
| 106 DCHECK(isCSSLengthNonInterpolableValue(b)); | |
| 107 return true; | |
| 108 } | |
| 109 | |
| 110 void CSSLengthInterpolationType::composite( | |
| 111 std::unique_ptr<InterpolableValue>& underlyingInterpolableValue, | |
| 112 RefPtr<NonInterpolableValue>& underlyingNonInterpolableValue, | |
| 113 double underlyingFraction, | |
| 114 const InterpolableValue& interpolableValue, | |
| 115 const NonInterpolableValue* nonInterpolableValue) | |
| 116 { | |
| 117 underlyingInterpolableValue->scaleAndAdd(underlyingFraction, interpolableVal
ue); | |
| 118 underlyingNonInterpolableValue = CSSLengthNonInterpolableValue::merge(underl
yingNonInterpolableValue.get(), nonInterpolableValue); | |
| 119 } | |
| 120 | |
| 121 void CSSLengthInterpolationType::subtractFromOneHundredPercent(InterpolationValu
e& result) | |
| 122 { | |
| 123 InterpolableList& list = toInterpolableList(*result.interpolableValue); | |
| 124 for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) { | |
| 125 double value = -toInterpolableNumber(*list.get(i)).value(); | |
| 126 if (i == CSSPrimitiveValue::UnitTypePercentage) | |
| 127 value += 100; | |
| 128 toInterpolableNumber(*list.getMutable(i)).set(value); | |
| 129 } | |
| 130 result.nonInterpolableValue = CSSLengthNonInterpolableValue::create(true); | |
| 131 } | |
| 132 | |
| 133 InterpolationValue CSSLengthInterpolationType::maybeConvertCSSValue(const CSSVal
ue& value) | |
| 134 { | |
| 135 if (!value.isPrimitiveValue()) | |
| 136 return nullptr; | |
| 137 | |
| 138 const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value); | |
| 139 if (!primitiveValue.isLength() && !primitiveValue.isPercentage() && !primiti
veValue.isCalculatedPercentageWithLength()) | |
| 140 return nullptr; | |
| 141 | |
| 142 CSSLengthArray lengthArray; | |
| 143 primitiveValue.accumulateLengthArray(lengthArray); | |
| 144 | |
| 145 std::unique_ptr<InterpolableList> values = InterpolableList::create(CSSPrimi
tiveValue::LengthUnitTypeCount); | |
| 146 for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) | |
| 147 values->set(i, InterpolableNumber::create(lengthArray.values[i])); | |
| 148 | |
| 149 bool hasPercentage = lengthArray.typeFlags.get(CSSPrimitiveValue::UnitTypePe
rcentage); | |
| 150 return InterpolationValue(std::move(values), CSSLengthNonInterpolableValue::
create(hasPercentage)); | |
| 151 } | |
| 152 | |
| 153 class ParentLengthChecker : public InterpolationType::ConversionChecker { | 28 class ParentLengthChecker : public InterpolationType::ConversionChecker { |
| 154 public: | 29 public: |
| 155 static std::unique_ptr<ParentLengthChecker> create(CSSPropertyID property, c
onst Length& length) | 30 static std::unique_ptr<ParentLengthChecker> create(CSSPropertyID property, c
onst Length& length) |
| 156 { | 31 { |
| 157 return wrapUnique(new ParentLengthChecker(property, length)); | 32 return wrapUnique(new ParentLengthChecker(property, length)); |
| 158 } | 33 } |
| 159 | 34 |
| 160 private: | 35 private: |
| 161 ParentLengthChecker(CSSPropertyID property, const Length& length) | 36 ParentLengthChecker(CSSPropertyID property, const Length& length) |
| 162 : m_property(property) | 37 : m_property(property) |
| 163 , m_length(length) | 38 , m_length(length) |
| 164 { } | 39 { } |
| 165 | 40 |
| 166 bool isValid(const InterpolationEnvironment& environment, const Interpolatio
nValue& underlying) const final | 41 bool isValid(const InterpolationEnvironment& environment, const Interpolatio
nValue& underlying) const final |
| 167 { | 42 { |
| 168 Length parentLength; | 43 Length parentLength; |
| 169 if (!LengthPropertyFunctions::getLength(m_property, *environment.state()
.parentStyle(), parentLength)) | 44 if (!LengthPropertyFunctions::getLength(m_property, *environment.state()
.parentStyle(), parentLength)) |
| 170 return false; | 45 return false; |
| 171 return parentLength == m_length; | 46 return parentLength == m_length; |
| 172 } | 47 } |
| 173 | 48 |
| 174 const CSSPropertyID m_property; | 49 const CSSPropertyID m_property; |
| 175 const Length m_length; | 50 const Length m_length; |
| 176 }; | 51 }; |
| 177 | 52 |
| 178 InterpolationValue CSSLengthInterpolationType::maybeConvertNeutral(const Interpo
lationValue&, ConversionCheckers&) const | 53 InterpolationValue CSSLengthInterpolationType::maybeConvertNeutral(const Interpo
lationValue&, ConversionCheckers&) const |
| 179 { | 54 { |
| 180 return InterpolationValue(createNeutralInterpolableValue()); | 55 return InterpolationValue(LengthInterpolationFunctions::createNeutralInterpo
lableValue()); |
| 181 } | 56 } |
| 182 | 57 |
| 183 InterpolationValue CSSLengthInterpolationType::maybeConvertInitial(const StyleRe
solverState&, ConversionCheckers& conversionCheckers) const | 58 InterpolationValue CSSLengthInterpolationType::maybeConvertInitial(const StyleRe
solverState&, ConversionCheckers& conversionCheckers) const |
| 184 { | 59 { |
| 185 Length initialLength; | 60 Length initialLength; |
| 186 if (!LengthPropertyFunctions::getInitialLength(cssProperty(), initialLength)
) | 61 if (!LengthPropertyFunctions::getInitialLength(cssProperty(), initialLength)
) |
| 187 return nullptr; | 62 return nullptr; |
| 188 return maybeConvertLength(initialLength, 1); | 63 return LengthInterpolationFunctions::maybeConvertLength(initialLength, 1); |
| 189 } | 64 } |
| 190 | 65 |
| 191 InterpolationValue CSSLengthInterpolationType::maybeConvertInherit(const StyleRe
solverState& state, ConversionCheckers& conversionCheckers) const | 66 InterpolationValue CSSLengthInterpolationType::maybeConvertInherit(const StyleRe
solverState& state, ConversionCheckers& conversionCheckers) const |
| 192 { | 67 { |
| 193 if (!state.parentStyle()) | 68 if (!state.parentStyle()) |
| 194 return nullptr; | 69 return nullptr; |
| 195 Length inheritedLength; | 70 Length inheritedLength; |
| 196 if (!LengthPropertyFunctions::getLength(cssProperty(), *state.parentStyle(),
inheritedLength)) | 71 if (!LengthPropertyFunctions::getLength(cssProperty(), *state.parentStyle(),
inheritedLength)) |
| 197 return nullptr; | 72 return nullptr; |
| 198 conversionCheckers.append(ParentLengthChecker::create(cssProperty(), inherit
edLength)); | 73 conversionCheckers.append(ParentLengthChecker::create(cssProperty(), inherit
edLength)); |
| 199 return maybeConvertLength(inheritedLength, effectiveZoom(*state.parentStyle(
))); | 74 return LengthInterpolationFunctions::maybeConvertLength(inheritedLength, eff
ectiveZoom(*state.parentStyle())); |
| 200 } | 75 } |
| 201 | 76 |
| 202 InterpolationValue CSSLengthInterpolationType::maybeConvertValue(const CSSValue&
value, const StyleResolverState&, ConversionCheckers& conversionCheckers) const | 77 InterpolationValue CSSLengthInterpolationType::maybeConvertValue(const CSSValue&
value, const StyleResolverState&, ConversionCheckers& conversionCheckers) const |
| 203 { | 78 { |
| 204 if (value.isPrimitiveValue() && toCSSPrimitiveValue(value).isValueID()) { | 79 if (value.isPrimitiveValue() && toCSSPrimitiveValue(value).isValueID()) { |
| 205 CSSValueID valueID = toCSSPrimitiveValue(value).getValueID(); | 80 CSSValueID valueID = toCSSPrimitiveValue(value).getValueID(); |
| 206 double pixels; | 81 double pixels; |
| 207 if (!LengthPropertyFunctions::getPixelsForKeyword(cssProperty(), valueID
, pixels)) | 82 if (!LengthPropertyFunctions::getPixelsForKeyword(cssProperty(), valueID
, pixels)) |
| 208 return nullptr; | 83 return nullptr; |
| 209 return InterpolationValue(createInterpolablePixels(pixels)); | 84 return InterpolationValue(LengthInterpolationFunctions::createInterpolab
lePixels(pixels)); |
| 210 } | 85 } |
| 211 | 86 |
| 212 return maybeConvertCSSValue(value); | 87 return LengthInterpolationFunctions::maybeConvertCSSValue(value); |
| 88 } |
| 89 |
| 90 PairwiseInterpolationValue CSSLengthInterpolationType::maybeMergeSingles(Interpo
lationValue&& start, InterpolationValue&& end) const |
| 91 { |
| 92 return LengthInterpolationFunctions::mergeSingles(std::move(start), std::mov
e(end)); |
| 213 } | 93 } |
| 214 | 94 |
| 215 InterpolationValue CSSLengthInterpolationType::maybeConvertUnderlyingValue(const
InterpolationEnvironment& environment) const | 95 InterpolationValue CSSLengthInterpolationType::maybeConvertUnderlyingValue(const
InterpolationEnvironment& environment) const |
| 216 { | 96 { |
| 217 Length underlyingLength; | 97 Length underlyingLength; |
| 218 if (!LengthPropertyFunctions::getLength(cssProperty(), *environment.state().
style(), underlyingLength)) | 98 if (!LengthPropertyFunctions::getLength(cssProperty(), *environment.state().
style(), underlyingLength)) |
| 219 return nullptr; | 99 return nullptr; |
| 220 return maybeConvertLength(underlyingLength, effectiveZoom(*environment.state
().style())); | 100 return LengthInterpolationFunctions::maybeConvertLength(underlyingLength, ef
fectiveZoom(*environment.state().style())); |
| 221 } | 101 } |
| 222 | 102 |
| 223 void CSSLengthInterpolationType::composite(UnderlyingValueOwner& underlyingValue
Owner, double underlyingFraction, const InterpolationValue& value, double interp
olationFraction) const | 103 void CSSLengthInterpolationType::composite(UnderlyingValueOwner& underlyingValue
Owner, double underlyingFraction, const InterpolationValue& value, double interp
olationFraction) const |
| 224 { | 104 { |
| 225 InterpolationValue& underlying = underlyingValueOwner.mutableValue(); | 105 InterpolationValue& underlying = underlyingValueOwner.mutableValue(); |
| 226 composite(underlying.interpolableValue, underlying.nonInterpolableValue, | 106 LengthInterpolationFunctions::composite(underlying.interpolableValue, underl
ying.nonInterpolableValue, |
| 227 underlyingFraction, *value.interpolableValue, value.nonInterpolableValue
.get()); | 107 underlyingFraction, *value.interpolableValue, value.nonInterpolableValue
.get()); |
| 228 } | 108 } |
| 229 | 109 |
| 230 // TODO(alancutter): Move this to Length.h. | |
| 231 static double clampToRange(double x, ValueRange range) | |
| 232 { | |
| 233 return (range == ValueRangeNonNegative && x < 0) ? 0 : x; | |
| 234 } | |
| 235 | |
| 236 Length CSSLengthInterpolationType::createLength(const InterpolableValue& interpo
lableValue, const NonInterpolableValue* nonInterpolableValue, const CSSToLengthC
onversionData& conversionData, ValueRange range) | |
| 237 { | |
| 238 const InterpolableList& interpolableList = toInterpolableList(interpolableVa
lue); | |
| 239 bool hasPercentage = CSSLengthNonInterpolableValue::hasPercentage(nonInterpo
lableValue); | |
| 240 double pixels = 0; | |
| 241 double percentage = 0; | |
| 242 for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) { | |
| 243 double value = toInterpolableNumber(*interpolableList.get(i)).value(); | |
| 244 if (i == CSSPrimitiveValue::UnitTypePercentage) { | |
| 245 percentage = value; | |
| 246 } else { | |
| 247 CSSPrimitiveValue::UnitType type = CSSPrimitiveValue::lengthUnitType
ToUnitType(static_cast<CSSPrimitiveValue::LengthUnitType>(i)); | |
| 248 pixels += conversionData.zoomedComputedPixels(value, type); | |
| 249 } | |
| 250 } | |
| 251 if (percentage != 0) | |
| 252 hasPercentage = true; | |
| 253 if (pixels != 0 && hasPercentage) | |
| 254 return Length(CalculationValue::create(PixelsAndPercent(pixels, percenta
ge), range)); | |
| 255 if (hasPercentage) | |
| 256 return Length(clampToRange(percentage, range), Percent); | |
| 257 return Length(CSSPrimitiveValue::clampToCSSLengthRange(clampToRange(pixels,
range)), Fixed); | |
| 258 } | |
| 259 | |
| 260 void CSSLengthInterpolationType::apply(const InterpolableValue& interpolableValu
e, const NonInterpolableValue* nonInterpolableValue, InterpolationEnvironment& e
nvironment) const | 110 void CSSLengthInterpolationType::apply(const InterpolableValue& interpolableValu
e, const NonInterpolableValue* nonInterpolableValue, InterpolationEnvironment& e
nvironment) const |
| 261 { | 111 { |
| 262 StyleResolverState& state = environment.state(); | 112 StyleResolverState& state = environment.state(); |
| 263 ComputedStyle& style = *state.style(); | 113 ComputedStyle& style = *state.style(); |
| 264 float zoom = effectiveZoom(style); | 114 float zoom = effectiveZoom(style); |
| 265 Length length = createLength(interpolableValue, nonInterpolableValue, state.
cssToLengthConversionData(), m_valueRange); | 115 Length length = LengthInterpolationFunctions::createLength(interpolableValue
, nonInterpolableValue, state.cssToLengthConversionData(), m_valueRange); |
| 266 if (LengthPropertyFunctions::setLength(cssProperty(), style, length)) { | 116 if (LengthPropertyFunctions::setLength(cssProperty(), style, length)) { |
| 267 #if DCHECK_IS_ON() | 117 #if DCHECK_IS_ON() |
| 268 // Assert that setting the length on ComputedStyle directly is identical
to the StyleBuilder code path. | 118 // Assert that setting the length on ComputedStyle directly is identical
to the StyleBuilder code path. |
| 269 // This check is useful for catching differences in clamping behaviour. | 119 // This check is useful for catching differences in clamping behaviour. |
| 270 Length before; | 120 Length before; |
| 271 Length after; | 121 Length after; |
| 272 DCHECK(LengthPropertyFunctions::getLength(cssProperty(), style, before))
; | 122 DCHECK(LengthPropertyFunctions::getLength(cssProperty(), style, before))
; |
| 273 StyleBuilder::applyProperty(cssProperty(), state, *CSSPrimitiveValue::cr
eate(length, zoom)); | 123 StyleBuilder::applyProperty(cssProperty(), state, *CSSPrimitiveValue::cr
eate(length, zoom)); |
| 274 DCHECK(LengthPropertyFunctions::getLength(cssProperty(), style, after)); | 124 DCHECK(LengthPropertyFunctions::getLength(cssProperty(), style, after)); |
| 275 DCHECK(before == after); | 125 DCHECK(before == after); |
| 276 #endif | 126 #endif |
| 277 return; | 127 return; |
| 278 } | 128 } |
| 279 StyleBuilder::applyProperty(cssProperty(), state, *CSSPrimitiveValue::create
(length, zoom)); | 129 StyleBuilder::applyProperty(cssProperty(), state, *CSSPrimitiveValue::create
(length, zoom)); |
| 280 } | 130 } |
| 281 | 131 |
| 282 } // namespace blink | 132 } // namespace blink |
| OLD | NEW |