blob: ff078f38b22f82736960149a56fb66931d765986 [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 "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 cssInterpolationTypes,
const PropertyRegistration& registration) {
InterpolationTypes result;
for (auto& cssInterpolationType : cssInterpolationTypes) {
cssInterpolationType->setCustomPropertyRegistration(registration);
result.push_back(std::move(cssInterpolationType));
}
return result;
}
PropertyRegistration::PropertyRegistration(
const CSSSyntaxDescriptor& syntax,
bool inherits,
const CSSValue* initial,
PassRefPtr<CSSVariableData> initialVariableData,
CSSInterpolationTypes cssInterpolationTypes)
: m_syntax(syntax),
m_inherits(inherits),
m_initial(initial),
m_initialVariableData(initialVariableData),
m_interpolationTypes(setRegistrationOnCSSInterpolationTypes(
std::move(cssInterpolationTypes),
*this)) {}
static bool computationallyIndependent(const CSSValue& value) {
DCHECK(!value.isCSSWideKeyword());
if (value.isVariableReferenceValue())
return !toCSSVariableReferenceValue(value)
.variableDataValue()
->needsVariableResolution();
if (value.isValueList()) {
for (const CSSValue* innerValue : toCSSValueList(value)) {
if (!computationallyIndependent(*innerValue))
return false;
}
return true;
}
if (value.isPrimitiveValue()) {
const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value);
if (!primitiveValue.isLength() &&
!primitiveValue.isCalculatedPercentageWithLength())
return true;
CSSPrimitiveValue::CSSLengthArray lengthArray;
primitiveValue.accumulateLengthArray(lengthArray);
for (size_t i = 0; i < lengthArray.values.size(); i++) {
if (lengthArray.typeFlags.get(i) &&
i != CSSPrimitiveValue::UnitTypePixels &&
i != CSSPrimitiveValue::UnitTypePercentage)
return false;
}
return true;
}
// TODO(timloh): Images and transform-function values can also contain
// lengths.
return true;
}
void PropertyRegistration::registerProperty(
ScriptState* scriptState,
const PropertyDescriptor& descriptor,
ExceptionState& exceptionState) {
// Bindings code ensures these are set.
DCHECK(descriptor.hasName());
DCHECK(descriptor.hasInherits());
DCHECK(descriptor.hasSyntax());
String name = descriptor.name();
if (!CSSVariableParser::isValidVariableName(name)) {
exceptionState.throwDOMException(
SyntaxError, "Custom property names must start with '--'.");
return;
}
AtomicString atomicName(name);
Document* document = toDocument(scriptState->getExecutionContext());
PropertyRegistry& registry = *document->propertyRegistry();
if (registry.registration(atomicName)) {
exceptionState.throwDOMException(
InvalidModificationError,
"The name provided has already been registered.");
return;
}
CSSSyntaxDescriptor syntaxDescriptor(descriptor.syntax());
if (!syntaxDescriptor.isValid()) {
exceptionState.throwDOMException(
SyntaxError,
"The syntax provided is not a valid custom property syntax.");
return;
}
CSSInterpolationTypes cssInterpolationTypes =
CSSInterpolationTypesMap::createCSSInterpolationTypesForSyntax(
atomicName, syntaxDescriptor);
if (descriptor.hasInitialValue()) {
CSSTokenizer tokenizer(descriptor.initialValue());
bool isAnimationTainted = false;
const CSSValue* initial = syntaxDescriptor.parse(
tokenizer.tokenRange(),
document->elementSheet().contents()->parserContext(),
isAnimationTainted);
if (!initial) {
exceptionState.throwDOMException(
SyntaxError,
"The initial value provided does not parse for the given syntax.");
return;
}
if (!computationallyIndependent(*initial)) {
exceptionState.throwDOMException(
SyntaxError,
"The initial value provided is not computationally independent.");
return;
}
initial =
&StyleBuilderConverter::convertRegisteredPropertyInitialValue(*initial);
RefPtr<CSSVariableData> initialVariableData = CSSVariableData::create(
tokenizer.tokenRange(), isAnimationTainted, false);
registry.registerProperty(
atomicName, syntaxDescriptor, descriptor.inherits(), initial,
std::move(initialVariableData), std::move(cssInterpolationTypes));
} else {
if (!syntaxDescriptor.isTokenStream()) {
exceptionState.throwDOMException(
SyntaxError,
"An initial value must be provided if the syntax is not '*'");
return;
}
registry.registerProperty(atomicName, syntaxDescriptor,
descriptor.inherits(), nullptr, nullptr,
std::move(cssInterpolationTypes));
}
// TODO(timloh): Invalidate only elements with this custom property set
document->setNeedsStyleRecalc(SubtreeStyleChange,
StyleChangeReasonForTracing::create(
StyleChangeReason::PropertyRegistration));
}
} // namespace blink