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