blob: 4aac7a767a1c21582625ff9317596f218ed0abec [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 "third_party/blink/renderer/core/animation/css_visibility_interpolation_type.h"
#include <memory>
#include "base/memory/ptr_util.h"
#include "third_party/blink/renderer/core/css/css_primitive_value_mappings.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 {
class CSSVisibilityNonInterpolableValue : public NonInterpolableValue {
public:
~CSSVisibilityNonInterpolableValue() final = default;
static scoped_refptr<CSSVisibilityNonInterpolableValue> Create(
EVisibility start,
EVisibility end) {
return base::AdoptRef(new CSSVisibilityNonInterpolableValue(start, end));
}
EVisibility Visibility() const {
DCHECK(is_single_);
return start_;
}
EVisibility Visibility(double fraction) const {
if (is_single_ || fraction <= 0)
return start_;
if (fraction >= 1)
return end_;
DCHECK(start_ == EVisibility::kVisible || end_ == EVisibility::kVisible);
return EVisibility::kVisible;
}
DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
private:
CSSVisibilityNonInterpolableValue(EVisibility start, EVisibility end)
: start_(start), end_(end), is_single_(start_ == end_) {}
const EVisibility start_;
const EVisibility end_;
const bool is_single_;
};
DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSVisibilityNonInterpolableValue);
DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSVisibilityNonInterpolableValue);
class UnderlyingVisibilityChecker
: public CSSInterpolationType::CSSConversionChecker {
public:
~UnderlyingVisibilityChecker() final = default;
static std::unique_ptr<UnderlyingVisibilityChecker> Create(
EVisibility visibility) {
return base::WrapUnique(new UnderlyingVisibilityChecker(visibility));
}
private:
UnderlyingVisibilityChecker(EVisibility visibility)
: visibility_(visibility) {}
bool IsValid(const StyleResolverState&,
const InterpolationValue& underlying) const final {
double underlying_fraction =
ToInterpolableNumber(*underlying.interpolable_value).Value();
EVisibility underlying_visibility =
ToCSSVisibilityNonInterpolableValue(*underlying.non_interpolable_value)
.Visibility(underlying_fraction);
return visibility_ == underlying_visibility;
}
const EVisibility visibility_;
};
class InheritedVisibilityChecker
: public CSSInterpolationType::CSSConversionChecker {
public:
static std::unique_ptr<InheritedVisibilityChecker> Create(
EVisibility visibility) {
return base::WrapUnique(new InheritedVisibilityChecker(visibility));
}
private:
InheritedVisibilityChecker(EVisibility visibility)
: visibility_(visibility) {}
bool IsValid(const StyleResolverState& state,
const InterpolationValue& underlying) const final {
return visibility_ == state.ParentStyle()->Visibility();
}
const EVisibility visibility_;
};
InterpolationValue CSSVisibilityInterpolationType::CreateVisibilityValue(
EVisibility visibility) const {
return InterpolationValue(
InterpolableNumber::Create(0),
CSSVisibilityNonInterpolableValue::Create(visibility, visibility));
}
InterpolationValue CSSVisibilityInterpolationType::MaybeConvertNeutral(
const InterpolationValue& underlying,
ConversionCheckers& conversion_checkers) const {
double underlying_fraction =
ToInterpolableNumber(*underlying.interpolable_value).Value();
EVisibility underlying_visibility =
ToCSSVisibilityNonInterpolableValue(*underlying.non_interpolable_value)
.Visibility(underlying_fraction);
conversion_checkers.push_back(
UnderlyingVisibilityChecker::Create(underlying_visibility));
return CreateVisibilityValue(underlying_visibility);
}
InterpolationValue CSSVisibilityInterpolationType::MaybeConvertInitial(
const StyleResolverState&,
ConversionCheckers&) const {
return CreateVisibilityValue(EVisibility::kVisible);
}
InterpolationValue CSSVisibilityInterpolationType::MaybeConvertInherit(
const StyleResolverState& state,
ConversionCheckers& conversion_checkers) const {
if (!state.ParentStyle())
return nullptr;
EVisibility inherited_visibility = state.ParentStyle()->Visibility();
conversion_checkers.push_back(
InheritedVisibilityChecker::Create(inherited_visibility));
return CreateVisibilityValue(inherited_visibility);
}
InterpolationValue CSSVisibilityInterpolationType::MaybeConvertValue(
const CSSValue& value,
const StyleResolverState*,
ConversionCheckers& conversion_checkers) const {
if (!value.IsIdentifierValue())
return nullptr;
const CSSIdentifierValue& identifier_value = ToCSSIdentifierValue(value);
CSSValueID keyword = identifier_value.GetValueID();
switch (keyword) {
case CSSValueHidden:
case CSSValueVisible:
case CSSValueCollapse:
return CreateVisibilityValue(identifier_value.ConvertTo<EVisibility>());
default:
return nullptr;
}
}
InterpolationValue
CSSVisibilityInterpolationType::MaybeConvertStandardPropertyUnderlyingValue(
const ComputedStyle& style) const {
return CreateVisibilityValue(style.Visibility());
}
PairwiseInterpolationValue CSSVisibilityInterpolationType::MaybeMergeSingles(
InterpolationValue&& start,
InterpolationValue&& end) const {
EVisibility start_visibility =
ToCSSVisibilityNonInterpolableValue(*start.non_interpolable_value)
.Visibility();
EVisibility end_visibility =
ToCSSVisibilityNonInterpolableValue(*end.non_interpolable_value)
.Visibility();
// One side must be "visible".
// Spec: https://drafts.csswg.org/css-transitions/#animtype-visibility
if (start_visibility != end_visibility &&
start_visibility != EVisibility::kVisible &&
end_visibility != EVisibility::kVisible) {
return nullptr;
}
return PairwiseInterpolationValue(InterpolableNumber::Create(0),
InterpolableNumber::Create(1),
CSSVisibilityNonInterpolableValue::Create(
start_visibility, end_visibility));
}
void CSSVisibilityInterpolationType::Composite(
UnderlyingValueOwner& underlying_value_owner,
double underlying_fraction,
const InterpolationValue& value,
double interpolation_fraction) const {
underlying_value_owner.Set(*this, value);
}
void CSSVisibilityInterpolationType::ApplyStandardPropertyValue(
const InterpolableValue& interpolable_value,
const NonInterpolableValue* non_interpolable_value,
StyleResolverState& state) const {
// Visibility interpolation has been deferred to application time here due to
// its non-linear behaviour.
double fraction = ToInterpolableNumber(interpolable_value).Value();
EVisibility visibility =
ToCSSVisibilityNonInterpolableValue(non_interpolable_value)
->Visibility(fraction);
state.Style()->SetVisibility(visibility);
}
} // namespace blink