| // 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 "third_party/blink/renderer/core/animation/css_size_list_interpolation_type.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/memory/ptr_util.h" |
| #include "third_party/blink/renderer/core/animation/list_interpolation_functions.h" |
| #include "third_party/blink/renderer/core/animation/size_interpolation_functions.h" |
| #include "third_party/blink/renderer/core/animation/size_list_property_functions.h" |
| #include "third_party/blink/renderer/core/css/css_value_list.h" |
| #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h" |
| #include "third_party/blink/renderer/core/style/computed_style.h" |
| #include "third_party/blink/renderer/platform/wtf/functional.h" |
| |
| namespace blink { |
| |
| class UnderlyingSizeListChecker |
| : public CSSInterpolationType::CSSConversionChecker { |
| public: |
| ~UnderlyingSizeListChecker() final = default; |
| |
| static std::unique_ptr<UnderlyingSizeListChecker> Create( |
| const NonInterpolableList& underlying_list) { |
| return base::WrapUnique(new UnderlyingSizeListChecker(underlying_list)); |
| } |
| |
| private: |
| UnderlyingSizeListChecker(const NonInterpolableList& underlying_list) |
| : underlying_list_(&underlying_list) {} |
| |
| bool IsValid(const StyleResolverState&, |
| const InterpolationValue& underlying) const final { |
| const auto& underlying_list = |
| ToNonInterpolableList(*underlying.non_interpolable_value); |
| wtf_size_t underlying_length = underlying_list.length(); |
| if (underlying_length != underlying_list_->length()) |
| return false; |
| for (wtf_size_t i = 0; i < underlying_length; i++) { |
| bool compatible = |
| SizeInterpolationFunctions::NonInterpolableValuesAreCompatible( |
| underlying_list.Get(i), underlying_list_->Get(i)); |
| if (!compatible) |
| return false; |
| } |
| return true; |
| } |
| |
| scoped_refptr<const NonInterpolableList> underlying_list_; |
| }; |
| |
| class InheritedSizeListChecker |
| : public CSSInterpolationType::CSSConversionChecker { |
| public: |
| ~InheritedSizeListChecker() final = default; |
| |
| static std::unique_ptr<InheritedSizeListChecker> Create( |
| const CSSProperty& property, |
| const SizeList& inherited_size_list) { |
| return base::WrapUnique( |
| new InheritedSizeListChecker(property, inherited_size_list)); |
| } |
| |
| private: |
| InheritedSizeListChecker(const CSSProperty& property, |
| const SizeList& inherited_size_list) |
| : property_(property), inherited_size_list_(inherited_size_list) {} |
| |
| bool IsValid(const StyleResolverState& state, |
| const InterpolationValue&) const final { |
| return inherited_size_list_ == SizeListPropertyFunctions::GetSizeList( |
| property_, *state.ParentStyle()); |
| } |
| |
| const CSSProperty& property_; |
| SizeList inherited_size_list_; |
| }; |
| |
| InterpolationValue ConvertSizeList(const SizeList& size_list, float zoom) { |
| // Flatten pairs of width/height into individual items, even for contain and |
| // cover keywords. |
| return ListInterpolationFunctions::CreateList( |
| size_list.size() * 2, |
| [&size_list, zoom](wtf_size_t index) -> InterpolationValue { |
| bool convert_width = index % 2 == 0; |
| return SizeInterpolationFunctions::ConvertFillSizeSide( |
| size_list[index / 2], zoom, convert_width); |
| }); |
| } |
| |
| InterpolationValue MaybeConvertCSSSizeList(const CSSValue& value) { |
| // CSSPropertyParser doesn't put single values in lists so wrap it up in a |
| // temporary list. |
| const CSSValueList* list = nullptr; |
| if (!value.IsBaseValueList()) { |
| CSSValueList* temp_list = CSSValueList::CreateCommaSeparated(); |
| temp_list->Append(value); |
| list = temp_list; |
| } else { |
| list = ToCSSValueList(&value); |
| } |
| |
| // Flatten pairs of width/height into individual items, even for contain and |
| // cover keywords. |
| return ListInterpolationFunctions::CreateList( |
| list->length() * 2, [list](wtf_size_t index) -> InterpolationValue { |
| const CSSValue& css_size = list->Item(index / 2); |
| bool convert_width = index % 2 == 0; |
| return SizeInterpolationFunctions::MaybeConvertCSSSizeSide( |
| css_size, convert_width); |
| }); |
| } |
| |
| InterpolationValue CSSSizeListInterpolationType::MaybeConvertNeutral( |
| const InterpolationValue& underlying, |
| ConversionCheckers& conversion_checkers) const { |
| const auto& underlying_list = |
| ToNonInterpolableList(*underlying.non_interpolable_value); |
| conversion_checkers.push_back( |
| UnderlyingSizeListChecker::Create(underlying_list)); |
| return ListInterpolationFunctions::CreateList( |
| underlying_list.length(), [&underlying_list](wtf_size_t index) { |
| return SizeInterpolationFunctions::CreateNeutralValue( |
| underlying_list.Get(index)); |
| }); |
| } |
| |
| InterpolationValue CSSSizeListInterpolationType::MaybeConvertInitial( |
| const StyleResolverState&, |
| ConversionCheckers&) const { |
| return ConvertSizeList( |
| SizeListPropertyFunctions::GetInitialSizeList(CssProperty()), 1); |
| } |
| |
| InterpolationValue CSSSizeListInterpolationType::MaybeConvertInherit( |
| const StyleResolverState& state, |
| ConversionCheckers& conversion_checkers) const { |
| SizeList inherited_size_list = SizeListPropertyFunctions::GetSizeList( |
| CssProperty(), *state.ParentStyle()); |
| conversion_checkers.push_back( |
| InheritedSizeListChecker::Create(CssProperty(), inherited_size_list)); |
| return ConvertSizeList(inherited_size_list, state.Style()->EffectiveZoom()); |
| } |
| |
| InterpolationValue CSSSizeListInterpolationType::MaybeConvertValue( |
| const CSSValue& value, |
| const StyleResolverState*, |
| ConversionCheckers&) const { |
| return MaybeConvertCSSSizeList(value); |
| } |
| |
| PairwiseInterpolationValue CSSSizeListInterpolationType::MaybeMergeSingles( |
| InterpolationValue&& start, |
| InterpolationValue&& end) const { |
| return ListInterpolationFunctions::MaybeMergeSingles( |
| std::move(start), std::move(end), |
| ListInterpolationFunctions::LengthMatchingStrategy::kLowestCommonMultiple, |
| WTF::BindRepeating(SizeInterpolationFunctions::MaybeMergeSingles)); |
| } |
| |
| InterpolationValue |
| CSSSizeListInterpolationType::MaybeConvertStandardPropertyUnderlyingValue( |
| const ComputedStyle& style) const { |
| return ConvertSizeList( |
| SizeListPropertyFunctions::GetSizeList(CssProperty(), style), |
| style.EffectiveZoom()); |
| } |
| |
| void CSSSizeListInterpolationType::Composite( |
| UnderlyingValueOwner& underlying_value_owner, |
| double underlying_fraction, |
| const InterpolationValue& value, |
| double interpolation_fraction) const { |
| ListInterpolationFunctions::Composite( |
| underlying_value_owner, underlying_fraction, *this, value, |
| ListInterpolationFunctions::LengthMatchingStrategy::kLowestCommonMultiple, |
| WTF::BindRepeating( |
| SizeInterpolationFunctions::NonInterpolableValuesAreCompatible), |
| WTF::BindRepeating(SizeInterpolationFunctions::Composite)); |
| } |
| |
| void CSSSizeListInterpolationType::ApplyStandardPropertyValue( |
| const InterpolableValue& interpolable_value, |
| const NonInterpolableValue* non_interpolable_value, |
| StyleResolverState& state) const { |
| const auto& interpolable_list = ToInterpolableList(interpolable_value); |
| const auto& non_interpolable_list = |
| ToNonInterpolableList(*non_interpolable_value); |
| wtf_size_t length = interpolable_list.length(); |
| DCHECK_EQ(length, non_interpolable_list.length()); |
| DCHECK_EQ(length % 2, 0ul); |
| wtf_size_t size_list_length = length / 2; |
| SizeList size_list(size_list_length); |
| for (wtf_size_t i = 0; i < size_list_length; i++) { |
| size_list[i] = SizeInterpolationFunctions::CreateFillSize( |
| *interpolable_list.Get(i * 2), non_interpolable_list.Get(i * 2), |
| *interpolable_list.Get(i * 2 + 1), non_interpolable_list.Get(i * 2 + 1), |
| state.CssToLengthConversionData()); |
| } |
| SizeListPropertyFunctions::SetSizeList(CssProperty(), *state.Style(), |
| size_list); |
| } |
| |
| } // namespace blink |