Chromium Code Reviews| Index: third_party/WebKit/Source/core/animation/CSSSizeListInterpolationType.cpp |
| diff --git a/third_party/WebKit/Source/core/animation/CSSSizeListInterpolationType.cpp b/third_party/WebKit/Source/core/animation/CSSSizeListInterpolationType.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..846d4edfcb924f28ba690cf7b9ede2da854ba0bc |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/core/animation/CSSSizeListInterpolationType.cpp |
| @@ -0,0 +1,311 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "core/animation/CSSSizeListInterpolationType.h" |
| + |
| +#include "core/animation/CSSLengthInterpolationType.h" |
| +#include "core/animation/ListInterpolationFunctions.h" |
| +#include "core/animation/SizeListPropertyFunctions.h" |
| +#include "core/css/CSSValueList.h" |
| +#include "core/css/CSSValuePair.h" |
| +#include "core/css/resolver/StyleResolverState.h" |
|
suzyh_UTC10 (ex-contributor)
2016/08/26 04:14:34
Consider also explicitly including the headers wer
alancutter (OOO until 2018)
2016/08/26 13:31:48
I think the policy is only to not include things t
|
| + |
| +namespace blink { |
| + |
| +class CSSSizeNonInterpolableValue : public NonInterpolableValue { |
| +public: |
| + static PassRefPtr<CSSSizeNonInterpolableValue> create(CSSValueID keyword) |
| + { |
| + return adoptRef(new CSSSizeNonInterpolableValue(keyword)); |
| + } |
| + |
| + static PassRefPtr<CSSSizeNonInterpolableValue> create(PassRefPtr<NonInterpolableValue> lengthNonInterpolableValue) |
| + { |
| + return adoptRef(new CSSSizeNonInterpolableValue(lengthNonInterpolableValue)); |
| + } |
| + |
| + bool isKeyword() const { return m_keyword != CSSValueInvalid; } |
| + CSSValueID keyword() const { DCHECK(isKeyword()); return m_keyword; } |
| + |
| + const NonInterpolableValue* lengthNonInterpolableValue() const { DCHECK(!isKeyword()); return m_lengthNonInterpolableValue.get(); } |
| + RefPtr<NonInterpolableValue>& lengthNonInterpolableValue() { DCHECK(!isKeyword()); return m_lengthNonInterpolableValue; } |
| + |
| + DECLARE_NON_INTERPOLABLE_VALUE_TYPE(); |
| + |
| +private: |
| + CSSSizeNonInterpolableValue(CSSValueID keyword) |
|
suzyh_UTC10 (ex-contributor)
2016/08/26 04:14:34
Is there a reason to have two constructors mirrori
alancutter (OOO until 2018)
2016/08/26 13:31:48
I think it's better for assertions to be in constr
suzyh_UTC10 (ex-contributor)
2016/08/30 08:01:07
I think this is a style thing, and I am undecided.
alancutter (OOO until 2018)
2016/09/05 07:44:27
There is precedence for checking in the constructo
|
| + : m_keyword(keyword) |
| + , m_lengthNonInterpolableValue(nullptr) |
| + { |
| + DCHECK_NE(keyword, CSSValueInvalid); |
| + } |
| + |
| + CSSSizeNonInterpolableValue(PassRefPtr<NonInterpolableValue> lengthNonInterpolableValue) |
| + : m_keyword(CSSValueInvalid) |
| + , m_lengthNonInterpolableValue(lengthNonInterpolableValue) |
| + { } |
| + |
| + CSSValueID m_keyword; |
| + RefPtr<NonInterpolableValue> m_lengthNonInterpolableValue; |
| +}; |
| + |
| +DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSSizeNonInterpolableValue); |
| +DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSSizeNonInterpolableValue); |
| + |
| +static bool sizesAreCompatible(const NonInterpolableValue* a, const NonInterpolableValue* b) |
|
suzyh_UTC10 (ex-contributor)
2016/08/26 04:14:34
Does this function belong in a separate helper lib
alancutter (OOO until 2018)
2016/08/26 13:31:48
Moved into SizeInterpolationFunctions.
|
| +{ |
| + const auto& sizeA = toCSSSizeNonInterpolableValue(*a); |
| + const auto& sizeB = toCSSSizeNonInterpolableValue(*b); |
| + if (sizeA.isKeyword() != sizeB.isKeyword()) |
| + return false; |
| + if (sizeA.isKeyword()) |
| + return sizeA.keyword() == sizeB.keyword(); |
| + return true; |
| +} |
| + |
| +class UnderlyingSizeListChecker : public InterpolationType::ConversionChecker { |
| +public: |
| + ~UnderlyingSizeListChecker() final {} |
| + |
| + static std::unique_ptr<UnderlyingSizeListChecker> create(const NonInterpolableList& underlyingList) |
| + { |
| + return wrapUnique(new UnderlyingSizeListChecker(underlyingList)); |
| + } |
| + |
| +private: |
| + UnderlyingSizeListChecker(const NonInterpolableList& underlyingList) |
| + : m_underlyingList(&underlyingList) |
| + { } |
| + |
| + bool isValid(const InterpolationEnvironment&, const InterpolationValue& underlying) const final |
| + { |
| + const auto& underlyingList = toNonInterpolableList(*underlying.nonInterpolableValue); |
| + size_t underlyingLength = underlyingList.length(); |
| + if (underlyingLength != m_underlyingList->length()) |
| + return false; |
| + for (size_t i = 0; i < underlyingLength; i++) { |
| + if (!sizesAreCompatible(underlyingList.get(i), m_underlyingList->get(i))) |
| + return false; |
| + } |
| + return true; |
| + } |
| + |
| + RefPtr<const NonInterpolableList> m_underlyingList; |
| +}; |
| + |
| +class InheritedSizeListChecker : public InterpolationType::ConversionChecker { |
| +public: |
| + ~InheritedSizeListChecker() final {} |
| + |
| + static std::unique_ptr<InheritedSizeListChecker> create(CSSPropertyID property, const SizeList& inheritedSizeList) |
| + { |
| + return wrapUnique(new InheritedSizeListChecker(property, inheritedSizeList)); |
| + } |
| + |
| +private: |
| + InheritedSizeListChecker(CSSPropertyID property, const SizeList& inheritedSizeList) |
| + : m_property(property) |
| + , m_inheritedSizeList(inheritedSizeList) |
| + { } |
| + |
| + bool isValid(const InterpolationEnvironment& environment, const InterpolationValue&) const final |
| + { |
| + return m_inheritedSizeList == SizeListPropertyFunctions::getSizeList(m_property, *environment.state().parentStyle()); |
| + } |
| + |
| + CSSPropertyID m_property; |
| + SizeList m_inheritedSizeList; |
| +}; |
| + |
| +InterpolationValue convertKeyword(CSSValueID keyword) |
| +{ |
| + return InterpolationValue(InterpolableList::create(0), CSSSizeNonInterpolableValue::create(keyword)); |
| +} |
| + |
| +InterpolationValue wrapConvertedLength(InterpolationValue&& convertedLength) |
| +{ |
| + if (!convertedLength) |
| + return nullptr; |
| + return InterpolationValue( |
| + std::move(convertedLength.interpolableValue), |
| + CSSSizeNonInterpolableValue::create(convertedLength.nonInterpolableValue.release())); |
| +} |
| + |
| +InterpolationValue convertSizeList(const SizeList& sizeList, float zoom) |
| +{ |
| + // Flatten pairs of width/height into individual items, even for contain and cover. |
| + return ListInterpolationFunctions::createList(sizeList.size() * 2, [&sizeList, zoom](size_t index) -> InterpolationValue { |
| + const FillSize& size = sizeList[index / 2]; |
| + switch (size.type) { |
| + case SizeLength: { |
| + const Length& side = index % 2 == 0 ? size.size.width() : size.size.height(); |
| + if (side.isAuto()) |
| + return convertKeyword(CSSValueAuto); |
| + return wrapConvertedLength(CSSLengthInterpolationType::maybeConvertLength(side, zoom)); |
| + } |
| + case Contain: |
| + return convertKeyword(CSSValueContain); |
| + case Cover: |
| + return convertKeyword(CSSValueCover); |
| + case SizeNone: |
| + default: |
| + NOTREACHED(); |
| + return nullptr; |
| + } |
| + }); |
| +} |
| + |
| +InterpolationValue CSSSizeListInterpolationType::maybeConvertNeutral(const InterpolationValue& underlying, ConversionCheckers& conversionCheckers) const |
| +{ |
| + const auto& underlyingList = toNonInterpolableList(*underlying.nonInterpolableValue); |
| + conversionCheckers.append(UnderlyingSizeListChecker::create(underlyingList)); |
| + return ListInterpolationFunctions::createList(underlyingList.length(), [&underlyingList](size_t index) { |
| + // const_cast to take a ref. |
| + auto& item = toCSSSizeNonInterpolableValue(const_cast<NonInterpolableValue&>(*underlyingList.get(index))); |
| + if (item.isKeyword()) |
| + return InterpolationValue(InterpolableList::create(0), &item); |
| + return InterpolationValue(CSSLengthInterpolationType::createNeutralInterpolableValue(), &item); |
| + }); |
| +} |
| + |
| +InterpolationValue CSSSizeListInterpolationType::maybeConvertInitial(const StyleResolverState&, ConversionCheckers&) const |
| +{ |
| + return convertSizeList(SizeListPropertyFunctions::getInitialSizeList(cssProperty()), 1); |
| +} |
| + |
| +InterpolationValue CSSSizeListInterpolationType::maybeConvertInherit(const StyleResolverState& state, ConversionCheckers& conversionCheckers) const |
| +{ |
| + SizeList inheritedSizeList = SizeListPropertyFunctions::getSizeList(cssProperty(), *state.parentStyle()); |
| + conversionCheckers.append(InheritedSizeListChecker::create(cssProperty(), inheritedSizeList)); |
| + return convertSizeList(inheritedSizeList, state.style()->effectiveZoom()); |
| +} |
| + |
| +InterpolationValue CSSSizeListInterpolationType::maybeConvertValue(const CSSValue& value, const StyleResolverState& state, ConversionCheckers&) const |
| +{ |
| + // CSSPropertyParser doesn't put single values in lists so wrap it up in a temporary list. |
| + const CSSValueList* list = nullptr; |
| + if (!value.isBaseValueList()) { |
| + CSSValueList* tempList = CSSValueList::createCommaSeparated(); |
| + tempList->append(value); |
| + list = tempList; |
| + } else { |
| + list = toCSSValueList(&value); |
| + } |
| + |
| + // Flatten pairs of width/height into individual items, even for contain and cover. |
| + return ListInterpolationFunctions::createList(list->length() * 2, [list](size_t index) -> InterpolationValue { |
|
suzyh_UTC10 (ex-contributor)
2016/08/26 04:14:34
I think I would prefer to see a separate library o
alancutter (OOO until 2018)
2016/08/26 13:31:48
This is a lambda due to the closure of list (and t
|
| + const CSSValue& item = list->item(index / 2); |
| + if (item.isValuePair()) { |
| + const CSSValuePair& pair = toCSSValuePair(item); |
| + const CSSValue& side = index % 2 == 0 ? pair.first() : pair.second(); |
| + if (side.isPrimitiveValue() && toCSSPrimitiveValue(side).getValueID() == CSSValueAuto) |
| + return convertKeyword(CSSValueAuto); |
| + return wrapConvertedLength(CSSLengthInterpolationType::maybeConvertCSSValue(side)); |
| + } |
| + if (!item.isPrimitiveValue()) |
| + return nullptr; |
| + CSSValueID keyword = toCSSPrimitiveValue(item).getValueID(); |
| + if (keyword) |
| + return convertKeyword(keyword); |
| + // A single length is equivalent to "<length> auto". |
| + if (index % 2 == 0) |
| + return wrapConvertedLength(CSSLengthInterpolationType::maybeConvertCSSValue(item)); |
| + return convertKeyword(CSSValueAuto); |
| + }); |
| +} |
| + |
| +static PairwiseInterpolationValue maybeMergeSingleSizes(InterpolationValue&& start, InterpolationValue&& end) |
| +{ |
| + if (!sizesAreCompatible(start.nonInterpolableValue.get(), end.nonInterpolableValue.get())) |
| + return nullptr; |
| + return PairwiseInterpolationValue( |
| + std::move(start.interpolableValue), |
| + std::move(end.interpolableValue), |
| + start.nonInterpolableValue.release()); |
| +} |
| + |
| +PairwiseInterpolationValue CSSSizeListInterpolationType::maybeMergeSingles(InterpolationValue&& start, InterpolationValue&& end) const |
| +{ |
| + return ListInterpolationFunctions::maybeMergeSingles(std::move(start), std::move(end), maybeMergeSingleSizes); |
| +} |
| + |
| +InterpolationValue CSSSizeListInterpolationType::maybeConvertUnderlyingValue(const InterpolationEnvironment& environment) const |
| +{ |
| + const ComputedStyle& style = *environment.state().style(); |
| + return convertSizeList(SizeListPropertyFunctions::getSizeList(cssProperty(), style), style.effectiveZoom()); |
| +} |
| + |
| +void compositeSizes(std::unique_ptr<InterpolableValue>& underlyingInterpolableValue, RefPtr<NonInterpolableValue>& underlyingNonInterpolableValue, double underlyingFraction, const InterpolableValue& interpolableValue, const NonInterpolableValue* nonInterpolableValue) |
| +{ |
| + const auto& sizeNonInterpolableValue = toCSSSizeNonInterpolableValue(*nonInterpolableValue); |
| + if (sizeNonInterpolableValue.isKeyword()) |
| + return; |
| + auto& underlyingSizeNonInterpolableValue = toCSSSizeNonInterpolableValue(*underlyingNonInterpolableValue); |
| + CSSLengthInterpolationType::composite( |
| + underlyingInterpolableValue, |
| + underlyingSizeNonInterpolableValue.lengthNonInterpolableValue(), |
| + underlyingFraction, |
| + interpolableValue, |
| + sizeNonInterpolableValue.lengthNonInterpolableValue()); |
| +} |
| + |
| +void CSSSizeListInterpolationType::composite(UnderlyingValueOwner& underlyingValueOwner, double underlyingFraction, const InterpolationValue& value, double interpolationFraction) const |
| +{ |
| + ListInterpolationFunctions::composite(underlyingValueOwner, underlyingFraction, *this, value, sizesAreCompatible, compositeSizes); |
| +} |
| + |
| +Length createLength(const InterpolableValue& interpolableValue, const CSSSizeNonInterpolableValue& nonInterpolableValue, const CSSToLengthConversionData& conversionData) |
| +{ |
| + if (nonInterpolableValue.isKeyword()) { |
| + DCHECK_EQ(nonInterpolableValue.keyword(), CSSValueAuto); |
| + return Length(Auto); |
| + } |
| + return CSSLengthInterpolationType::resolveInterpolableLength(interpolableValue, nonInterpolableValue.lengthNonInterpolableValue(), conversionData, ValueRangeNonNegative); |
|
suzyh_UTC10 (ex-contributor)
2016/08/26 04:14:34
As a reader of this code it is surprising to have
alancutter (OOO until 2018)
2016/08/26 13:31:48
Agreed, CSSLengthInterpolationType should have its
|
| +} |
| + |
| +FillSize createFillSize(const InterpolableValue& interpolableValueA, const NonInterpolableValue* nonInterpolableValueA, const InterpolableValue& interpolableValueB, const NonInterpolableValue* nonInterpolableValueB, const CSSToLengthConversionData& conversionData) |
| +{ |
| + const auto& sideA = toCSSSizeNonInterpolableValue(*nonInterpolableValueA); |
| + const auto& sideB = toCSSSizeNonInterpolableValue(*nonInterpolableValueB); |
| + if (sideA.isKeyword()) { |
| + switch (sideA.keyword()) { |
| + case CSSValueCover: |
| + DCHECK_EQ(sideA.keyword(), sideB.keyword()); |
| + return FillSize(Cover, LengthSize()); |
| + case CSSValueContain: |
| + DCHECK_EQ(sideA.keyword(), sideB.keyword()); |
| + return FillSize(Contain, LengthSize()); |
| + case CSSValueAuto: |
| + break; |
| + default: |
| + NOTREACHED(); |
| + break; |
| + } |
| + } |
| + return FillSize(SizeLength, LengthSize( |
| + createLength(interpolableValueA, sideA, conversionData), |
| + createLength(interpolableValueB, sideB, conversionData))); |
| +} |
| + |
| +void CSSSizeListInterpolationType::apply(const InterpolableValue& interpolableValue, const NonInterpolableValue* nonInterpolableValue, InterpolationEnvironment& environment) const |
| +{ |
| + const auto& interpolableList = toInterpolableList(interpolableValue); |
| + const auto& nonInterpolableList = toNonInterpolableList(*nonInterpolableValue); |
| + size_t length = interpolableList.length(); |
| + DCHECK_EQ(length, nonInterpolableList.length()); |
| + DCHECK_EQ(length % 2, 0ul); |
| + size_t sizeListLength = length / 2; |
| + SizeList sizeList(sizeListLength); |
| + for (size_t i = 0; i < sizeListLength; i++) { |
| + sizeList[i] = createFillSize( |
| + *interpolableList.get(i * 2), |
| + nonInterpolableList.get(i * 2), |
| + *interpolableList.get(i * 2 + 1), |
| + nonInterpolableList.get(i * 2 + 1), |
| + environment.state().cssToLengthConversionData()); |
| + } |
| + SizeListPropertyFunctions::setSizeList(cssProperty(), *environment.state().style(), sizeList); |
| +} |
| + |
| +} // namespace blink |