blob: caf47737bc4ec1187e9a42f99d5d16673c9d4354 [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/CSSVisibilityInterpolationType.h"
#include "core/css/CSSPrimitiveValueMappings.h"
#include "core/css/resolver/StyleResolverState.h"
#include "wtf/PtrUtil.h"
#include <memory>
namespace blink {
class CSSVisibilityNonInterpolableValue : public NonInterpolableValue {
public:
~CSSVisibilityNonInterpolableValue() final { }
static PassRefPtr<CSSVisibilityNonInterpolableValue> create(EVisibility start, EVisibility end)
{
return adoptRef(new CSSVisibilityNonInterpolableValue(start, end));
}
EVisibility visibility() const
{
ASSERT(m_isSingle);
return m_start;
}
EVisibility visibility(double fraction) const
{
if (m_isSingle || fraction <= 0)
return m_start;
if (fraction >= 1)
return m_end;
if (m_start == EVisibility::Visible || m_end == EVisibility::Visible)
return EVisibility::Visible;
return fraction < 0.5 ? m_start : m_end;
}
DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
private:
CSSVisibilityNonInterpolableValue(EVisibility start, EVisibility end)
: m_start(start)
, m_end(end)
, m_isSingle(m_start == m_end)
{ }
const EVisibility m_start;
const EVisibility m_end;
const bool m_isSingle;
};
DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSVisibilityNonInterpolableValue);
DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSVisibilityNonInterpolableValue);
class UnderlyingVisibilityChecker : public InterpolationType::ConversionChecker {
public:
~UnderlyingVisibilityChecker() final {}
static std::unique_ptr<UnderlyingVisibilityChecker> create(EVisibility visibility)
{
return wrapUnique(new UnderlyingVisibilityChecker(visibility));
}
private:
UnderlyingVisibilityChecker(EVisibility visibility)
: m_visibility(visibility)
{ }
bool isValid(const InterpolationEnvironment&, const InterpolationValue& underlying) const final
{
double underlyingFraction = toInterpolableNumber(*underlying.interpolableValue).value();
EVisibility underlyingVisibility = toCSSVisibilityNonInterpolableValue(*underlying.nonInterpolableValue).visibility(underlyingFraction);
return m_visibility == underlyingVisibility;
}
const EVisibility m_visibility;
};
class ParentVisibilityChecker : public InterpolationType::ConversionChecker {
public:
static std::unique_ptr<ParentVisibilityChecker> create(EVisibility visibility)
{
return wrapUnique(new ParentVisibilityChecker(visibility));
}
private:
ParentVisibilityChecker(EVisibility visibility)
: m_visibility(visibility)
{ }
bool isValid(const InterpolationEnvironment& environment, const InterpolationValue& underlying) const final
{
return m_visibility == environment.state().parentStyle()->visibility();
}
const EVisibility m_visibility;
};
InterpolationValue CSSVisibilityInterpolationType::createVisibilityValue(EVisibility visibility) const
{
return InterpolationValue(InterpolableNumber::create(0), CSSVisibilityNonInterpolableValue::create(visibility, visibility));
}
InterpolationValue CSSVisibilityInterpolationType::maybeConvertNeutral(const InterpolationValue& underlying, ConversionCheckers& conversionCheckers) const
{
double underlyingFraction = toInterpolableNumber(*underlying.interpolableValue).value();
EVisibility underlyingVisibility = toCSSVisibilityNonInterpolableValue(*underlying.nonInterpolableValue).visibility(underlyingFraction);
conversionCheckers.append(UnderlyingVisibilityChecker::create(underlyingVisibility));
return createVisibilityValue(underlyingVisibility);
}
InterpolationValue CSSVisibilityInterpolationType::maybeConvertInitial(const StyleResolverState&, ConversionCheckers&) const
{
return createVisibilityValue(EVisibility::Visible);
}
InterpolationValue CSSVisibilityInterpolationType::maybeConvertInherit(const StyleResolverState& state, ConversionCheckers& conversionCheckers) const
{
if (!state.parentStyle())
return nullptr;
EVisibility inheritedVisibility = state.parentStyle()->visibility();
conversionCheckers.append(ParentVisibilityChecker::create(inheritedVisibility));
return createVisibilityValue(inheritedVisibility);
}
InterpolationValue CSSVisibilityInterpolationType::maybeConvertValue(const CSSValue& value, const StyleResolverState& state, ConversionCheckers& conversionCheckers) const
{
if (!value.isPrimitiveValue())
return nullptr;
const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value);
CSSValueID keyword = primitiveValue.getValueID();
switch (keyword) {
case CSSValueHidden:
case CSSValueVisible:
case CSSValueCollapse:
return createVisibilityValue(primitiveValue.convertTo<EVisibility>());
default:
return nullptr;
}
}
InterpolationValue CSSVisibilityInterpolationType::maybeConvertUnderlyingValue(const InterpolationEnvironment& environment) const
{
return createVisibilityValue(environment.state().style()->visibility());
}
PairwiseInterpolationValue CSSVisibilityInterpolationType::maybeMergeSingles(InterpolationValue&& start, InterpolationValue&& end) const
{
return PairwiseInterpolationValue(
InterpolableNumber::create(0),
InterpolableNumber::create(1),
CSSVisibilityNonInterpolableValue::create(
toCSSVisibilityNonInterpolableValue(*start.nonInterpolableValue).visibility(),
toCSSVisibilityNonInterpolableValue(*end.nonInterpolableValue).visibility()));
}
void CSSVisibilityInterpolationType::composite(UnderlyingValueOwner& underlyingValueOwner, double underlyingFraction, const InterpolationValue& value, double interpolationFraction) const
{
underlyingValueOwner.set(*this, value);
}
void CSSVisibilityInterpolationType::apply(const InterpolableValue& interpolableValue, const NonInterpolableValue* nonInterpolableValue, InterpolationEnvironment& environment) const
{
// Visibility interpolation has been deferred to application time here due to its non-linear behaviour.
double fraction = toInterpolableNumber(interpolableValue).value();
EVisibility visibility = toCSSVisibilityNonInterpolableValue(nonInterpolableValue)->visibility(fraction);
environment.state().style()->setVisibility(visibility);
}
} // namespace blink