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

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: ToT-ed again... Created 5 years, 5 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 static bool isVariableReferenceToken(CSSParserToken& token)
23 {
24 return token.type() == FunctionToken && token.valueEqualsIgnoringCase("var") ;
25 }
26
27 // TODO(leviw): These will get moved out of StyleBuilder
28 // TODO(leviw): Validation should no longer be required here
29 static bool findEndOfVariableReference(Vector<CSSParserToken>& resolvedTokens, u nsigned startOffset, unsigned& end, unsigned& commaLocation)
30 {
31 end = 0;
32 commaLocation = 0;
33 unsigned bracketCount = 0;
34 bool validSyntax = true;
35 for (unsigned i = startOffset; i < resolvedTokens.size(); ++i) {
36 CSSParserTokenType type = resolvedTokens[i].type();
37
38 if (type == CommaToken && !commaLocation) {
39 commaLocation = i;
40 } else if (type == LeftParenthesisToken || type == FunctionToken) {
41 bracketCount++;
42 } else if (type == RightParenthesisToken) {
43 if (bracketCount) {
44 bracketCount--;
45 } else {
46 end = i;
47 break;
48 }
49 } else if (type != WhitespaceToken && !commaLocation) {
50 // We need content after a comma for valid syntax, but if it occurs
51 // before a comma its invalid.
52 validSyntax = commaLocation;
53 }
54 }
55 if (!end || !validSyntax) {
56 end = resolvedTokens.size() - 1;
57 return false;
58 }
59 return true;
60 }
61
62 unsigned CSSVariableResolver::resolveVariableTokensRecursive(Vector<CSSParserTok en>& resolvedTokens, unsigned startOffset)
63 {
64 ASSERT(startOffset);
65 // There should be at least 2 tokens after the start position: a the variabl e name and the close bracket
66 if (resolvedTokens.size() < startOffset + 1)
67 return -1;
68
69 unsigned commaLocation, end;
70
71 // Find the variable location
72 unsigned variableLocation = 0;
73 for (unsigned i = startOffset; i < resolvedTokens.size(); ++i) {
74 if (resolvedTokens[i].type() == IdentToken) {
75 variableLocation = i;
76 break;
77 }
78 if (resolvedTokens[i].type() != WhitespaceToken) {
79 findEndOfVariableReference(resolvedTokens, i, end, commaLocation);
80 unsigned length = end - startOffset + 2;
81 resolvedTokens.remove(startOffset - 1, length);
82 return -1;
83 }
84 }
85
86 // Find default value and match braces
87 if (!findEndOfVariableReference(resolvedTokens, variableLocation + 1, end, c ommaLocation)) {
88 unsigned length = end - startOffset + 2;
89 resolvedTokens.remove(startOffset - 1, length);
90 return -1;
91 }
92
93 unsigned length = end - startOffset + 2;
94 unsigned varFunctionPosition = startOffset - 1;
95
96 AtomicString variableName = resolvedTokens[variableLocation].value();
97
98 if (m_variablesSeen.contains(variableName)) {
99 m_cycleDetected = true;
100 resolvedTokens.clear();
101 return 0;
102 }
103
104 CSSVariableData* variableData = m_styleVariableData ? m_styleVariableData->g etVariable(variableName) : nullptr;
105 if (variableData) {
106 Vector<CSSParserToken>& tokens = variableData->tokens();
107 if (variableData->needsVariableResolution()) {
108 m_variablesSeen.add(variableName);
109 resolveVariableReferencesFromTokens(tokens);
110 m_variablesSeen.remove(variableName);
111 }
112 if (tokens.size()) {
113 resolvedTokens.remove(startOffset - 1, length);
114 resolvedTokens.insert(startOffset - 1, tokens);
115 return tokens.size();
116 }
117 }
118
119 // Fallback on default value if present
120 if (!commaLocation || m_cycleDetected) {
121 resolvedTokens.clear();
122 return 0;
123 }
124
125 // Move the tokens to the beginning of the variable reference
126 unsigned defaultValueOffset = commaLocation + 1;
127 unsigned defaultValueLength = end - commaLocation - 1;
128 for (unsigned i = 0; i < defaultValueLength; ++i)
129 resolvedTokens[varFunctionPosition + i] = resolvedTokens[defaultValueOff set + i];
130 resolvedTokens.remove(varFunctionPosition + defaultValueLength, length - def aultValueLength);
131
132 resolveVariableReferencesFromTokens(resolvedTokens);
133
134 return resolvedTokens.size();
135 }
136
137 void CSSVariableResolver::resolveVariableReferencesFromTokens(Vector<CSSParserTo ken>& tokens)
Timothy Loh 2015/07/23 08:11:47 Probably nicer to take a CSSParserTokenRange and V
138 {
139 for (unsigned i = 0; i < tokens.size(); ++i) {
140 if (isVariableReferenceToken(tokens[i])) {
141 unsigned validTokens = resolveVariableTokensRecursive(tokens, i + 1) ;
142 if (validTokens < 1 || m_cycleDetected) {
143 tokens.clear();
144 break;
145 }
146 i += validTokens - 1;
147 }
148 }
149 }
150
151 void CSSVariableResolver::resolveAndApplyVariableReferences(StyleResolverState& state, CSSPropertyID id, CSSPrimitiveValue* value)
152 {
153 // TODO(leviw): This should be a stack
154 Vector<CSSParserToken> tokens = value->getVariableDataValue()->tokens();
155
156 CSSVariableResolver resolver(state.style()->variables());
157
158 resolver.resolveVariableReferencesFromTokens(tokens);
159
160 if (!tokens.size())
161 return;
162
163 CSSParserContext context(HTMLStandardMode, 0);
164
165 WillBeHeapVector<CSSProperty, 256> parsedProperties;
166
167 bool usesRemUnits;
168
169 // TODO(leviw): respect the usesRemUnits flag.
170 CSSParserValueList valueList(CSSParserTokenRange(tokens), usesRemUnits);
171
172 if (!valueList.size())
173 return;
174
175 CSSPropertyParser::parseValue(id, false, &valueList, context, parsedProperti es, StyleRule::Type::Style);
176
177 unsigned parsedPropertiesCount = parsedProperties.size();
178 for (unsigned i = 0; i < parsedPropertiesCount; ++i)
179 StyleBuilder::applyProperty(parsedProperties[i].id(), state, parsedPrope rties[i].value());
180 }
181
182 void CSSVariableResolver::resolveVariableDefinitions(StyleResolverState& state)
183 {
184 StyleVariableData* variables = state.style()->variables();
185 if (!variables)
186 return;
187
188 // TODO(leviw): Skip resolving definitions without references.
189 for (auto& variable : variables->m_data) {
190 if (!variable.value->needsVariableResolution())
191 continue;
192 Vector<CSSParserToken>& tokens = variable.value->tokens();
193
194 CSSVariableResolver resolver(variables, variable.key);
195 resolver.resolveVariableReferencesFromTokens(tokens);
196
197 variable.value->setNeedsVariableResolution(false);
Timothy Loh 2015/07/23 08:11:47 I think this will fail cases like: #parent { --fo
198 }
199 }
200
201 CSSVariableResolver::CSSVariableResolver(const StyleVariableData* styleVariableD ata)
202 : m_styleVariableData(styleVariableData)
203 , m_cycleDetected(false)
204 {
205 }
206
207 CSSVariableResolver::CSSVariableResolver(const StyleVariableData* styleVariableD ata, AtomicString& variable)
208 : m_styleVariableData(styleVariableData)
209 , m_cycleDetected(false)
210 {
211 m_variablesSeen.add(variable);
212 }
213
214 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698