blob: 05af3cef555878f96ff524997f240934de786c90 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// 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_basic_shape_interpolation_type.h"
#include <memory>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/memory/values_equivalent.h"
#include "third_party/blink/renderer/core/animation/basic_shape_interpolation_functions.h"
#include "third_party/blink/renderer/core/css/css_property_names.h"
#include "third_party/blink/renderer/core/css/css_value_list.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
#include "third_party/blink/renderer/core/style/basic_shapes.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/core/style/shape_clip_path_operation.h"
#include "third_party/blink/renderer/core/style/shape_offset_path_operation.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
namespace blink {
namespace {
const BasicShape* GetBasicShape(const CSSProperty& property,
const ComputedStyle& style) {
switch (property.PropertyID()) {
case CSSPropertyID::kShapeOutside:
if (!style.ShapeOutside())
return nullptr;
if (style.ShapeOutside()->GetType() != ShapeValue::kShape)
return nullptr;
if (style.ShapeOutside()->CssBox() != CSSBoxType::kMissing)
return nullptr;
return style.ShapeOutside()->Shape();
case CSSPropertyID::kOffsetPath: {
auto* offset_path_operation =
DynamicTo<ShapeOffsetPathOperation>(style.OffsetPath());
if (!offset_path_operation) {
return nullptr;
}
const auto& shape = offset_path_operation->GetBasicShape();
// Path and Ray shapes are handled by PathInterpolationType and
// RayInterpolationType.
if (shape.GetType() == BasicShape::kStylePathType ||
shape.GetType() == BasicShape::kStyleRayType) {
return nullptr;
}
return &shape;
}
case CSSPropertyID::kClipPath: {
auto* clip_path_operation =
DynamicTo<ShapeClipPathOperation>(style.ClipPath());
if (!clip_path_operation)
return nullptr;
auto* shape = clip_path_operation->GetBasicShape();
// Path shape is handled by PathInterpolationType.
if (shape->GetType() == BasicShape::kStylePathType)
return nullptr;
return shape;
}
case CSSPropertyID::kObjectViewBox:
return style.ObjectViewBox();
default:
NOTREACHED();
return nullptr;
}
}
class UnderlyingCompatibilityChecker
: public CSSInterpolationType::CSSConversionChecker {
public:
UnderlyingCompatibilityChecker(scoped_refptr<const NonInterpolableValue>
underlying_non_interpolable_value)
: underlying_non_interpolable_value_(
std::move(underlying_non_interpolable_value)) {}
private:
bool IsValid(const StyleResolverState&,
const InterpolationValue& underlying) const final {
return basic_shape_interpolation_functions::ShapesAreCompatible(
*underlying_non_interpolable_value_,
*underlying.non_interpolable_value);
}
scoped_refptr<const NonInterpolableValue> underlying_non_interpolable_value_;
};
class InheritedShapeChecker
: public CSSInterpolationType::CSSConversionChecker {
public:
InheritedShapeChecker(const CSSProperty& property,
scoped_refptr<const BasicShape> inherited_shape)
: property_(property), inherited_shape_(std::move(inherited_shape)) {}
private:
bool IsValid(const StyleResolverState& state,
const InterpolationValue&) const final {
return base::ValuesEquivalent(
inherited_shape_.get(), GetBasicShape(property_, *state.ParentStyle()));
}
const CSSProperty& property_;
scoped_refptr<const BasicShape> inherited_shape_;
};
} // namespace
InterpolationValue CSSBasicShapeInterpolationType::MaybeConvertNeutral(
const InterpolationValue& underlying,
ConversionCheckers& conversion_checkers) const {
// const_cast is for taking refs.
NonInterpolableValue* non_interpolable_value =
const_cast<NonInterpolableValue*>(
underlying.non_interpolable_value.get());
conversion_checkers.push_back(
MakeGarbageCollected<UnderlyingCompatibilityChecker>(
non_interpolable_value));
return InterpolationValue(
basic_shape_interpolation_functions::CreateNeutralValue(
*underlying.non_interpolable_value),
non_interpolable_value);
}
InterpolationValue CSSBasicShapeInterpolationType::MaybeConvertInitial(
const StyleResolverState& state,
ConversionCheckers&) const {
return basic_shape_interpolation_functions::MaybeConvertBasicShape(
GetBasicShape(CssProperty(),
state.GetDocument().GetStyleResolver().InitialStyle()),
1);
}
InterpolationValue CSSBasicShapeInterpolationType::MaybeConvertInherit(
const StyleResolverState& state,
ConversionCheckers& conversion_checkers) const {
const BasicShape* shape = GetBasicShape(CssProperty(), *state.ParentStyle());
conversion_checkers.push_back(
MakeGarbageCollected<InheritedShapeChecker>(CssProperty(), shape));
return basic_shape_interpolation_functions::MaybeConvertBasicShape(
shape, state.ParentStyle()->EffectiveZoom());
}
InterpolationValue CSSBasicShapeInterpolationType::MaybeConvertValue(
const CSSValue& value,
const StyleResolverState*,
ConversionCheckers&) const {
if (!value.IsBaseValueList())
return basic_shape_interpolation_functions::MaybeConvertCSSValue(value);
const auto& list = To<CSSValueList>(value);
// Path and Ray shapes are handled by PathInterpolationType and
// RayInterpolationType.
if (!list.First().IsBasicShapeValue() || list.First().IsRayValue() ||
list.First().IsPathValue()) {
return nullptr;
}
return basic_shape_interpolation_functions::MaybeConvertCSSValue(
list.Item(0));
}
PairwiseInterpolationValue CSSBasicShapeInterpolationType::MaybeMergeSingles(
InterpolationValue&& start,
InterpolationValue&& end) const {
if (!basic_shape_interpolation_functions::ShapesAreCompatible(
*start.non_interpolable_value, *end.non_interpolable_value))
return nullptr;
return PairwiseInterpolationValue(std::move(start.interpolable_value),
std::move(end.interpolable_value),
std::move(start.non_interpolable_value));
}
InterpolationValue
CSSBasicShapeInterpolationType::MaybeConvertStandardPropertyUnderlyingValue(
const ComputedStyle& style) const {
return basic_shape_interpolation_functions::MaybeConvertBasicShape(
GetBasicShape(CssProperty(), style), style.EffectiveZoom());
}
void CSSBasicShapeInterpolationType::Composite(
UnderlyingValueOwner& underlying_value_owner,
double underlying_fraction,
const InterpolationValue& value,
double interpolation_fraction) const {
if (!basic_shape_interpolation_functions::ShapesAreCompatible(
*underlying_value_owner.Value().non_interpolable_value,
*value.non_interpolable_value)) {
underlying_value_owner.Set(*this, value);
return;
}
underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
underlying_fraction, *value.interpolable_value);
}
void CSSBasicShapeInterpolationType::ApplyStandardPropertyValue(
const InterpolableValue& interpolable_value,
const NonInterpolableValue* non_interpolable_value,
StyleResolverState& state) const {
scoped_refptr<BasicShape> shape =
basic_shape_interpolation_functions::CreateBasicShape(
interpolable_value, *non_interpolable_value,
state.CssToLengthConversionData());
switch (CssProperty().PropertyID()) {
case CSSPropertyID::kShapeOutside:
state.StyleBuilder().SetShapeOutside(MakeGarbageCollected<ShapeValue>(
std::move(shape), CSSBoxType::kMissing));
break;
case CSSPropertyID::kOffsetPath:
// TODO(sakhapov): handle coord box.
state.StyleBuilder().SetOffsetPath(
MakeGarbageCollected<ShapeOffsetPathOperation>(std::move(shape),
CoordBox::kBorderBox));
break;
case CSSPropertyID::kClipPath:
// TODO(pdr): Handle geometry box.
state.StyleBuilder().SetClipPath(
MakeGarbageCollected<ShapeClipPathOperation>(
std::move(shape), GeometryBox::kBorderBox));
break;
case CSSPropertyID::kObjectViewBox:
state.StyleBuilder().SetObjectViewBox(std::move(shape));
break;
default:
NOTREACHED();
break;
}
}
} // namespace blink