blob: cb79a29792bb0109b733d9c4747d296fb88c860d [file] [log] [blame]
// 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 final
: public CSSInterpolationType::CSSConversionChecker {
public:
explicit UnderlyingSizeListChecker(const NonInterpolableList& underlying_list)
: underlying_list_(&underlying_list) {}
~UnderlyingSizeListChecker() final = default;
private:
bool IsValid(const StyleResolverState&,
const InterpolationValue& underlying) const final {
const auto& underlying_list =
To<NonInterpolableList>(*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 final
: public CSSInterpolationType::CSSConversionChecker {
public:
InheritedSizeListChecker(const CSSProperty& property,
const SizeList& inherited_size_list)
: property_(property), inherited_size_list_(inherited_size_list) {}
~InheritedSizeListChecker() final = default;
private:
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 = To<CSSValueList>(&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 =
To<NonInterpolableList>(*underlying.non_interpolable_value);
conversion_checkers.push_back(
std::make_unique<UnderlyingSizeListChecker>(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(std::make_unique<InheritedSizeListChecker>(
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(
ListInterpolationFunctions::InterpolableValuesKnownCompatible),
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 = To<InterpolableList>(interpolable_value);
const auto& non_interpolable_list =
To<NonInterpolableList>(*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