OLD | NEW |
---|---|
(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 | |
OLD | NEW |