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

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: 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 "wtf/Vector.h"
17
18 namespace blink {
19
20 enum ResolutionBehavior {
21 ResolvingVariableDefinitions,
22 DoNotResolveVariableDefinitions
23 };
24
25 static bool isVariableReferenceToken(CSSParserToken& token)
26 {
27 return token.type() == FunctionToken && token.valueEqualsIgnoringCase("var") ;
28 }
29
30 // TODO(leviw): These will get moved out of StyleBuilder
31 // TODO(leviw): Validation should no longer be required here
32 static bool findEndOfVariableReference(Vector<CSSParserToken>& resolvedTokens, u nsigned startOffset, unsigned& end, unsigned& commaLocation)
33 {
34 end = 0;
35 commaLocation = 0;
36 unsigned bracketCount = 0;
37 bool validSyntax = true;
38 for (unsigned i = startOffset; i < resolvedTokens.size(); ++i) {
39 CSSParserTokenType type = resolvedTokens[i].type();
40
41 if (type == CommaToken && !commaLocation) {
42 commaLocation = i;
43 } else if (type == LeftParenthesisToken) {
44 bracketCount++;
45 } else if (type == RightParenthesisToken) {
46 if (bracketCount) {
47 bracketCount--;
48 } else {
49 end = i;
50 break;
51 }
52 } else if (type != WhitespaceToken && !commaLocation) {
53 // We need content after a comma for valid syntax, but if it occurs
54 // before a comma its invalid.
55 validSyntax = commaLocation;
56 }
57 }
58 if (!end || !validSyntax) {
59 end = resolvedTokens.size() - 1;
60 return false;
61 }
62 return true;
63 }
64
65 // TODO(leviw): Factor this into another file. Make it actually recursive.
66 static unsigned resolveVariableTokensRecursive(Vector<CSSParserToken>& resolvedT okens, const StyleVariableData* styleVariableData, HashSet<AtomicString>& variab lesSeen, unsigned startOffset, ResolutionBehavior resolutionBehavior)
67 {
68 ASSERT(startOffset);
69 // There should be at least 2 tokens after the start position: a the variabl e name and the close bracket
70 if (resolvedTokens.size() < startOffset + 1)
71 return -1;
72
73 unsigned commaLocation, end;
74
75 // Find the variable location
76 unsigned variableLocation = 0;
77 for (unsigned i = startOffset; i < resolvedTokens.size(); ++i) {
78 if (resolvedTokens[i].type() == IdentToken) {
79 variableLocation = i;
80 break;
81 }
82 if (resolvedTokens[i].type() != WhitespaceToken) {
83 findEndOfVariableReference(resolvedTokens, i, end, commaLocation);
84 unsigned length = end - startOffset + 2;
leviw_travelin_and_unemployed 2015/06/20 00:58:15 I need to go back and remove all these magic offse
85 resolvedTokens.remove(startOffset - 1, length);
86 return -1;
87 }
88 }
89
90 // Find default value and match braces
91 if (!findEndOfVariableReference(resolvedTokens, variableLocation + 1, end, c ommaLocation)) {
92 unsigned length = end - startOffset + 2;
93 resolvedTokens.remove(startOffset - 1, length);
94 return -1;
95 }
96
97 unsigned length = end - startOffset + 2;
98 unsigned varFunctionPosition = startOffset - 1;
99
100 AtomicString variableName = resolvedTokens[variableLocation].value();
101
102 if (variablesSeen.contains(variableName)) {
103 // Cycle detected.
104 resolvedTokens.clear();
105 return 0;
106 }
107
108 CSSVariableData* variableData = styleVariableData ? styleVariableData->getVa riable(variableName) : nullptr;
109 if (variableData) {
110 Vector<CSSParserToken>& tokens = variableData->tokens();
111 if (variableData->needsVariableResolution()) {
112 ASSERT(resolutionBehavior == ResolvingVariableDefinitions);
113 variablesSeen.add(variableName);
114 for (unsigned i = 0; i < tokens.size(); ++i) {
115 if (isVariableReferenceToken(tokens[i])) {
116 i += resolveVariableTokensRecursive(tokens, styleVariableDat a, variablesSeen, i + 1, resolutionBehavior);
117 }
118 }
119 variablesSeen.remove(variableName);
120 }
121 if (tokens.size()) {
122 resolvedTokens.remove(startOffset - 1, length);
123 resolvedTokens.insert(startOffset - 1, tokens);
124 return tokens.size();
125 }
126 }
127
128 // Fallback on default value if present
129 if (!commaLocation) {
130 resolvedTokens.remove(varFunctionPosition, length);
131 return 0;
132 }
133
134 // Move the tokens to the beginning of the variable reference
135 unsigned defaultValueOffset = commaLocation + 1;
136 unsigned defaultValueLength = end - commaLocation - 1;
137 for (unsigned i = 0; i < defaultValueLength; ++i)
138 resolvedTokens[varFunctionPosition + i] = resolvedTokens[defaultValueOff set + i];
139 resolvedTokens.remove(varFunctionPosition + defaultValueLength, length - def aultValueLength);
140
141 return commaLocation;
142 }
143
144 static void resolveVariableReferencesFromTokens(Vector<CSSParserToken>& tokens, const StyleVariableData* variables, ResolutionBehavior resolutionBehavior)
145 {
146 HashSet<AtomicString> variablesSeen;
147
148 for (unsigned i = 0; i < tokens.size(); ++i) {
149 if (isVariableReferenceToken(tokens[i])) {
150 unsigned validTokens = resolveVariableTokensRecursive(tokens, variab les, variablesSeen, i + 1, resolutionBehavior);
151 if (validTokens < 1) {
152 tokens.clear();
153 break;
154 }
155 i += validTokens - 1;
156 }
157 }
158 }
159
160 void CSSVariableResolver::resolveAndApplyVariableReferences(StyleResolverState& state, CSSPropertyID id, CSSPrimitiveValue* value)
161 {
162 // TODO(leviw): This should be a stack
163 Vector<CSSParserToken> tokens = value->getVariableDataValue()->tokens();
164
165 resolveVariableReferencesFromTokens(tokens, state.style()->variables(), DoNo tResolveVariableDefinitions);
166
167 if (!tokens.size())
168 return;
169
170 CSSParserContext context(HTMLStandardMode, 0);
171
172 WillBeHeapVector<CSSProperty, 256> parsedProperties;
173
174 bool usesRemUnits, usesVariables;
175
176 CSSParserValueList valueList(CSSParserTokenRange(tokens, 0), usesRemUnits, u sesVariables);
177
178 if (!valueList.size())
179 return;
180
181 CSSPropertyParser::parseValue(id, false, &valueList, context, parsedProperti es, StyleRule::Type::Style);
182
183 unsigned parsedPropertiesCount = parsedProperties.size();
184 for (unsigned i = 0; i < parsedPropertiesCount; ++i)
185 StyleBuilder::applyProperty(parsedProperties[i].id(), state, parsedPrope rties[i].value());
186 }
187
188 void CSSVariableResolver::resolveVariableDefinitions(StyleResolverState& state)
189 {
190 StyleVariableData* variables = state.style()->variables();
191 if (!variables)
192 return;
193
194 // TODO(leviw): Skip resolving definitions without references.
195 for (auto& variable : variables->m_data) {
196 if (!variable.value->needsVariableResolution())
197 continue;
198 Vector<CSSParserToken>& tokens = variable.value->tokens();
199
200 resolveVariableReferencesFromTokens(tokens, variables, ResolvingVariable Definitions);
201
202 // TODO(leviw): This should have been done at parse-time and used the no rmal machinery.
203 // for (CSSParserToken& token : tokens) {
204 // if (token.type() == IdentToken) {
205 // if (token.valueEqualsIgnoringCase("initial")) {
206 // tokens.clear();
207 // break;
208 // } else if (token.valueEqualsIgnoringCase("inherit")) {
209 // if (state.parentStyle()->variables()) {
210 // if
211 // }
212 // tokens =
213 // break;
214 // }
215 // }
216 // }
217
218 // TODO(leviw): this is a hack!
219 variable.value->setNeedsVariableResolution(false);
220
221 // CSSParserTokenRange range(resolvedTokens, &variable.value->scope());
222 // CSSVariableData* resolvedData = CSSVariableData::create(range).leakRe f();
223 // resolvedData->setResolved();
224 // variable.value = resolvedData;
225
226 // // TODO(leviw): Can I do this!?
227 // variable.value->scope().m_tokens = resolvedTokens;
228 // variable.value->setResolved();
229
230 }
231 }
232
233 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698