blob: f74d0d30f6d243e342a05a00e9ef1bfe08184aa2 [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/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/parser/CSSTokenizer.h"
#include "core/css/parser/CSSVariableParser.h"
#include "core/dom/Document.h"
#include "core/dom/ExceptionCode.h"
#include "core/dom/StyleChangeReason.h"
namespace blink {
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(ExecutionContext* executionContext, 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, "The name provided is not a valid custom property name.");
return;
}
AtomicString atomicName(name);
Document* document = toDocument(executionContext);
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;
}
if (descriptor.hasInitialValue()) {
CSSTokenizer::Scope scope(descriptor.initialValue());
const CSSValue* initial = syntaxDescriptor.parse(scope.tokenRange());
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;
}
RefPtr<CSSVariableData> initialVariableData = CSSVariableData::create(scope.tokenRange(), false);
registry.registerProperty(atomicName, syntaxDescriptor, descriptor.inherits(), initial, initialVariableData.release());
} 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);
}
// TODO(timloh): Invalidate only elements with this custom property set
document->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::PropertyRegistration));
}
void PropertyRegistration::unregisterProperty(ExecutionContext* executionContext, const String& property, ExceptionState& exceptionState)
{
Document* document = toDocument(executionContext);
PropertyRegistry& registry = *document->propertyRegistry();
AtomicString atomicProperty(property);
if (!registry.registration(atomicProperty)) {
exceptionState.throwDOMException(NotFoundError, "CSS.unregisterProperty() called with non-registered property " + property);
return;
}
registry.unregisterProperty(atomicProperty);
document->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::PropertyUnregistration));
}
} // namespace blink