blob: 8baa9775229ad7dae847cd0d28d0a15922d60335 [file] [log] [blame]
// Copyright 2018 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/css/properties/longhands/custom_property.h"
#include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_local_context.h"
#include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
#include "third_party/blink/renderer/core/css/property_registration.h"
#include "third_party/blink/renderer/core/css/property_registry.h"
#include "third_party/blink/renderer/core/css/resolver/style_builder_converter.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
namespace blink {
namespace {
CSSProperty::Flags InheritedFlag(const PropertyRegistration* registration) {
if (!registration || registration->Inherits())
return CSSProperty::kInherited;
return 0;
}
} // namespace
CustomProperty::CustomProperty(const AtomicString& name,
const Document& document)
: CustomProperty(
name,
PropertyRegistration::From(document.GetExecutionContext(), name)) {}
CustomProperty::CustomProperty(const AtomicString& name,
const PropertyRegistry* registry)
: CustomProperty(name, registry ? registry->Registration(name) : nullptr) {}
CustomProperty::CustomProperty(const AtomicString& name,
const PropertyRegistration* registration)
: Variable(InheritedFlag(registration)),
name_(name),
registration_(registration) {}
const AtomicString& CustomProperty::GetPropertyNameAtomicString() const {
return name_;
}
CSSPropertyName CustomProperty::GetCSSPropertyName() const {
return CSSPropertyName(name_);
}
void CustomProperty::ApplyInitial(StyleResolverState& state) const {
bool is_inherited_property = IsInherited();
if (!registration_) {
state.Style()->SetVariableData(name_, nullptr, is_inherited_property);
return;
}
state.Style()->SetVariableData(name_, registration_->InitialVariableData(),
is_inherited_property);
state.Style()->SetVariableValue(name_, registration_->Initial(),
is_inherited_property);
}
void CustomProperty::ApplyInherit(StyleResolverState& state) const {
bool is_inherited_property = IsInherited();
CSSVariableData* parent_data =
state.ParentStyle()->GetVariableData(name_, is_inherited_property);
state.Style()->SetVariableData(name_, parent_data, is_inherited_property);
if (registration_) {
const CSSValue* parent_value = state.ParentStyle()->GetVariableValue(name_);
state.Style()->SetVariableValue(name_, parent_value, is_inherited_property);
}
}
void CustomProperty::ApplyValue(StyleResolverState& state,
const CSSValue& value) const {
if (value.IsInvalidVariableValue()) {
state.Style()->SetVariableData(name_, nullptr, IsInherited());
if (registration_)
state.Style()->SetVariableValue(name_, nullptr, IsInherited());
return;
}
const auto& declaration = To<CSSCustomPropertyDeclaration>(value);
DCHECK(!value.IsRevertValue());
bool is_inherited_property = IsInherited();
bool initial = declaration.IsInitial(is_inherited_property);
bool inherit = declaration.IsInherit(is_inherited_property);
DCHECK(!(initial && inherit));
// TODO(andruud): Use regular initial/inherit dispatch in StyleBuilder
// once custom properties are Ribbonized.
if (initial) {
ApplyInitial(state);
} else if (inherit) {
ApplyInherit(state);
} else {
scoped_refptr<CSSVariableData> data = declaration.Value();
DCHECK(!data->NeedsVariableResolution());
state.Style()->SetVariableData(name_, data, is_inherited_property);
if (registration_) {
// TODO(andruud): Store CSSParserContext on CSSCustomPropertyDeclaration
// and use that.
const CSSParserContext* context = StrictCSSParserContext(
state.GetDocument().GetExecutionContext()->GetSecureContextMode());
auto mode = CSSParserLocalContext::VariableMode::kTyped;
auto local_context = CSSParserLocalContext().WithVariableMode(mode);
CSSParserTokenRange range = data->TokenRange();
const CSSValue* registered_value =
ParseSingleValue(range, *context, local_context);
if (!registered_value) {
if (is_inherited_property)
ApplyInherit(state);
else
ApplyInitial(state);
return;
}
registered_value = &StyleBuilderConverter::ConvertRegisteredPropertyValue(
state, *registered_value, data->BaseURL(), data->Charset());
data = StyleBuilderConverter::ConvertRegisteredPropertyVariableData(
*registered_value, data->IsAnimationTainted());
state.Style()->SetVariableData(name_, data, is_inherited_property);
state.Style()->SetVariableValue(name_, registered_value,
is_inherited_property);
}
}
}
const CSSValue* CustomProperty::ParseSingleValue(
CSSParserTokenRange& range,
const CSSParserContext& context,
const CSSParserLocalContext& local_context) const {
using VariableMode = CSSParserLocalContext::VariableMode;
switch (local_context.GetVariableMode()) {
case VariableMode::kTyped:
return ParseTyped(range, context, local_context);
case VariableMode::kUntyped:
return ParseUntyped(range, context, local_context);
case VariableMode::kValidatedUntyped:
if (registration_ && !ParseTyped(range, context, local_context))
return nullptr;
return ParseUntyped(range, context, local_context);
}
}
const CSSValue* CustomProperty::CSSValueFromComputedStyleInternal(
const ComputedStyle& style,
const LayoutObject*,
bool allow_visited_style) const {
if (registration_) {
const CSSValue* value = style.GetVariableValue(name_, IsInherited());
if (value)
return value;
// If we don't have CSSValue for this registered property, it means that
// that the property was not registered at the time |style| was calculated,
// hence we proceed with unregistered behavior.
}
CSSVariableData* data = style.GetVariableData(name_, IsInherited());
if (!data)
return nullptr;
return MakeGarbageCollected<CSSCustomPropertyDeclaration>(name_, data);
}
const CSSValue* CustomProperty::ParseUntyped(
CSSParserTokenRange range,
const CSSParserContext& context,
const CSSParserLocalContext& local_context) const {
// TODO(crbug.com/661854): Pass through the original string when we have it.
return CSSVariableParser::ParseDeclarationValue(
name_, {range, StringView()}, local_context.IsAnimationTainted(),
context);
}
const CSSValue* CustomProperty::ParseTyped(
CSSParserTokenRange range,
const CSSParserContext& context,
const CSSParserLocalContext& local_context) const {
if (!registration_)
return ParseUntyped(range, context, local_context);
return registration_->Syntax().Parse(range, context,
local_context.IsAnimationTainted());
}
bool CustomProperty::HasInitialValue() const {
if (!registration_)
return false;
return registration_->InitialVariableData();
}
bool CustomProperty::SupportsGuaranteedInvalid() const {
return !registration_ || registration_->Syntax().IsUniversal();
}
} // namespace blink