| Index: Source/core/css/resolver/CSSVariableResolver.cpp
|
| diff --git a/Source/core/css/resolver/CSSVariableResolver.cpp b/Source/core/css/resolver/CSSVariableResolver.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8a873cd6acf956b6a474a300b64d9f71dc595af7
|
| --- /dev/null
|
| +++ b/Source/core/css/resolver/CSSVariableResolver.cpp
|
| @@ -0,0 +1,221 @@
|
| +// Copyright 2015 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 "config.h"
|
| +#include "core/css/resolver/CSSVariableResolver.h"
|
| +
|
| +#include "core/CSSPropertyNames.h"
|
| +#include "core/CSSValueKeywords.h"
|
| +#include "core/StyleBuilderFunctions.h"
|
| +#include "core/css/CSSVariableData.h"
|
| +#include "core/css/parser/CSSParserToken.h"
|
| +#include "core/css/parser/CSSParserTokenRange.h"
|
| +#include "core/css/parser/CSSParserValues.h"
|
| +#include "core/css/parser/CSSPropertyParser.h"
|
| +#include "core/css/resolver/StyleResolverState.h"
|
| +#include "core/style/StyleVariableData.h"
|
| +#include "wtf/Vector.h"
|
| +
|
| +namespace blink {
|
| +
|
| +enum ResolutionBehavior {
|
| + ResolvingVariableDefinitions,
|
| + DoNotResolveVariableDefinitions
|
| +};
|
| +
|
| +static bool isVariableReferenceToken(CSSParserToken& token)
|
| +{
|
| + return token.type() == FunctionToken && token.valueEqualsIgnoringCase("var");
|
| +}
|
| +
|
| +// TODO(leviw): These will get moved out of StyleBuilder
|
| +// TODO(leviw): Validation should no longer be required here
|
| +static bool findEndOfVariableReference(Vector<CSSParserToken>& resolvedTokens, unsigned startOffset, unsigned& end, unsigned& commaLocation)
|
| +{
|
| + end = 0;
|
| + commaLocation = 0;
|
| + unsigned bracketCount = 0;
|
| + bool validSyntax = true;
|
| + for (unsigned i = startOffset; i < resolvedTokens.size(); ++i) {
|
| + CSSParserTokenType type = resolvedTokens[i].type();
|
| +
|
| + if (type == CommaToken && !commaLocation) {
|
| + commaLocation = i;
|
| + } else if (type == LeftParenthesisToken || type == FunctionToken) {
|
| + bracketCount++;
|
| + } else if (type == RightParenthesisToken) {
|
| + if (bracketCount) {
|
| + bracketCount--;
|
| + } else {
|
| + end = i;
|
| + break;
|
| + }
|
| + } else if (type != WhitespaceToken && !commaLocation) {
|
| + // We need content after a comma for valid syntax, but if it occurs
|
| + // before a comma its invalid.
|
| + validSyntax = commaLocation;
|
| + }
|
| + }
|
| + if (!end || !validSyntax) {
|
| + end = resolvedTokens.size() - 1;
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +unsigned CSSVariableResolver::resolveVariableTokensRecursive(Vector<CSSParserToken>& resolvedTokens, unsigned startOffset)
|
| +{
|
| + ASSERT(startOffset);
|
| + // There should be at least 2 tokens after the start position: a the variable name and the close bracket
|
| + if (resolvedTokens.size() < startOffset + 1)
|
| + return -1;
|
| +
|
| + unsigned commaLocation, end;
|
| +
|
| + // Find the variable location
|
| + unsigned variableLocation = 0;
|
| + for (unsigned i = startOffset; i < resolvedTokens.size(); ++i) {
|
| + if (resolvedTokens[i].type() == IdentToken) {
|
| + variableLocation = i;
|
| + break;
|
| + }
|
| + if (resolvedTokens[i].type() != WhitespaceToken) {
|
| + findEndOfVariableReference(resolvedTokens, i, end, commaLocation);
|
| + unsigned length = end - startOffset + 2;
|
| + resolvedTokens.remove(startOffset - 1, length);
|
| + return -1;
|
| + }
|
| + }
|
| +
|
| + // Find default value and match braces
|
| + if (!findEndOfVariableReference(resolvedTokens, variableLocation + 1, end, commaLocation)) {
|
| + unsigned length = end - startOffset + 2;
|
| + resolvedTokens.remove(startOffset - 1, length);
|
| + return -1;
|
| + }
|
| +
|
| + unsigned length = end - startOffset + 2;
|
| + unsigned varFunctionPosition = startOffset - 1;
|
| +
|
| + AtomicString variableName = resolvedTokens[variableLocation].value();
|
| +
|
| + if (m_variablesSeen.contains(variableName)) {
|
| + m_cycleDetected = true;
|
| + resolvedTokens.clear();
|
| + return 0;
|
| + }
|
| +
|
| + CSSVariableData* variableData = m_styleVariableData ? m_styleVariableData->getVariable(variableName) : nullptr;
|
| + if (variableData) {
|
| + Vector<CSSParserToken>& tokens = variableData->tokens();
|
| + if (variableData->needsVariableResolution()) {
|
| + ASSERT(m_resolutionBehavior == ResolvingVariableDefinitions);
|
| + m_variablesSeen.add(variableName);
|
| + resolveVariableReferencesFromTokens(tokens);
|
| + m_variablesSeen.remove(variableName);
|
| + }
|
| + if (tokens.size()) {
|
| + resolvedTokens.remove(startOffset - 1, length);
|
| + resolvedTokens.insert(startOffset - 1, tokens);
|
| + return tokens.size();
|
| + }
|
| + }
|
| +
|
| + // Fallback on default value if present
|
| + if (!commaLocation || m_cycleDetected) {
|
| + resolvedTokens.clear();
|
| + return 0;
|
| + }
|
| +
|
| + // Move the tokens to the beginning of the variable reference
|
| + unsigned defaultValueOffset = commaLocation + 1;
|
| + unsigned defaultValueLength = end - commaLocation - 1;
|
| + for (unsigned i = 0; i < defaultValueLength; ++i)
|
| + resolvedTokens[varFunctionPosition + i] = resolvedTokens[defaultValueOffset + i];
|
| + resolvedTokens.remove(varFunctionPosition + defaultValueLength, length - defaultValueLength);
|
| +
|
| + resolveVariableReferencesFromTokens(resolvedTokens);
|
| +
|
| + return resolvedTokens.size();
|
| +}
|
| +
|
| +void CSSVariableResolver::resolveVariableReferencesFromTokens(Vector<CSSParserToken>& tokens)
|
| +{
|
| + for (unsigned i = 0; i < tokens.size(); ++i) {
|
| + if (isVariableReferenceToken(tokens[i])) {
|
| + unsigned validTokens = resolveVariableTokensRecursive(tokens, i + 1);
|
| + if (validTokens < 1 || m_cycleDetected) {
|
| + tokens.clear();
|
| + break;
|
| + }
|
| + i += validTokens - 1;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void CSSVariableResolver::resolveAndApplyVariableReferences(StyleResolverState& state, CSSPropertyID id, CSSPrimitiveValue* value)
|
| +{
|
| + // TODO(leviw): This should be a stack
|
| + Vector<CSSParserToken> tokens = value->getVariableDataValue()->tokens();
|
| +
|
| + CSSVariableResolver resolver(state.style()->variables());
|
| +
|
| + resolver.resolveVariableReferencesFromTokens(tokens);
|
| +
|
| + if (!tokens.size())
|
| + return;
|
| +
|
| + CSSParserContext context(HTMLStandardMode, 0);
|
| +
|
| + WillBeHeapVector<CSSProperty, 256> parsedProperties;
|
| +
|
| + bool usesRemUnits, usesVariables;
|
| +
|
| + CSSParserValueList valueList(CSSParserTokenRange(tokens, 0), usesRemUnits, usesVariables);
|
| +
|
| + if (!valueList.size())
|
| + return;
|
| +
|
| + CSSPropertyParser::parseValue(id, false, &valueList, context, parsedProperties, StyleRule::Type::Style);
|
| +
|
| + unsigned parsedPropertiesCount = parsedProperties.size();
|
| + for (unsigned i = 0; i < parsedPropertiesCount; ++i)
|
| + StyleBuilder::applyProperty(parsedProperties[i].id(), state, parsedProperties[i].value());
|
| +}
|
| +
|
| +void CSSVariableResolver::resolveVariableDefinitions(StyleResolverState& state)
|
| +{
|
| + StyleVariableData* variables = state.style()->variables();
|
| + if (!variables)
|
| + return;
|
| +
|
| + // TODO(leviw): Skip resolving definitions without references.
|
| + for (auto& variable : variables->m_data) {
|
| + if (!variable.value->needsVariableResolution())
|
| + continue;
|
| + Vector<CSSParserToken>& tokens = variable.value->tokens();
|
| +
|
| + CSSVariableResolver resolver(variables, variable.key);
|
| + resolver.resolveVariableReferencesFromTokens(tokens);
|
| +
|
| + variable.value->setNeedsVariableResolution(false);
|
| + }
|
| +}
|
| +
|
| +CSSVariableResolver::CSSVariableResolver(const StyleVariableData* styleVariableData)
|
| + : m_styleVariableData(styleVariableData)
|
| + , m_cycleDetected(false)
|
| + , m_resolutionBehavior(DoNotResolveVariableDefinitions)
|
| +{
|
| +}
|
| +
|
| +CSSVariableResolver::CSSVariableResolver(const StyleVariableData* styleVariableData, AtomicString& variable)
|
| + : m_styleVariableData(styleVariableData)
|
| + , m_cycleDetected(false)
|
| + , m_resolutionBehavior(ResolvingVariableDefinitions)
|
| +{
|
| + m_variablesSeen.add(variable);
|
| +}
|
| +
|
| +} // namespace blink
|
|
|