blob: e15d0981cfc82e652349dbde6655229265da7b35 [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 "core/animation/CSSClipInterpolationType.h"
#include "core/animation/LengthInterpolationFunctions.h"
#include "core/css/CSSIdentifierValue.h"
#include "core/css/CSSQuadValue.h"
#include "core/css/resolver/StyleResolverState.h"
#include "wtf/PtrUtil.h"
#include <memory>
namespace blink {
struct ClipAutos {
ClipAutos()
: isAuto(true),
isTopAuto(false),
isRightAuto(false),
isBottomAuto(false),
isLeftAuto(false) {}
ClipAutos(bool isTopAuto,
bool isRightAuto,
bool isBottomAuto,
bool isLeftAuto)
: isAuto(false),
isTopAuto(isTopAuto),
isRightAuto(isRightAuto),
isBottomAuto(isBottomAuto),
isLeftAuto(isLeftAuto) {}
explicit ClipAutos(const LengthBox& clip)
: isAuto(false),
isTopAuto(clip.top().isAuto()),
isRightAuto(clip.right().isAuto()),
isBottomAuto(clip.bottom().isAuto()),
isLeftAuto(clip.left().isAuto()) {}
bool operator==(const ClipAutos& other) const {
return isAuto == other.isAuto && isTopAuto == other.isTopAuto &&
isRightAuto == other.isRightAuto &&
isBottomAuto == other.isBottomAuto && isLeftAuto == other.isLeftAuto;
}
bool operator!=(const ClipAutos& other) const { return !(*this == other); }
bool isAuto;
bool isTopAuto;
bool isRightAuto;
bool isBottomAuto;
bool isLeftAuto;
};
static ClipAutos getClipAutos(const ComputedStyle& style) {
if (style.hasAutoClip())
return ClipAutos();
return ClipAutos(style.clipTop().isAuto(), style.clipRight().isAuto(),
style.clipBottom().isAuto(), style.clipLeft().isAuto());
}
class InheritedAutosChecker : public InterpolationType::ConversionChecker {
public:
static std::unique_ptr<InheritedAutosChecker> create(
const ClipAutos& inheritedAutos) {
return WTF::wrapUnique(new InheritedAutosChecker(inheritedAutos));
}
private:
InheritedAutosChecker(const ClipAutos& inheritedAutos)
: m_inheritedAutos(inheritedAutos) {}
bool isValid(const InterpolationEnvironment& environment,
const InterpolationValue& underlying) const final {
return m_inheritedAutos == getClipAutos(*environment.state().parentStyle());
}
const ClipAutos m_inheritedAutos;
};
class CSSClipNonInterpolableValue : public NonInterpolableValue {
public:
~CSSClipNonInterpolableValue() final {}
static PassRefPtr<CSSClipNonInterpolableValue> create(
const ClipAutos& clipAutos) {
return adoptRef(new CSSClipNonInterpolableValue(clipAutos));
}
const ClipAutos& clipAutos() const { return m_clipAutos; }
DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
private:
CSSClipNonInterpolableValue(const ClipAutos& clipAutos)
: m_clipAutos(clipAutos) {
DCHECK(!m_clipAutos.isAuto);
}
const ClipAutos m_clipAutos;
};
DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSClipNonInterpolableValue);
DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSClipNonInterpolableValue);
class UnderlyingAutosChecker : public InterpolationType::ConversionChecker {
public:
~UnderlyingAutosChecker() final {}
static std::unique_ptr<UnderlyingAutosChecker> create(
const ClipAutos& underlyingAutos) {
return WTF::wrapUnique(new UnderlyingAutosChecker(underlyingAutos));
}
static ClipAutos getUnderlyingAutos(const InterpolationValue& underlying) {
if (!underlying)
return ClipAutos();
return toCSSClipNonInterpolableValue(*underlying.nonInterpolableValue)
.clipAutos();
}
private:
UnderlyingAutosChecker(const ClipAutos& underlyingAutos)
: m_underlyingAutos(underlyingAutos) {}
bool isValid(const InterpolationEnvironment&,
const InterpolationValue& underlying) const final {
return m_underlyingAutos == getUnderlyingAutos(underlying);
}
const ClipAutos m_underlyingAutos;
};
enum ClipComponentIndex : unsigned {
ClipTop,
ClipRight,
ClipBottom,
ClipLeft,
ClipComponentIndexCount,
};
static std::unique_ptr<InterpolableValue> convertClipComponent(
const Length& length,
double zoom) {
if (length.isAuto())
return InterpolableList::create(0);
return LengthInterpolationFunctions::maybeConvertLength(length, zoom)
.interpolableValue;
}
static InterpolationValue createClipValue(const LengthBox& clip, double zoom) {
std::unique_ptr<InterpolableList> list =
InterpolableList::create(ClipComponentIndexCount);
list->set(ClipTop, convertClipComponent(clip.top(), zoom));
list->set(ClipRight, convertClipComponent(clip.right(), zoom));
list->set(ClipBottom, convertClipComponent(clip.bottom(), zoom));
list->set(ClipLeft, convertClipComponent(clip.left(), zoom));
return InterpolationValue(
std::move(list), CSSClipNonInterpolableValue::create(ClipAutos(clip)));
}
InterpolationValue CSSClipInterpolationType::maybeConvertNeutral(
const InterpolationValue& underlying,
ConversionCheckers& conversionCheckers) const {
ClipAutos underlyingAutos =
UnderlyingAutosChecker::getUnderlyingAutos(underlying);
conversionCheckers.push_back(UnderlyingAutosChecker::create(underlyingAutos));
if (underlyingAutos.isAuto)
return nullptr;
LengthBox neutralBox(
underlyingAutos.isTopAuto ? Length(Auto) : Length(0, Fixed),
underlyingAutos.isRightAuto ? Length(Auto) : Length(0, Fixed),
underlyingAutos.isBottomAuto ? Length(Auto) : Length(0, Fixed),
underlyingAutos.isLeftAuto ? Length(Auto) : Length(0, Fixed));
return createClipValue(neutralBox, 1);
}
InterpolationValue CSSClipInterpolationType::maybeConvertInitial(
const StyleResolverState&,
ConversionCheckers&) const {
return nullptr;
}
InterpolationValue CSSClipInterpolationType::maybeConvertInherit(
const StyleResolverState& state,
ConversionCheckers& conversionCheckers) const {
ClipAutos inheritedAutos = getClipAutos(*state.parentStyle());
conversionCheckers.push_back(InheritedAutosChecker::create(inheritedAutos));
if (inheritedAutos.isAuto)
return nullptr;
return createClipValue(state.parentStyle()->clip(),
state.parentStyle()->effectiveZoom());
}
static bool isCSSAuto(const CSSValue& value) {
return value.isIdentifierValue() &&
toCSSIdentifierValue(value).getValueID() == CSSValueAuto;
}
static std::unique_ptr<InterpolableValue> convertClipComponent(
const CSSValue& length) {
if (isCSSAuto(length))
return InterpolableList::create(0);
return LengthInterpolationFunctions::maybeConvertCSSValue(length)
.interpolableValue;
}
InterpolationValue CSSClipInterpolationType::maybeConvertValue(
const CSSValue& value,
const StyleResolverState& state,
ConversionCheckers&) const {
if (!value.isQuadValue())
return nullptr;
const CSSQuadValue& quad = toCSSQuadValue(value);
std::unique_ptr<InterpolableList> list =
InterpolableList::create(ClipComponentIndexCount);
list->set(ClipTop, convertClipComponent(*quad.top()));
list->set(ClipRight, convertClipComponent(*quad.right()));
list->set(ClipBottom, convertClipComponent(*quad.bottom()));
list->set(ClipLeft, convertClipComponent(*quad.left()));
ClipAutos autos(isCSSAuto(*quad.top()), isCSSAuto(*quad.right()),
isCSSAuto(*quad.bottom()), isCSSAuto(*quad.left()));
return InterpolationValue(std::move(list),
CSSClipNonInterpolableValue::create(autos));
}
InterpolationValue
CSSClipInterpolationType::maybeConvertStandardPropertyUnderlyingValue(
const StyleResolverState& state) const {
if (state.style()->hasAutoClip())
return nullptr;
return createClipValue(state.style()->clip(), state.style()->effectiveZoom());
}
PairwiseInterpolationValue CSSClipInterpolationType::maybeMergeSingles(
InterpolationValue&& start,
InterpolationValue&& end) const {
const ClipAutos& startAutos =
toCSSClipNonInterpolableValue(*start.nonInterpolableValue).clipAutos();
const ClipAutos& endAutos =
toCSSClipNonInterpolableValue(*end.nonInterpolableValue).clipAutos();
if (startAutos != endAutos)
return nullptr;
return PairwiseInterpolationValue(std::move(start.interpolableValue),
std::move(end.interpolableValue),
start.nonInterpolableValue.release());
}
void CSSClipInterpolationType::composite(
UnderlyingValueOwner& underlyingValueOwner,
double underlyingFraction,
const InterpolationValue& value,
double interpolationFraction) const {
const ClipAutos& underlyingAutos =
toCSSClipNonInterpolableValue(
*underlyingValueOwner.value().nonInterpolableValue)
.clipAutos();
const ClipAutos& autos =
toCSSClipNonInterpolableValue(*value.nonInterpolableValue).clipAutos();
if (underlyingAutos == autos)
underlyingValueOwner.mutableValue().interpolableValue->scaleAndAdd(
underlyingFraction, *value.interpolableValue);
else
underlyingValueOwner.set(*this, value);
}
void CSSClipInterpolationType::applyStandardPropertyValue(
const InterpolableValue& interpolableValue,
const NonInterpolableValue* nonInterpolableValue,
StyleResolverState& state) const {
const ClipAutos& autos =
toCSSClipNonInterpolableValue(nonInterpolableValue)->clipAutos();
const InterpolableList& list = toInterpolableList(interpolableValue);
const auto& convertIndex = [&list, &state](bool isAuto, size_t index) {
if (isAuto)
return Length(Auto);
return LengthInterpolationFunctions::createLength(
*list.get(index), nullptr, state.cssToLengthConversionData(),
ValueRangeAll);
};
state.style()->setClip(LengthBox(convertIndex(autos.isTopAuto, ClipTop),
convertIndex(autos.isRightAuto, ClipRight),
convertIndex(autos.isBottomAuto, ClipBottom),
convertIndex(autos.isLeftAuto, ClipLeft)));
}
} // namespace blink