Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Unified Diff: third_party/WebKit/Source/core/animation/CSSSizeListInterpolationType.cpp

Issue 2280553002: Allow interpolation of background-size values with keywords in CSS Animations (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698