Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(48)

Side by Side Diff: Source/core/css/resolver/CSSVariableResolver.cpp

Issue 1192983003: CSS Custom Properties (Variables) (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: with missing files Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "config.h"
6 #include "core/css/resolver/CSSVariableResolver.h"
7
8 #include "core/CSSPropertyNames.h"
9 #include "core/CSSValueKeywords.h"
10 #include "core/StyleBuilderFunctions.h"
11 #include "core/css/CSSVariableData.h"
12 #include "core/css/parser/CSSParserToken.h"
13 #include "core/css/parser/CSSParserTokenRange.h"
14 #include "core/css/parser/CSSParserValues.h"
15 #include "core/css/parser/CSSPropertyParser.h"
16 #include "core/css/resolver/StyleResolverState.h"
17 #include "core/style/StyleVariableData.h"
18 #include "wtf/Vector.h"
19
20 namespace blink {
21
22 enum ResolutionBehavior {
23 ResolvingVariableDefinitions,
24 DoNotResolveVariableDefinitions
25 };
26
27 static bool isVariableReferenceToken(CSSParserToken& token)
28 {
29 return token.type() == FunctionToken && token.valueEqualsIgnoringCase("var") ;
30 }
31
32 // TODO(leviw): These will get moved out of StyleBuilder
33 // TODO(leviw): Validation should no longer be required here
34 static bool findEndOfVariableReference(Vector<CSSParserToken>& resolvedTokens, u nsigned startOffset, unsigned& end, unsigned& commaLocation)
35 {
36 end = 0;
37 commaLocation = 0;
38 unsigned bracketCount = 0;
39 bool validSyntax = true;
40 for (unsigned i = startOffset; i < resolvedTokens.size(); ++i) {
41 CSSParserTokenType type = resolvedTokens[i].type();
42
43 if (type == CommaToken && !commaLocation) {
44 commaLocation = i;
45 } else if (type == LeftParenthesisToken || type == FunctionToken) {
46 bracketCount++;
47 } else if (type == RightParenthesisToken) {
48 if (bracketCount) {
49 bracketCount--;
50 } else {
51 end = i;
52 break;
53 }
54 } else if (type != WhitespaceToken && !commaLocation) {
55 // We need content after a comma for valid syntax, but if it occurs
56 // before a comma its invalid.
57 validSyntax = commaLocation;
58 }
59 }
60 if (!end || !validSyntax) {
61 end = resolvedTokens.size() - 1;
62 return false;
63 }
64 return true;
65 }
66
67 unsigned CSSVariableResolver::resolveVariableTokensRecursive(Vector<CSSParserTok en>& resolvedTokens, unsigned startOffset)
68 {
69 ASSERT(startOffset);
70 // There should be at least 2 tokens after the start position: a the variabl e name and the close bracket
71 if (resolvedTokens.size() < startOffset + 1)
72 return -1;
73
74 unsigned commaLocation, end;
75
76 // Find the variable location
77 unsigned variableLocation = 0;
78 for (unsigned i = startOffset; i < resolvedTokens.size(); ++i) {
79 if (resolvedTokens[i].type() == IdentToken) {
80 variableLocation = i;
81 break;
82 }
83 if (resolvedTokens[i].type() != WhitespaceToken) {
84 findEndOfVariableReference(resolvedTokens, i, end, commaLocation);
85 unsigned length = end - startOffset + 2;
86 resolvedTokens.remove(startOffset - 1, length);
87 return -1;
88 }
89 }
90
91 // Find default value and match braces
92 if (!findEndOfVariableReference(resolvedTokens, variableLocation + 1, end, c ommaLocation)) {
93 unsigned length = end - startOffset + 2;
94 resolvedTokens.remove(startOffset - 1, length);
95 return -1;
96 }
97
98 unsigned length = end - startOffset + 2;
99 unsigned varFunctionPosition = startOffset - 1;
100
101 AtomicString variableName = resolvedTokens[variableLocation].value();
102
103 if (m_variablesSeen.contains(variableName)) {
104 m_cycleDetected = true;
105 resolvedTokens.clear();
106 return 0;
107 }
108
109 CSSVariableData* variableData = m_styleVariableData ? m_styleVariableData->g etVariable(variableName) : nullptr;
110 if (variableData) {
111 Vector<CSSParserToken>& tokens = variableData->tokens();
112 if (variableData->needsVariableResolution()) {
113 ASSERT(m_resolutionBehavior == ResolvingVariableDefinitions);
114 m_variablesSeen.add(variableName);
115 resolveVariableReferencesFromTokens(tokens);
116 m_variablesSeen.remove(variableName);
117 }
118 if (tokens.size()) {
119 resolvedTokens.remove(startOffset - 1, length);
120 resolvedTokens.insert(startOffset - 1, tokens);
121 return tokens.size();
122 }
123 }
124
125 // Fallback on default value if present
126 if (!commaLocation || m_cycleDetected) {
127 resolvedTokens.clear();
128 return 0;
129 }
130
131 // Move the tokens to the beginning of the variable reference
132 unsigned defaultValueOffset = commaLocation + 1;
133 unsigned defaultValueLength = end - commaLocation - 1;
134 for (unsigned i = 0; i < defaultValueLength; ++i)
135 resolvedTokens[varFunctionPosition + i] = resolvedTokens[defaultValueOff set + i];
136 resolvedTokens.remove(varFunctionPosition + defaultValueLength, length - def aultValueLength);
137
138 resolveVariableReferencesFromTokens(resolvedTokens);
139
140 return resolvedTokens.size();
141 }
142
143 void CSSVariableResolver::resolveVariableReferencesFromTokens(Vector<CSSParserTo ken>& tokens)
144 {
145 for (unsigned i = 0; i < tokens.size(); ++i) {
146 if (isVariableReferenceToken(tokens[i])) {
147 unsigned validTokens = resolveVariableTokensRecursive(tokens, i + 1) ;
148 if (validTokens < 1 || m_cycleDetected) {
149 tokens.clear();
150 break;
151 }
152 i += validTokens - 1;
153 }
154 }
155 }
156
157 void CSSVariableResolver::resolveAndApplyVariableReferences(StyleResolverState& state, CSSPropertyID id, CSSPrimitiveValue* value)
158 {
159 // TODO(leviw): This should be a stack
160 Vector<CSSParserToken> tokens = value->getVariableDataValue()->tokens();
161
162 CSSVariableResolver resolver(state.style()->variables());
163
164 resolver.resolveVariableReferencesFromTokens(tokens);
165
166 if (!tokens.size())
167 return;
168
169 CSSParserContext context(HTMLStandardMode, 0);
170
171 WillBeHeapVector<CSSProperty, 256> parsedProperties;
172
173 bool usesRemUnits, usesVariables;
174
175 CSSParserValueList valueList(CSSParserTokenRange(tokens, 0), usesRemUnits, u sesVariables);
176
177 if (!valueList.size())
178 return;
179
180 CSSPropertyParser::parseValue(id, false, &valueList, context, parsedProperti es, StyleRule::Type::Style);
181
182 unsigned parsedPropertiesCount = parsedProperties.size();
183 for (unsigned i = 0; i < parsedPropertiesCount; ++i)
184 StyleBuilder::applyProperty(parsedProperties[i].id(), state, parsedPrope rties[i].value());
185 }
186
187 void CSSVariableResolver::resolveVariableDefinitions(StyleResolverState& state)
188 {
189 StyleVariableData* variables = state.style()->variables();
190 if (!variables)
191 return;
192
193 // TODO(leviw): Skip resolving definitions without references.
194 for (auto& variable : variables->m_data) {
195 if (!variable.value->needsVariableResolution())
196 continue;
197 Vector<CSSParserToken>& tokens = variable.value->tokens();
198
199 CSSVariableResolver resolver(variables, variable.key);
200 resolver.resolveVariableReferencesFromTokens(tokens);
201
202 variable.value->setNeedsVariableResolution(false);
203 }
204 }
205
206 CSSVariableResolver::CSSVariableResolver(const StyleVariableData* styleVariableD ata)
207 : m_styleVariableData(styleVariableData)
208 , m_cycleDetected(false)
209 , m_resolutionBehavior(DoNotResolveVariableDefinitions)
210 {
211 }
212
213 CSSVariableResolver::CSSVariableResolver(const StyleVariableData* styleVariableD ata, AtomicString& variable)
214 : m_styleVariableData(styleVariableData)
215 , m_cycleDetected(false)
216 , m_resolutionBehavior(ResolvingVariableDefinitions)
217 {
218 m_variablesSeen.add(variable);
219 }
220
221 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698