| // 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/css/PropertyRegistration.h" |
| |
| #include "core/animation/CSSInterpolationTypesMap.h" |
| #include "core/css/CSSStyleSheet.h" |
| #include "core/css/CSSSyntaxDescriptor.h" |
| #include "core/css/CSSValueList.h" |
| #include "core/css/CSSVariableReferenceValue.h" |
| #include "core/css/PropertyDescriptor.h" |
| #include "core/css/PropertyRegistry.h" |
| #include "core/css/StyleSheetContents.h" |
| #include "core/css/parser/CSSParserContext.h" |
| #include "core/css/parser/CSSTokenizer.h" |
| #include "core/css/parser/CSSVariableParser.h" |
| #include "core/css/resolver/StyleBuilderConverter.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/ExceptionCode.h" |
| #include "core/dom/StyleChangeReason.h" |
| |
| namespace blink { |
| |
| static InterpolationTypes SetRegistrationOnCSSInterpolationTypes( |
| CSSInterpolationTypes css_interpolation_types, |
| const PropertyRegistration& registration) { |
| InterpolationTypes result; |
| for (auto& css_interpolation_type : css_interpolation_types) { |
| css_interpolation_type->SetCustomPropertyRegistration(registration); |
| result.push_back(std::move(css_interpolation_type)); |
| } |
| return result; |
| } |
| |
| PropertyRegistration::PropertyRegistration( |
| const CSSSyntaxDescriptor& syntax, |
| bool inherits, |
| const CSSValue* initial, |
| PassRefPtr<CSSVariableData> initial_variable_data, |
| CSSInterpolationTypes css_interpolation_types) |
| : syntax_(syntax), |
| inherits_(inherits), |
| initial_(initial), |
| initial_variable_data_(std::move(initial_variable_data)), |
| interpolation_types_(SetRegistrationOnCSSInterpolationTypes( |
| std::move(css_interpolation_types), |
| *this)) {} |
| |
| static bool ComputationallyIndependent(const CSSValue& value) { |
| DCHECK(!value.IsCSSWideKeyword()); |
| |
| if (value.IsVariableReferenceValue()) |
| return !ToCSSVariableReferenceValue(value) |
| .VariableDataValue() |
| ->NeedsVariableResolution(); |
| |
| if (value.IsValueList()) { |
| for (const CSSValue* inner_value : ToCSSValueList(value)) { |
| if (!ComputationallyIndependent(*inner_value)) |
| return false; |
| } |
| return true; |
| } |
| |
| if (value.IsPrimitiveValue()) { |
| const CSSPrimitiveValue& primitive_value = ToCSSPrimitiveValue(value); |
| if (!primitive_value.IsLength() && |
| !primitive_value.IsCalculatedPercentageWithLength()) |
| return true; |
| |
| CSSPrimitiveValue::CSSLengthArray length_array; |
| primitive_value.AccumulateLengthArray(length_array); |
| for (size_t i = 0; i < length_array.values.size(); i++) { |
| if (length_array.type_flags.Get(i) && |
| i != CSSPrimitiveValue::kUnitTypePixels && |
| i != CSSPrimitiveValue::kUnitTypePercentage) |
| return false; |
| } |
| return true; |
| } |
| |
| // TODO(timloh): Images and transform-function values can also contain |
| // lengths. |
| |
| return true; |
| } |
| |
| void PropertyRegistration::registerProperty( |
| ExecutionContext* execution_context, |
| const PropertyDescriptor& descriptor, |
| ExceptionState& exception_state) { |
| // Bindings code ensures these are set. |
| DCHECK(descriptor.hasName()); |
| DCHECK(descriptor.hasInherits()); |
| DCHECK(descriptor.hasSyntax()); |
| |
| String name = descriptor.name(); |
| if (!CSSVariableParser::IsValidVariableName(name)) { |
| exception_state.ThrowDOMException( |
| kSyntaxError, "Custom property names must start with '--'."); |
| return; |
| } |
| AtomicString atomic_name(name); |
| Document* document = ToDocument(execution_context); |
| PropertyRegistry& registry = *document->GetPropertyRegistry(); |
| if (registry.Registration(atomic_name)) { |
| exception_state.ThrowDOMException( |
| kInvalidModificationError, |
| "The name provided has already been registered."); |
| return; |
| } |
| |
| CSSSyntaxDescriptor syntax_descriptor(descriptor.syntax()); |
| if (!syntax_descriptor.IsValid()) { |
| exception_state.ThrowDOMException( |
| kSyntaxError, |
| "The syntax provided is not a valid custom property syntax."); |
| return; |
| } |
| |
| CSSInterpolationTypes css_interpolation_types = |
| CSSInterpolationTypesMap::CreateCSSInterpolationTypesForSyntax( |
| atomic_name, syntax_descriptor); |
| |
| if (descriptor.hasInitialValue()) { |
| CSSTokenizer tokenizer(descriptor.initialValue()); |
| bool is_animation_tainted = false; |
| const CSSValue* initial = syntax_descriptor.Parse( |
| tokenizer.TokenRange(), |
| document->ElementSheet().Contents()->ParserContext(), |
| is_animation_tainted); |
| if (!initial) { |
| exception_state.ThrowDOMException( |
| kSyntaxError, |
| "The initial value provided does not parse for the given syntax."); |
| return; |
| } |
| if (!ComputationallyIndependent(*initial)) { |
| exception_state.ThrowDOMException( |
| kSyntaxError, |
| "The initial value provided is not computationally independent."); |
| return; |
| } |
| initial = |
| &StyleBuilderConverter::ConvertRegisteredPropertyInitialValue(*initial); |
| RefPtr<CSSVariableData> initial_variable_data = CSSVariableData::Create( |
| tokenizer.TokenRange(), is_animation_tainted, false); |
| registry.RegisterProperty( |
| atomic_name, syntax_descriptor, descriptor.inherits(), initial, |
| std::move(initial_variable_data), std::move(css_interpolation_types)); |
| } else { |
| if (!syntax_descriptor.IsTokenStream()) { |
| exception_state.ThrowDOMException( |
| kSyntaxError, |
| "An initial value must be provided if the syntax is not '*'"); |
| return; |
| } |
| registry.RegisterProperty(atomic_name, syntax_descriptor, |
| descriptor.inherits(), nullptr, nullptr, |
| std::move(css_interpolation_types)); |
| } |
| |
| // TODO(timloh): Invalidate only elements with this custom property set |
| document->SetNeedsStyleRecalc(kSubtreeStyleChange, |
| StyleChangeReasonForTracing::Create( |
| StyleChangeReason::kPropertyRegistration)); |
| } |
| |
| } // namespace blink |