blob: 47f2b90159e6298c014c8e3051c20310649b6126 [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/css/property_registration.h"
#include "third_party/blink/renderer/core/animation/css_interpolation_types_map.h"
#include "third_party/blink/renderer/core/css/css_style_sheet.h"
#include "third_party/blink/renderer/core/css/css_syntax_descriptor.h"
#include "third_party/blink/renderer/core/css/css_value_list.h"
#include "third_party/blink/renderer/core/css/css_variable_reference_value.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
#include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
#include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
#include "third_party/blink/renderer/core/css/property_descriptor.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/css/style_change_reason.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/css/style_sheet_contents.h"
#include "third_party/blink/renderer/core/dom/document.h"
namespace blink {
const PropertyRegistration* PropertyRegistration::From(
const ExecutionContext* execution_context,
const AtomicString& property_name) {
if (!execution_context || !execution_context->IsDocument())
return nullptr;
const PropertyRegistry* registry =
ToDocument(*execution_context).GetPropertyRegistry();
return registry ? registry->Registration(property_name) : nullptr;
}
PropertyRegistration::PropertyRegistration(
const AtomicString& name,
const CSSSyntaxDescriptor& syntax,
bool inherits,
const CSSValue* initial,
scoped_refptr<CSSVariableData> initial_variable_data)
: syntax_(syntax),
inherits_(inherits),
initial_(initial),
initial_variable_data_(std::move(initial_variable_data)),
interpolation_types_(
CSSInterpolationTypesMap::CreateInterpolationTypesForCSSSyntax(
name,
syntax,
*this)) {
DCHECK(RuntimeEnabledFeatures::CSSVariables2Enabled());
}
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 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(
DOMExceptionCode::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(
DOMExceptionCode::kInvalidModificationError,
"The name provided has already been registered.");
return;
}
CSSSyntaxDescriptor syntax_descriptor(descriptor.syntax());
if (!syntax_descriptor.IsValid()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"The syntax provided is not a valid custom property syntax.");
return;
}
const CSSParserContext* parser_context =
document->ElementSheet().Contents()->ParserContext();
const CSSValue* initial = nullptr;
scoped_refptr<CSSVariableData> initial_variable_data;
if (descriptor.hasInitialValue()) {
CSSTokenizer tokenizer(descriptor.initialValue());
const auto tokens = tokenizer.TokenizeToEOF();
bool is_animation_tainted = false;
initial = syntax_descriptor.Parse(CSSParserTokenRange(tokens),
parser_context, is_animation_tainted);
if (!initial) {
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"The initial value provided does not parse for the given syntax.");
return;
}
if (!ComputationallyIndependent(*initial)) {
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"The initial value provided is not computationally independent.");
return;
}
initial =
&StyleBuilderConverter::ConvertRegisteredPropertyInitialValue(*initial);
initial_variable_data = CSSVariableData::Create(
CSSParserTokenRange(tokens), is_animation_tainted, false,
parser_context->BaseURL(), parser_context->Charset());
} else {
if (!syntax_descriptor.IsTokenStream()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"An initial value must be provided if the syntax is not '*'");
return;
}
}
registry.RegisterProperty(
atomic_name, *new PropertyRegistration(atomic_name, syntax_descriptor,
descriptor.inherits(), initial,
std::move(initial_variable_data)));
document->GetStyleEngine().CustomPropertyRegistered();
}
} // namespace blink