blob: 122375ec80133e94e08e0729c86bf92ee4df0717 [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_interpolation_type.h"
#include <memory>
#include <utility>
#include "base/memory/ptr_util.h"
#include "third_party/blink/renderer/core/animation/css_interpolation_environment.h"
#include "third_party/blink/renderer/core/animation/string_keyframe.h"
#include "third_party/blink/renderer/core/css/computed_style_css_value_mapping.h"
#include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
#include "third_party/blink/renderer/core/css/css_inherited_value.h"
#include "third_party/blink/renderer/core/css/css_initial_value.h"
#include "third_party/blink/renderer/core/css/css_revert_value.h"
#include "third_party/blink/renderer/core/css/css_unset_value.h"
#include "third_party/blink/renderer/core/css/css_value.h"
#include "third_party/blink/renderer/core/css/css_variable_reference_value.h"
#include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
#include "third_party/blink/renderer/core/css/properties/css_property.h"
#include "third_party/blink/renderer/core/css/property_registration.h"
#include "third_party/blink/renderer/core/css/resolver/style_builder.h"
#include "third_party/blink/renderer/core/css/resolver/style_cascade.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
#include "third_party/blink/renderer/core/css/scoped_css_value.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/data_equivalency.h"
#include "third_party/blink/renderer/core/style_property_shorthand.h"
namespace blink {
class ResolvedVariableChecker : public CSSInterpolationType::ConversionChecker {
public:
ResolvedVariableChecker(CSSPropertyID property,
const CSSValue* variable_reference,
const CSSValue* resolved_value)
: property_(property),
variable_reference_(variable_reference),
resolved_value_(resolved_value) {}
private:
bool IsValid(const InterpolationEnvironment& environment,
const InterpolationValue&) const final {
const auto& css_environment = To<CSSInterpolationEnvironment>(environment);
// TODO(alancutter): Just check the variables referenced instead of doing a
// full CSSValue resolve.
const CSSValue* resolved_value = css_environment.Resolve(
PropertyHandle(CSSProperty::Get(property_)), variable_reference_);
return DataEquivalent(resolved_value_.Get(), resolved_value);
}
CSSPropertyID property_;
Persistent<const CSSValue> variable_reference_;
Persistent<const CSSValue> resolved_value_;
};
class InheritedCustomPropertyChecker
: public CSSInterpolationType::CSSConversionChecker {
public:
InheritedCustomPropertyChecker(const AtomicString& name,
bool is_inherited_property,
const CSSValue* inherited_value,
const CSSValue* initial_value)
: name_(name),
is_inherited_property_(is_inherited_property),
inherited_value_(inherited_value),
initial_value_(initial_value) {}
private:
bool IsValid(const StyleResolverState& state,
const InterpolationValue&) const final {
const CSSValue* inherited_value =
state.ParentStyle()->GetVariableValue(name_, is_inherited_property_);
if (!inherited_value) {
inherited_value = initial_value_.Get();
}
return DataEquivalent(inherited_value_.Get(), inherited_value);
}
const AtomicString& name_;
const bool is_inherited_property_;
Persistent<const CSSValue> inherited_value_;
Persistent<const CSSValue> initial_value_;
};
class ResolvedRegisteredCustomPropertyChecker
: public InterpolationType::ConversionChecker {
public:
ResolvedRegisteredCustomPropertyChecker(
const CSSCustomPropertyDeclaration& declaration,
scoped_refptr<CSSVariableData> resolved_tokens)
: declaration_(declaration),
resolved_tokens_(std::move(resolved_tokens)) {}
private:
bool IsValid(const InterpolationEnvironment& environment,
const InterpolationValue&) const final {
const auto& css_environment = To<CSSInterpolationEnvironment>(environment);
const CSSValue* resolved = css_environment.Resolve(
PropertyHandle(declaration_->GetName()), declaration_);
scoped_refptr<CSSVariableData> resolved_tokens = nullptr;
if (const auto* decl = DynamicTo<CSSCustomPropertyDeclaration>(resolved))
resolved_tokens = decl->Value();
return DataEquivalent(resolved_tokens, resolved_tokens_);
}
Persistent<const CSSCustomPropertyDeclaration> declaration_;
scoped_refptr<CSSVariableData> resolved_tokens_;
};
class RevertChecker : public CSSInterpolationType::ConversionChecker {
public:
RevertChecker(const PropertyHandle& property_handle,
const CSSValue* resolved_value)
: property_handle_(property_handle), resolved_value_(resolved_value) {
}
private:
bool IsValid(const InterpolationEnvironment& environment,
const InterpolationValue&) const final {
const auto& css_environment = To<CSSInterpolationEnvironment>(environment);
const CSSValue* current_resolved_value = css_environment.Resolve(
property_handle_, cssvalue::CSSRevertValue::Create());
return DataEquivalent(resolved_value_.Get(), current_resolved_value);
}
PropertyHandle property_handle_;
Persistent<const CSSValue> resolved_value_;
};
CSSInterpolationType::CSSInterpolationType(
PropertyHandle property,
const PropertyRegistration* registration)
: InterpolationType(property), registration_(registration) {
DCHECK(!GetProperty().IsCSSCustomProperty() || registration);
DCHECK(!CssProperty().IsShorthand());
}
InterpolationValue CSSInterpolationType::MaybeConvertSingle(
const PropertySpecificKeyframe& keyframe,
const InterpolationEnvironment& environment,
const InterpolationValue& underlying,
ConversionCheckers& conversion_checkers) const {
InterpolationValue result = MaybeConvertSingleInternal(
keyframe, environment, underlying, conversion_checkers);
if (result && keyframe.Composite() !=
EffectModel::CompositeOperation::kCompositeReplace) {
return PreInterpolationCompositeIfNeeded(std::move(result), underlying,
keyframe.Composite(),
conversion_checkers);
}
return result;
}
InterpolationValue CSSInterpolationType::MaybeConvertSingleInternal(
const PropertySpecificKeyframe& keyframe,
const InterpolationEnvironment& environment,
const InterpolationValue& underlying,
ConversionCheckers& conversion_checkers) const {
const CSSValue* value = To<CSSPropertySpecificKeyframe>(keyframe).Value();
const auto& css_environment = To<CSSInterpolationEnvironment>(environment);
const StyleResolverState& state = css_environment.GetState();
if (!value)
return MaybeConvertNeutral(underlying, conversion_checkers);
if (GetProperty().IsCSSCustomProperty()) {
return MaybeConvertCustomPropertyDeclaration(
To<CSSCustomPropertyDeclaration>(*value), environment,
conversion_checkers);
}
if (value->IsVariableReferenceValue() ||
value->IsPendingSubstitutionValue()) {
const CSSValue* resolved_value =
css_environment.Resolve(GetProperty(), value);
DCHECK(resolved_value);
conversion_checkers.push_back(std::make_unique<ResolvedVariableChecker>(
CssProperty().PropertyID(), value, resolved_value));
value = resolved_value;
}
if (value->IsRevertValue()) {
value = css_environment.Resolve(GetProperty(), value);
DCHECK(value);
conversion_checkers.push_back(
std::make_unique<RevertChecker>(GetProperty(), value));
}
bool is_inherited = CssProperty().IsInherited();
if (value->IsInitialValue() || (value->IsUnsetValue() && !is_inherited)) {
return MaybeConvertInitial(state, conversion_checkers);
}
if (value->IsInheritedValue() || (value->IsUnsetValue() && is_inherited)) {
return MaybeConvertInherit(state, conversion_checkers);
}
return MaybeConvertValue(*value, &state, conversion_checkers);
}
InterpolationValue CSSInterpolationType::MaybeConvertCustomPropertyDeclaration(
const CSSCustomPropertyDeclaration& declaration,
const InterpolationEnvironment& environment,
ConversionCheckers& conversion_checkers) const {
const auto& css_environment = To<CSSInterpolationEnvironment>(environment);
const StyleResolverState& state = css_environment.GetState();
const AtomicString& name = declaration.GetName();
DCHECK_EQ(GetProperty().CustomPropertyName(), name);
const CSSValue* value = &declaration;
value = css_environment.Resolve(GetProperty(), value);
DCHECK(value) << "CSSVarCycleInterpolationType should have handled nullptr";
if (declaration.IsRevert()) {
conversion_checkers.push_back(
std::make_unique<RevertChecker>(GetProperty(), value));
}
if (const auto* resolved_declaration =
DynamicTo<CSSCustomPropertyDeclaration>(value)) {
// If Resolve returned a different CSSCustomPropertyDeclaration, var()
// references were substituted.
if (resolved_declaration != &declaration) {
conversion_checkers.push_back(
std::make_unique<ResolvedRegisteredCustomPropertyChecker>(
declaration, resolved_declaration->Value()));
}
}
// Unfortunately we transport CSS-wide keywords inside the
// CSSCustomPropertyDeclaration. Expand those keywords into real CSSValues
// if present.
bool is_inherited = Registration().Inherits();
if (const auto* resolved_declaration =
DynamicTo<CSSCustomPropertyDeclaration>(value)) {
if (resolved_declaration->IsInitial(is_inherited))
value = CSSInitialValue::Create();
else if (resolved_declaration->IsInherit(is_inherited))
value = CSSInheritedValue::Create();
}
// Handle CSS-wide keywords (except 'revert', which should have been
// handled already).
DCHECK(!value->IsRevertValue());
if (value->IsInitialValue() || (value->IsUnsetValue() && !is_inherited)) {
value = Registration().Initial();
} else if (value->IsInheritedValue() ||
(value->IsUnsetValue() && is_inherited)) {
value = state.ParentStyle()->GetVariableValue(name, is_inherited);
if (!value) {
value = Registration().Initial();
}
conversion_checkers.push_back(
std::make_unique<InheritedCustomPropertyChecker>(
name, is_inherited, value, Registration().Initial()));
}
if (const auto* resolved_declaration =
DynamicTo<CSSCustomPropertyDeclaration>(value)) {
DCHECK(resolved_declaration->Value());
value = resolved_declaration->Value()->ParseForSyntax(
registration_->Syntax(),
state.GetDocument().GetExecutionContext()->GetSecureContextMode());
if (!value)
return nullptr;
}
DCHECK(value);
return MaybeConvertValue(*value, &state, conversion_checkers);
}
InterpolationValue CSSInterpolationType::MaybeConvertUnderlyingValue(
const InterpolationEnvironment& environment) const {
const ComputedStyle& style =
To<CSSInterpolationEnvironment>(environment).Style();
if (!GetProperty().IsCSSCustomProperty()) {
return MaybeConvertStandardPropertyUnderlyingValue(style);
}
const PropertyHandle property = GetProperty();
const AtomicString& name = property.CustomPropertyName();
const CSSValue* underlying_value =
style.GetVariableValue(name, Registration().Inherits());
if (!underlying_value)
return nullptr;
// TODO(alancutter): Remove the need for passing in conversion checkers.
ConversionCheckers dummy_conversion_checkers;
return MaybeConvertValue(*underlying_value, nullptr,
dummy_conversion_checkers);
}
void CSSInterpolationType::Apply(
const InterpolableValue& interpolable_value,
const NonInterpolableValue* non_interpolable_value,
InterpolationEnvironment& environment) const {
StyleResolverState& state =
To<CSSInterpolationEnvironment>(environment).GetState();
if (GetProperty().IsCSSCustomProperty()) {
ApplyCustomPropertyValue(interpolable_value, non_interpolable_value, state);
return;
}
ApplyStandardPropertyValue(interpolable_value, non_interpolable_value, state);
}
void CSSInterpolationType::ApplyCustomPropertyValue(
const InterpolableValue& interpolable_value,
const NonInterpolableValue* non_interpolable_value,
StyleResolverState& state) const {
DCHECK(GetProperty().IsCSSCustomProperty());
const CSSValue* css_value =
CreateCSSValue(interpolable_value, non_interpolable_value, state);
DCHECK(!css_value->IsCustomPropertyDeclaration());
// TODO(alancutter): Defer tokenization of the CSSValue until it is needed.
String string_value = css_value->CssText();
CSSTokenizer tokenizer(string_value);
const auto tokens = tokenizer.TokenizeToEOF();
bool is_animation_tainted = true;
bool needs_variable_resolution = false;
scoped_refptr<CSSVariableData> variable_data = CSSVariableData::Create(
{CSSParserTokenRange(tokens), StringView(string_value)},
is_animation_tainted, needs_variable_resolution, KURL(),
WTF::TextEncoding());
const PropertyHandle property = GetProperty();
// TODO(andruud): Avoid making the CSSCustomPropertyDeclaration by allowing
// any CSSValue in CustomProperty::ApplyValue.
const CSSValue* value = MakeGarbageCollected<CSSCustomPropertyDeclaration>(
property.CustomPropertyName(), std::move(variable_data));
StyleBuilder::ApplyProperty(GetProperty().GetCSSPropertyName(), state,
ScopedCSSValue(*value, nullptr));
}
} // namespace blink