blob: 3cfbbbd02280fefdd49d0c8f4e7adda05f51c365 [file] [log] [blame]
// Copyright 2015 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/CSSImageInterpolationType.h"
#include <memory>
#include "core/CSSPropertyNames.h"
#include "core/css/CSSCrossfadeValue.h"
#include "core/css/CSSPrimitiveValue.h"
#include "core/css/resolver/StyleResolverState.h"
#include "core/style/ComputedStyle.h"
#include "core/style/StyleImage.h"
#include "platform/wtf/PtrUtil.h"
namespace blink {
namespace {
const StyleImage* GetStyleImage(const CSSProperty& property,
const ComputedStyle& style) {
switch (property.PropertyID()) {
case CSSPropertyBorderImageSource:
return style.BorderImageSource();
case CSSPropertyListStyleImage:
return style.ListStyleImage();
case CSSPropertyWebkitMaskBoxImageSource:
return style.MaskBoxImageSource();
default:
NOTREACHED();
return nullptr;
}
}
} // namespace
class CSSImageNonInterpolableValue : public NonInterpolableValue {
public:
~CSSImageNonInterpolableValue() final = default;
static scoped_refptr<CSSImageNonInterpolableValue> Create(CSSValue* start,
CSSValue* end) {
return base::AdoptRef(new CSSImageNonInterpolableValue(start, end));
}
bool IsSingle() const { return is_single_; }
bool Equals(const CSSImageNonInterpolableValue& other) const {
return DataEquivalent(start_, other.start_) &&
DataEquivalent(end_, other.end_);
}
static scoped_refptr<CSSImageNonInterpolableValue> Merge(
scoped_refptr<NonInterpolableValue> start,
scoped_refptr<NonInterpolableValue> end);
CSSValue* Crossfade(double progress) const {
if (is_single_ || progress <= 0)
return start_;
if (progress >= 1)
return end_;
return cssvalue::CSSCrossfadeValue::Create(
start_, end_,
CSSPrimitiveValue::Create(progress,
CSSPrimitiveValue::UnitType::kNumber));
}
DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
private:
CSSImageNonInterpolableValue(CSSValue* start, CSSValue* end)
: start_(start), end_(end), is_single_(start_ == end_) {
DCHECK(start_);
DCHECK(end_);
}
Persistent<CSSValue> start_;
Persistent<CSSValue> end_;
const bool is_single_;
};
DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSImageNonInterpolableValue);
DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSImageNonInterpolableValue);
scoped_refptr<CSSImageNonInterpolableValue> CSSImageNonInterpolableValue::Merge(
scoped_refptr<NonInterpolableValue> start,
scoped_refptr<NonInterpolableValue> end) {
const CSSImageNonInterpolableValue& start_image_pair =
ToCSSImageNonInterpolableValue(*start);
const CSSImageNonInterpolableValue& end_image_pair =
ToCSSImageNonInterpolableValue(*end);
DCHECK(start_image_pair.is_single_);
DCHECK(end_image_pair.is_single_);
return Create(start_image_pair.start_, end_image_pair.end_);
}
InterpolationValue CSSImageInterpolationType::MaybeConvertStyleImage(
const StyleImage& style_image,
bool accept_gradients) {
return MaybeConvertCSSValue(*style_image.CssValue(), accept_gradients);
}
InterpolationValue CSSImageInterpolationType::MaybeConvertCSSValue(
const CSSValue& value,
bool accept_gradients) {
if (value.IsImageValue() || (value.IsGradientValue() && accept_gradients)) {
CSSValue* refable_css_value = const_cast<CSSValue*>(&value);
return InterpolationValue(InterpolableNumber::Create(1),
CSSImageNonInterpolableValue::Create(
refable_css_value, refable_css_value));
}
return nullptr;
}
PairwiseInterpolationValue
CSSImageInterpolationType::StaticMergeSingleConversions(
InterpolationValue&& start,
InterpolationValue&& end) {
if (!ToCSSImageNonInterpolableValue(*start.non_interpolable_value)
.IsSingle() ||
!ToCSSImageNonInterpolableValue(*end.non_interpolable_value).IsSingle()) {
return nullptr;
}
return PairwiseInterpolationValue(
InterpolableNumber::Create(0), InterpolableNumber::Create(1),
CSSImageNonInterpolableValue::Merge(start.non_interpolable_value,
end.non_interpolable_value));
}
const CSSValue* CSSImageInterpolationType::CreateCSSValue(
const InterpolableValue& interpolable_value,
const NonInterpolableValue* non_interpolable_value,
const StyleResolverState&) const {
return StaticCreateCSSValue(interpolable_value, non_interpolable_value);
}
const CSSValue* CSSImageInterpolationType::StaticCreateCSSValue(
const InterpolableValue& interpolable_value,
const NonInterpolableValue* non_interpolable_value) {
return ToCSSImageNonInterpolableValue(non_interpolable_value)
->Crossfade(ToInterpolableNumber(interpolable_value).Value());
}
StyleImage* CSSImageInterpolationType::ResolveStyleImage(
const CSSProperty& property,
const InterpolableValue& interpolable_value,
const NonInterpolableValue* non_interpolable_value,
StyleResolverState& state) {
const CSSValue* image =
StaticCreateCSSValue(interpolable_value, non_interpolable_value);
return state.GetStyleImage(property.PropertyID(), *image);
}
bool CSSImageInterpolationType::EqualNonInterpolableValues(
const NonInterpolableValue* a,
const NonInterpolableValue* b) {
return ToCSSImageNonInterpolableValue(*a).Equals(
ToCSSImageNonInterpolableValue(*b));
}
class UnderlyingImageChecker
: public CSSInterpolationType::CSSConversionChecker {
public:
~UnderlyingImageChecker() final = default;
static std::unique_ptr<UnderlyingImageChecker> Create(
const InterpolationValue& underlying) {
return WTF::WrapUnique(new UnderlyingImageChecker(underlying));
}
private:
UnderlyingImageChecker(const InterpolationValue& underlying)
: underlying_(underlying.Clone()) {}
bool IsValid(const StyleResolverState&,
const InterpolationValue& underlying) const final {
if (!underlying && !underlying_)
return true;
if (!underlying || !underlying_)
return false;
return underlying_.interpolable_value->Equals(
*underlying.interpolable_value) &&
CSSImageInterpolationType::EqualNonInterpolableValues(
underlying_.non_interpolable_value.get(),
underlying.non_interpolable_value.get());
}
const InterpolationValue underlying_;
};
InterpolationValue CSSImageInterpolationType::MaybeConvertNeutral(
const InterpolationValue& underlying,
ConversionCheckers& conversion_checkers) const {
conversion_checkers.push_back(UnderlyingImageChecker::Create(underlying));
return InterpolationValue(underlying.Clone());
}
InterpolationValue CSSImageInterpolationType::MaybeConvertInitial(
const StyleResolverState&,
ConversionCheckers& conversion_checkers) const {
return nullptr;
}
class InheritedImageChecker
: public CSSInterpolationType::CSSConversionChecker {
public:
~InheritedImageChecker() final = default;
static std::unique_ptr<InheritedImageChecker> Create(
const CSSProperty& property,
StyleImage* inherited_image) {
return WTF::WrapUnique(
new InheritedImageChecker(property, inherited_image));
}
private:
InheritedImageChecker(const CSSProperty& property,
StyleImage* inherited_image)
: property_(property), inherited_image_(inherited_image) {}
bool IsValid(const StyleResolverState& state,
const InterpolationValue& underlying) const final {
const StyleImage* inherited_image =
GetStyleImage(property_, *state.ParentStyle());
if (!inherited_image_ && !inherited_image)
return true;
if (!inherited_image_ || !inherited_image)
return false;
return *inherited_image_ == *inherited_image;
}
const CSSProperty& property_;
Persistent<StyleImage> inherited_image_;
};
InterpolationValue CSSImageInterpolationType::MaybeConvertInherit(
const StyleResolverState& state,
ConversionCheckers& conversion_checkers) const {
if (!state.ParentStyle())
return nullptr;
const StyleImage* inherited_image =
GetStyleImage(CssProperty(), *state.ParentStyle());
StyleImage* refable_image = const_cast<StyleImage*>(inherited_image);
conversion_checkers.push_back(
InheritedImageChecker::Create(CssProperty(), refable_image));
return MaybeConvertStyleImage(inherited_image, true);
}
InterpolationValue CSSImageInterpolationType::MaybeConvertValue(
const CSSValue& value,
const StyleResolverState*,
ConversionCheckers&) const {
return MaybeConvertCSSValue(value, true);
}
InterpolationValue
CSSImageInterpolationType::MaybeConvertStandardPropertyUnderlyingValue(
const ComputedStyle& style) const {
return MaybeConvertStyleImage(GetStyleImage(CssProperty(), style), true);
}
void CSSImageInterpolationType::Composite(
UnderlyingValueOwner& underlying_value_owner,
double underlying_fraction,
const InterpolationValue& value,
double interpolation_fraction) const {
underlying_value_owner.Set(*this, value);
}
void CSSImageInterpolationType::ApplyStandardPropertyValue(
const InterpolableValue& interpolable_value,
const NonInterpolableValue* non_interpolable_value,
StyleResolverState& state) const {
StyleImage* image = ResolveStyleImage(CssProperty(), interpolable_value,
non_interpolable_value, state);
switch (CssProperty().PropertyID()) {
case CSSPropertyBorderImageSource:
state.Style()->SetBorderImageSource(image);
break;
case CSSPropertyListStyleImage:
state.Style()->SetListStyleImage(image);
break;
case CSSPropertyWebkitMaskBoxImageSource:
state.Style()->SetMaskBoxImageSource(image);
break;
default:
NOTREACHED();
}
}
} // namespace blink