| // 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 |