| // 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_clip_interpolation_type.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/memory/ptr_util.h" |
| #include "third_party/blink/renderer/core/animation/length_interpolation_functions.h" |
| #include "third_party/blink/renderer/core/css/css_identifier_value.h" |
| #include "third_party/blink/renderer/core/css/css_quad_value.h" |
| #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h" |
| #include "third_party/blink/renderer/core/style/computed_style.h" |
| |
| namespace blink { |
| |
| struct ClipAutos { |
| ClipAutos() |
| : is_auto(true), |
| is_top_auto(false), |
| is_right_auto(false), |
| is_bottom_auto(false), |
| is_left_auto(false) {} |
| ClipAutos(bool is_top_auto, |
| bool is_right_auto, |
| bool is_bottom_auto, |
| bool is_left_auto) |
| : is_auto(false), |
| is_top_auto(is_top_auto), |
| is_right_auto(is_right_auto), |
| is_bottom_auto(is_bottom_auto), |
| is_left_auto(is_left_auto) {} |
| explicit ClipAutos(const LengthBox& clip) |
| : is_auto(false), |
| is_top_auto(clip.Top().IsAuto()), |
| is_right_auto(clip.Right().IsAuto()), |
| is_bottom_auto(clip.Bottom().IsAuto()), |
| is_left_auto(clip.Left().IsAuto()) {} |
| |
| bool operator==(const ClipAutos& other) const { |
| return is_auto == other.is_auto && is_top_auto == other.is_top_auto && |
| is_right_auto == other.is_right_auto && |
| is_bottom_auto == other.is_bottom_auto && |
| is_left_auto == other.is_left_auto; |
| } |
| bool operator!=(const ClipAutos& other) const { return !(*this == other); } |
| |
| bool is_auto; |
| bool is_top_auto; |
| bool is_right_auto; |
| bool is_bottom_auto; |
| bool is_left_auto; |
| }; |
| |
| class InheritedClipChecker : public CSSInterpolationType::CSSConversionChecker { |
| public: |
| static std::unique_ptr<InheritedClipChecker> Create( |
| const ComputedStyle& parent_style) { |
| Vector<Length> inherited_length_list; |
| GetClipLengthList(parent_style, inherited_length_list); |
| return base::WrapUnique( |
| new InheritedClipChecker(std::move(inherited_length_list))); |
| } |
| |
| private: |
| InheritedClipChecker(const Vector<Length>&& inherited_length_list) |
| : inherited_length_list_(std::move(inherited_length_list)) {} |
| |
| bool IsValid(const StyleResolverState& state, |
| const InterpolationValue& underlying) const final { |
| Vector<Length> inherited_length_list; |
| GetClipLengthList(*state.ParentStyle(), inherited_length_list); |
| return inherited_length_list_ == inherited_length_list; |
| } |
| |
| static void GetClipLengthList(const ComputedStyle& style, |
| Vector<Length>& length_list) { |
| if (style.HasAutoClip()) |
| return; |
| length_list.push_back(style.ClipTop()); |
| length_list.push_back(style.ClipRight()); |
| length_list.push_back(style.ClipBottom()); |
| length_list.push_back(style.ClipLeft()); |
| } |
| |
| const Vector<Length> inherited_length_list_; |
| }; |
| |
| class CSSClipNonInterpolableValue : public NonInterpolableValue { |
| public: |
| ~CSSClipNonInterpolableValue() final = default; |
| |
| static scoped_refptr<CSSClipNonInterpolableValue> Create( |
| const ClipAutos& clip_autos) { |
| return base::AdoptRef(new CSSClipNonInterpolableValue(clip_autos)); |
| } |
| |
| const ClipAutos& GetClipAutos() const { return clip_autos_; } |
| |
| DECLARE_NON_INTERPOLABLE_VALUE_TYPE(); |
| |
| private: |
| CSSClipNonInterpolableValue(const ClipAutos& clip_autos) |
| : clip_autos_(clip_autos) { |
| DCHECK(!clip_autos_.is_auto); |
| } |
| |
| const ClipAutos clip_autos_; |
| }; |
| |
| DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSClipNonInterpolableValue); |
| DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSClipNonInterpolableValue); |
| |
| class UnderlyingAutosChecker |
| : public CSSInterpolationType::CSSConversionChecker { |
| public: |
| ~UnderlyingAutosChecker() final = default; |
| |
| static std::unique_ptr<UnderlyingAutosChecker> Create( |
| const ClipAutos& underlying_autos) { |
| return base::WrapUnique(new UnderlyingAutosChecker(underlying_autos)); |
| } |
| |
| static ClipAutos GetUnderlyingAutos(const InterpolationValue& underlying) { |
| if (!underlying) |
| return ClipAutos(); |
| return ToCSSClipNonInterpolableValue(*underlying.non_interpolable_value) |
| .GetClipAutos(); |
| } |
| |
| private: |
| UnderlyingAutosChecker(const ClipAutos& underlying_autos) |
| : underlying_autos_(underlying_autos) {} |
| |
| bool IsValid(const StyleResolverState&, |
| const InterpolationValue& underlying) const final { |
| return underlying_autos_ == GetUnderlyingAutos(underlying); |
| } |
| |
| const ClipAutos underlying_autos_; |
| }; |
| |
| enum ClipComponentIndex : unsigned { |
| kClipTop, |
| kClipRight, |
| kClipBottom, |
| kClipLeft, |
| kClipComponentIndexCount, |
| }; |
| |
| static std::unique_ptr<InterpolableValue> ConvertClipComponent( |
| const Length& length, |
| double zoom) { |
| if (length.IsAuto()) |
| return InterpolableList::Create(0); |
| return LengthInterpolationFunctions::MaybeConvertLength(length, zoom) |
| .interpolable_value; |
| } |
| |
| static InterpolationValue CreateClipValue(const LengthBox& clip, double zoom) { |
| std::unique_ptr<InterpolableList> list = |
| InterpolableList::Create(kClipComponentIndexCount); |
| list->Set(kClipTop, ConvertClipComponent(clip.Top(), zoom)); |
| list->Set(kClipRight, ConvertClipComponent(clip.Right(), zoom)); |
| list->Set(kClipBottom, ConvertClipComponent(clip.Bottom(), zoom)); |
| list->Set(kClipLeft, ConvertClipComponent(clip.Left(), zoom)); |
| return InterpolationValue( |
| std::move(list), CSSClipNonInterpolableValue::Create(ClipAutos(clip))); |
| } |
| |
| InterpolationValue CSSClipInterpolationType::MaybeConvertNeutral( |
| const InterpolationValue& underlying, |
| ConversionCheckers& conversion_checkers) const { |
| ClipAutos underlying_autos = |
| UnderlyingAutosChecker::GetUnderlyingAutos(underlying); |
| conversion_checkers.push_back( |
| UnderlyingAutosChecker::Create(underlying_autos)); |
| if (underlying_autos.is_auto) |
| return nullptr; |
| LengthBox neutral_box( |
| underlying_autos.is_top_auto ? Length(kAuto) : Length(0, kFixed), |
| underlying_autos.is_right_auto ? Length(kAuto) : Length(0, kFixed), |
| underlying_autos.is_bottom_auto ? Length(kAuto) : Length(0, kFixed), |
| underlying_autos.is_left_auto ? Length(kAuto) : Length(0, kFixed)); |
| return CreateClipValue(neutral_box, 1); |
| } |
| |
| InterpolationValue CSSClipInterpolationType::MaybeConvertInitial( |
| const StyleResolverState&, |
| ConversionCheckers&) const { |
| return nullptr; |
| } |
| |
| InterpolationValue CSSClipInterpolationType::MaybeConvertInherit( |
| const StyleResolverState& state, |
| ConversionCheckers& conversion_checkers) const { |
| conversion_checkers.push_back( |
| InheritedClipChecker::Create(*state.ParentStyle())); |
| if (state.ParentStyle()->HasAutoClip()) |
| return nullptr; |
| return CreateClipValue(state.ParentStyle()->Clip(), |
| state.ParentStyle()->EffectiveZoom()); |
| } |
| |
| static bool IsCSSAuto(const CSSValue& value) { |
| return value.IsIdentifierValue() && |
| ToCSSIdentifierValue(value).GetValueID() == CSSValueAuto; |
| } |
| |
| static std::unique_ptr<InterpolableValue> ConvertClipComponent( |
| const CSSValue& length) { |
| if (IsCSSAuto(length)) |
| return InterpolableList::Create(0); |
| return LengthInterpolationFunctions::MaybeConvertCSSValue(length) |
| .interpolable_value; |
| } |
| |
| InterpolationValue CSSClipInterpolationType::MaybeConvertValue( |
| const CSSValue& value, |
| const StyleResolverState*, |
| ConversionCheckers&) const { |
| if (!value.IsQuadValue()) |
| return nullptr; |
| const CSSQuadValue& quad = ToCSSQuadValue(value); |
| std::unique_ptr<InterpolableList> list = |
| InterpolableList::Create(kClipComponentIndexCount); |
| list->Set(kClipTop, ConvertClipComponent(*quad.Top())); |
| list->Set(kClipRight, ConvertClipComponent(*quad.Right())); |
| list->Set(kClipBottom, ConvertClipComponent(*quad.Bottom())); |
| list->Set(kClipLeft, ConvertClipComponent(*quad.Left())); |
| ClipAutos autos(IsCSSAuto(*quad.Top()), IsCSSAuto(*quad.Right()), |
| IsCSSAuto(*quad.Bottom()), IsCSSAuto(*quad.Left())); |
| return InterpolationValue(std::move(list), |
| CSSClipNonInterpolableValue::Create(autos)); |
| } |
| |
| InterpolationValue |
| CSSClipInterpolationType::MaybeConvertStandardPropertyUnderlyingValue( |
| const ComputedStyle& style) const { |
| if (style.HasAutoClip()) |
| return nullptr; |
| return CreateClipValue(style.Clip(), style.EffectiveZoom()); |
| } |
| |
| PairwiseInterpolationValue CSSClipInterpolationType::MaybeMergeSingles( |
| InterpolationValue&& start, |
| InterpolationValue&& end) const { |
| const ClipAutos& start_autos = |
| ToCSSClipNonInterpolableValue(*start.non_interpolable_value) |
| .GetClipAutos(); |
| const ClipAutos& end_autos = |
| ToCSSClipNonInterpolableValue(*end.non_interpolable_value).GetClipAutos(); |
| if (start_autos != end_autos) |
| return nullptr; |
| return PairwiseInterpolationValue(std::move(start.interpolable_value), |
| std::move(end.interpolable_value), |
| std::move(start.non_interpolable_value)); |
| } |
| |
| void CSSClipInterpolationType::Composite( |
| UnderlyingValueOwner& underlying_value_owner, |
| double underlying_fraction, |
| const InterpolationValue& value, |
| double interpolation_fraction) const { |
| const ClipAutos& underlying_autos = |
| ToCSSClipNonInterpolableValue( |
| *underlying_value_owner.Value().non_interpolable_value) |
| .GetClipAutos(); |
| const ClipAutos& autos = |
| ToCSSClipNonInterpolableValue(*value.non_interpolable_value) |
| .GetClipAutos(); |
| if (underlying_autos == autos) |
| underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd( |
| underlying_fraction, *value.interpolable_value); |
| else |
| underlying_value_owner.Set(*this, value); |
| } |
| |
| void CSSClipInterpolationType::ApplyStandardPropertyValue( |
| const InterpolableValue& interpolable_value, |
| const NonInterpolableValue* non_interpolable_value, |
| StyleResolverState& state) const { |
| const ClipAutos& autos = |
| ToCSSClipNonInterpolableValue(non_interpolable_value)->GetClipAutos(); |
| const InterpolableList& list = ToInterpolableList(interpolable_value); |
| const auto& convert_index = [&list, &state](bool is_auto, wtf_size_t index) { |
| if (is_auto) |
| return Length(kAuto); |
| return LengthInterpolationFunctions::CreateLength( |
| *list.Get(index), nullptr, state.CssToLengthConversionData(), |
| kValueRangeAll); |
| }; |
| state.Style()->SetClip( |
| LengthBox(convert_index(autos.is_top_auto, kClipTop), |
| convert_index(autos.is_right_auto, kClipRight), |
| convert_index(autos.is_bottom_auto, kClipBottom), |
| convert_index(autos.is_left_auto, kClipLeft))); |
| } |
| |
| } // namespace blink |