Index: third_party/WebKit/Source/core/css/resolver/CSSVariableResolver.cpp |
diff --git a/third_party/WebKit/Source/core/css/resolver/CSSVariableResolver.cpp b/third_party/WebKit/Source/core/css/resolver/CSSVariableResolver.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..68e6ebe293d8d74e0e3a98ee26e7b209fa885116 |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/css/resolver/CSSVariableResolver.cpp |
@@ -0,0 +1,181 @@ |
+// 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/CSSVariableReferenceValue.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/StyleBuilder.h" |
+#include "core/css/resolver/StyleResolverState.h" |
+#include "core/style/StyleVariableData.h" |
+#include "wtf/Vector.h" |
+ |
+namespace blink { |
+ |
+// TODO(leviw): This should take a CSSParserTokenRange |
+static void findEndOfVariableReference(const Vector<CSSParserToken>& resolvedTokens, unsigned startOffset, unsigned& end, unsigned& commaLocation) |
Timothy Loh
2015/10/28 05:12:41
Maybe startIndex, endIndex, commaIndex (as well as
leviw_travelin_and_unemployed
2015/10/30 21:41:16
Done.
|
+{ |
+ end = 0; |
+ commaLocation = 0; |
+ unsigned bracketCount = 0; |
Timothy Loh
2015/10/28 05:12:41
imo, the logic below would be slightly clearer if
leviw_travelin_and_unemployed
2015/10/30 21:41:16
Done.
|
+ 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; |
+ } |
+ } |
+ } |
+ if (!end) |
+ end = resolvedTokens.size() - 1; |
+} |
+ |
+unsigned CSSVariableResolver::resolveVariableTokensRecursive(Vector<CSSParserToken>& resolvedTokens, unsigned startOffset) |
+{ |
+ ASSERT(startOffset); |
+ |
+ unsigned commaLocation, end; |
Timothy Loh
2015/10/28 05:12:41
I think Blink style is to have multiple variable d
leviw_travelin_and_unemployed
2015/10/30 21:41:16
Done.
|
+ |
+ // Find the variable location |
+ unsigned variableLocation = 0; |
Timothy Loh
2015/10/28 05:12:41
How about (we definitely have an ident, right?):
leviw_travelin_and_unemployed
2015/10/30 21:41:16
Done.
|
+ for (unsigned i = startOffset; i < resolvedTokens.size(); ++i) { |
+ if (resolvedTokens[i].type() == IdentToken) { |
+ variableLocation = i; |
+ break; |
+ } |
+ } |
+ |
+ // Find default value and match braces |
+ findEndOfVariableReference(resolvedTokens, variableLocation + 1, end, commaLocation); |
+ |
+ unsigned length = end - startOffset + 2; |
Timothy Loh
2015/10/28 05:12:42
All the +1s and -1s are a bit hard to follow. This
leviw_travelin_and_unemployed
2015/10/30 21:41:16
Addressed.
|
+ 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()) { |
+ m_variablesSeen.add(variableName); |
+ resolveVariableReferencesFromTokens(tokens); |
+ m_variablesSeen.remove(variableName); |
+ |
+ m_styleVariableData->setVariable(variableName, CSSVariableData::createResolved(tokens)); |
+ } |
+ 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; |
Timothy Loh
2015/10/28 05:12:41
defaultValueStart?
leviw_travelin_and_unemployed
2015/10/30 21:41:16
Sure :)
|
+ unsigned defaultValueLength = end - commaLocation - 1; |
Timothy Loh
2015/10/28 05:12:42
end - defaultValueStart
|
+ 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 (tokens[i].functionId() == CSSValueVar) { |
+ unsigned validTokens = resolveVariableTokensRecursive(tokens, i + 1); |
+ if (validTokens < 1 || m_cycleDetected) { |
+ tokens.clear(); |
+ break; |
+ } |
+ i += validTokens - 1; |
+ } |
+ } |
+} |
+ |
+void CSSVariableResolver::resolveAndApplyVariableReferences(StyleResolverState& state, CSSPropertyID id, const CSSVariableReferenceValue& value) |
+{ |
+ // TODO(leviw): This should be a stack |
+ Vector<CSSParserToken> tokens = value.variableDataValue()->tokens(); |
+ |
+ CSSVariableResolver resolver(state.style()->variables()); |
+ |
+ resolver.resolveVariableReferencesFromTokens(tokens); |
+ |
+ if (!tokens.size()) |
+ return; |
+ |
+ CSSParserContext context(HTMLStandardMode, 0); |
+ |
+ WillBeHeapVector<CSSProperty, 256> parsedProperties; |
+ |
+ CSSPropertyParser::parseValue(id, false, CSSParserTokenRange(tokens), 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(StyleVariableData* variables) |
+{ |
+ if (!variables) |
+ return; |
+ |
+ for (auto& variable : variables->m_data) { |
+ if (!variable.value->needsVariableResolution()) |
+ continue; |
+ Vector<CSSParserToken> resolvedTokens(variable.value->tokens()); |
+ |
+ CSSVariableResolver resolver(variables, variable.key); |
+ resolver.resolveVariableReferencesFromTokens(resolvedTokens); |
+ |
+ variable.value = CSSVariableData::createResolved(resolvedTokens); |
+ } |
+} |
+ |
+CSSVariableResolver::CSSVariableResolver(StyleVariableData* styleVariableData) |
+ : m_styleVariableData(styleVariableData) |
+ , m_cycleDetected(false) |
+{ |
+} |
+ |
+CSSVariableResolver::CSSVariableResolver(StyleVariableData* styleVariableData, AtomicString& variable) |
+ : m_styleVariableData(styleVariableData) |
+ , m_cycleDetected(false) |
+{ |
+ m_variablesSeen.add(variable); |
+} |
+ |
+} // namespace blink |