blob: 97cdbac1515124690b3606cb6589c99b9dc7f046 [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 "third_party/blink/renderer/core/animation/css_text_indent_interpolation_type.h"
#include <memory>
#include <utility>
#include "base/memory/ptr_util.h"
#include "third_party/blink/renderer/core/animation/interpolable_length.h"
#include "third_party/blink/renderer/core/css/css_identifier_value.h"
#include "third_party/blink/renderer/core/css/css_primitive_value.h"
#include "third_party/blink/renderer/core/css/css_value_list.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 {
namespace {
struct IndentMode {
IndentMode(const TextIndentLine line, const TextIndentType type)
: line(line), type(type) {}
explicit IndentMode(const ComputedStyle& style)
: line(style.GetTextIndentLine()), type(style.GetTextIndentType()) {}
bool operator==(const IndentMode& other) const {
return line == other.line && type == other.type;
}
bool operator!=(const IndentMode& other) const { return !(*this == other); }
const TextIndentLine line;
const TextIndentType type;
};
} // namespace
class CSSTextIndentNonInterpolableValue : public NonInterpolableValue {
public:
static scoped_refptr<CSSTextIndentNonInterpolableValue> Create(
scoped_refptr<const NonInterpolableValue> length_non_interpolable_value,
const IndentMode& mode) {
return base::AdoptRef(new CSSTextIndentNonInterpolableValue(
std::move(length_non_interpolable_value), mode));
}
const NonInterpolableValue* LengthNonInterpolableValue() const {
return length_non_interpolable_value_.get();
}
const IndentMode& Mode() const { return mode_; }
DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
private:
CSSTextIndentNonInterpolableValue(
scoped_refptr<const NonInterpolableValue> length_non_interpolable_value,
const IndentMode& mode)
: length_non_interpolable_value_(
std::move(length_non_interpolable_value)),
mode_(mode) {}
scoped_refptr<const NonInterpolableValue> length_non_interpolable_value_;
const IndentMode mode_;
};
DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSTextIndentNonInterpolableValue);
template <>
struct DowncastTraits<CSSTextIndentNonInterpolableValue> {
static bool AllowFrom(const NonInterpolableValue* value) {
return value && AllowFrom(*value);
}
static bool AllowFrom(const NonInterpolableValue& value) {
return value.GetType() == CSSTextIndentNonInterpolableValue::static_type_;
}
};
namespace {
class UnderlyingIndentModeChecker
: public CSSInterpolationType::CSSConversionChecker {
public:
explicit UnderlyingIndentModeChecker(const IndentMode& mode) : mode_(mode) {}
bool IsValid(const StyleResolverState&,
const InterpolationValue& underlying) const final {
return mode_ == To<CSSTextIndentNonInterpolableValue>(
*underlying.non_interpolable_value)
.Mode();
}
private:
const IndentMode mode_;
};
class InheritedIndentChecker
: public CSSInterpolationType::CSSConversionChecker {
public:
InheritedIndentChecker(const Length& length, const IndentMode& mode)
: length_(length), mode_(mode) {}
bool IsValid(const StyleResolverState& state,
const InterpolationValue&) const final {
return length_ == state.ParentStyle()->TextIndent() &&
mode_ == IndentMode(*state.ParentStyle());
}
private:
const Length length_;
const IndentMode mode_;
};
InterpolationValue CreateValue(const Length& length,
const IndentMode& mode,
double zoom) {
InterpolationValue converted_length(
InterpolableLength::MaybeConvertLength(length, zoom));
DCHECK(converted_length);
return InterpolationValue(
std::move(converted_length.interpolable_value),
CSSTextIndentNonInterpolableValue::Create(
std::move(converted_length.non_interpolable_value), mode));
}
} // namespace
InterpolationValue CSSTextIndentInterpolationType::MaybeConvertNeutral(
const InterpolationValue& underlying,
ConversionCheckers& conversion_checkers) const {
IndentMode mode =
To<CSSTextIndentNonInterpolableValue>(*underlying.non_interpolable_value)
.Mode();
conversion_checkers.push_back(
std::make_unique<UnderlyingIndentModeChecker>(mode));
return CreateValue(Length::Fixed(0), mode, 1);
}
InterpolationValue CSSTextIndentInterpolationType::MaybeConvertInitial(
const StyleResolverState&,
ConversionCheckers&) const {
IndentMode mode(ComputedStyleInitialValues::InitialTextIndentLine(),
ComputedStyleInitialValues::InitialTextIndentType());
return CreateValue(ComputedStyleInitialValues::InitialTextIndent(), mode, 1);
}
InterpolationValue CSSTextIndentInterpolationType::MaybeConvertInherit(
const StyleResolverState& state,
ConversionCheckers& conversion_checkers) const {
const ComputedStyle& parent_style = *state.ParentStyle();
IndentMode mode(parent_style);
conversion_checkers.push_back(std::make_unique<InheritedIndentChecker>(
parent_style.TextIndent(), mode));
return CreateValue(parent_style.TextIndent(), mode,
parent_style.EffectiveZoom());
}
InterpolationValue CSSTextIndentInterpolationType::MaybeConvertValue(
const CSSValue& value,
const StyleResolverState*,
ConversionCheckers&) const {
InterpolationValue length = nullptr;
TextIndentLine line = ComputedStyleInitialValues::InitialTextIndentLine();
TextIndentType type = ComputedStyleInitialValues::InitialTextIndentType();
for (const auto& item : To<CSSValueList>(value)) {
auto* identifier_value = DynamicTo<CSSIdentifierValue>(item.Get());
if (identifier_value &&
identifier_value->GetValueID() == CSSValueID::kEachLine) {
line = TextIndentLine::kEachLine;
} else if (identifier_value &&
identifier_value->GetValueID() == CSSValueID::kHanging) {
type = TextIndentType::kHanging;
} else {
length =
InterpolationValue(InterpolableLength::MaybeConvertCSSValue(*item));
}
}
DCHECK(length);
return InterpolationValue(
std::move(length.interpolable_value),
CSSTextIndentNonInterpolableValue::Create(
std::move(length.non_interpolable_value), IndentMode(line, type)));
}
InterpolationValue
CSSTextIndentInterpolationType::MaybeConvertStandardPropertyUnderlyingValue(
const ComputedStyle& style) const {
return CreateValue(style.TextIndent(), IndentMode(style),
style.EffectiveZoom());
}
PairwiseInterpolationValue CSSTextIndentInterpolationType::MaybeMergeSingles(
InterpolationValue&& start,
InterpolationValue&& end) const {
const auto& start_non_interpolable_value =
To<CSSTextIndentNonInterpolableValue>(*start.non_interpolable_value);
const auto& end_non_interpolable_value =
To<CSSTextIndentNonInterpolableValue>(*end.non_interpolable_value);
if (start_non_interpolable_value.Mode() != end_non_interpolable_value.Mode())
return nullptr;
PairwiseInterpolationValue result = InterpolableLength::MergeSingles(
std::move(start.interpolable_value), std::move(end.interpolable_value));
result.non_interpolable_value = CSSTextIndentNonInterpolableValue::Create(
std::move(result.non_interpolable_value),
start_non_interpolable_value.Mode());
return result;
}
void CSSTextIndentInterpolationType::Composite(
UnderlyingValueOwner& underlying_value_owner,
double underlying_fraction,
const InterpolationValue& value,
double interpolation_fraction) const {
const IndentMode& underlying_mode =
To<CSSTextIndentNonInterpolableValue>(
*underlying_value_owner.Value().non_interpolable_value)
.Mode();
const auto& non_interpolable_value =
To<CSSTextIndentNonInterpolableValue>(*value.non_interpolable_value);
const IndentMode& mode = non_interpolable_value.Mode();
if (underlying_mode != mode) {
underlying_value_owner.Set(*this, value);
return;
}
underlying_value_owner.MutableInterpolableValue().ScaleAndAdd(
underlying_fraction, *value.interpolable_value);
}
void CSSTextIndentInterpolationType::ApplyStandardPropertyValue(
const InterpolableValue& interpolable_value,
const NonInterpolableValue* non_interpolable_value,
StyleResolverState& state) const {
const auto& css_text_indent_non_interpolable_value =
To<CSSTextIndentNonInterpolableValue>(*non_interpolable_value);
ComputedStyle& style = *state.Style();
style.SetTextIndent(
To<InterpolableLength>(interpolable_value)
.CreateLength(state.CssToLengthConversionData(), kValueRangeAll));
const IndentMode& mode = css_text_indent_non_interpolable_value.Mode();
style.SetTextIndentLine(mode.line);
style.SetTextIndentType(mode.type);
}
} // namespace blink