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 static void findEndOfVariableReference(Vector<CSSParserToken>& resolvedTokens, u nsigned startOffset, unsigned& end, unsigned& commaLocation) | |
28 { | |
29 end = 0; | |
30 commaLocation = 0; | |
31 unsigned bracketCount = 0; | |
alancutter (OOO until 2018)
2015/08/05 08:01:43
If resolvedTokens was a CSSParserTokenRange you co
| |
32 for (unsigned i = startOffset; i < resolvedTokens.size(); ++i) { | |
33 CSSParserTokenType type = resolvedTokens[i].type(); | |
34 | |
35 if (type == CommaToken && !commaLocation) { | |
36 commaLocation = i; | |
37 } else if (type == LeftParenthesisToken || type == FunctionToken) { | |
38 bracketCount++; | |
39 } else if (type == RightParenthesisToken) { | |
40 if (bracketCount) { | |
41 bracketCount--; | |
42 } else { | |
43 end = i; | |
44 break; | |
45 } | |
46 } | |
47 } | |
48 if (!end) | |
49 end = resolvedTokens.size() - 1; | |
50 } | |
51 | |
52 unsigned CSSVariableResolver::resolveVariableTokensRecursive(Vector<CSSParserTok en>& resolvedTokens, unsigned startOffset) | |
53 { | |
54 ASSERT(startOffset); | |
55 // There should be at least 2 tokens after the start position: a the variabl e name and the close bracket | |
56 if (resolvedTokens.size() < startOffset + 1) | |
57 return -1; | |
58 | |
59 unsigned commaLocation, end; | |
60 | |
61 // Find the variable location | |
62 unsigned variableLocation = 0; | |
63 for (unsigned i = startOffset; i < resolvedTokens.size(); ++i) { | |
64 if (resolvedTokens[i].type() == IdentToken) { | |
65 variableLocation = i; | |
66 break; | |
67 } | |
68 if (resolvedTokens[i].type() != WhitespaceToken) { | |
69 findEndOfVariableReference(resolvedTokens, i, end, commaLocation); | |
70 unsigned length = end - startOffset + 2; | |
71 resolvedTokens.remove(startOffset - 1, length); | |
72 return -1; | |
73 } | |
74 } | |
75 | |
76 // Find default value and match braces | |
77 findEndOfVariableReference(resolvedTokens, variableLocation + 1, end, commaL ocation); | |
78 | |
79 unsigned length = end - startOffset + 2; | |
80 unsigned varFunctionPosition = startOffset - 1; | |
81 | |
82 AtomicString variableName = resolvedTokens[variableLocation].value(); | |
83 | |
84 if (m_variablesSeen.contains(variableName)) { | |
85 m_cycleDetected = true; | |
86 resolvedTokens.clear(); | |
87 return 0; | |
88 } | |
89 | |
90 CSSVariableData* variableData = m_styleVariableData ? m_styleVariableData->g etVariable(variableName) : nullptr; | |
91 if (variableData) { | |
92 Vector<CSSParserToken>& tokens = variableData->tokens(); | |
93 if (variableData->needsVariableResolution()) { | |
94 m_variablesSeen.add(variableName); | |
95 resolveVariableReferencesFromTokens(tokens); | |
96 m_variablesSeen.remove(variableName); | |
97 } | |
98 if (tokens.size()) { | |
99 resolvedTokens.remove(startOffset - 1, length); | |
100 resolvedTokens.insert(startOffset - 1, tokens); | |
101 return tokens.size(); | |
102 } | |
103 } | |
104 | |
105 // Fallback on default value if present | |
106 if (!commaLocation || m_cycleDetected) { | |
107 resolvedTokens.clear(); | |
108 return 0; | |
109 } | |
110 | |
111 // Move the tokens to the beginning of the variable reference | |
112 unsigned defaultValueOffset = commaLocation + 1; | |
113 unsigned defaultValueLength = end - commaLocation - 1; | |
114 for (unsigned i = 0; i < defaultValueLength; ++i) | |
115 resolvedTokens[varFunctionPosition + i] = resolvedTokens[defaultValueOff set + i]; | |
116 resolvedTokens.remove(varFunctionPosition + defaultValueLength, length - def aultValueLength); | |
117 | |
118 resolveVariableReferencesFromTokens(resolvedTokens); | |
119 | |
120 return resolvedTokens.size(); | |
121 } | |
122 | |
123 void CSSVariableResolver::resolveVariableReferencesFromTokens(Vector<CSSParserTo ken>& tokens) | |
124 { | |
125 for (unsigned i = 0; i < tokens.size(); ++i) { | |
126 if (isVariableReferenceToken(tokens[i])) { | |
127 unsigned validTokens = resolveVariableTokensRecursive(tokens, i + 1) ; | |
128 if (validTokens < 1 || m_cycleDetected) { | |
129 tokens.clear(); | |
130 break; | |
131 } | |
132 i += validTokens - 1; | |
133 } | |
134 } | |
135 } | |
136 | |
137 void CSSVariableResolver::resolveAndApplyVariableReferences(StyleResolverState& state, CSSPropertyID id, CSSPrimitiveValue* value) | |
138 { | |
139 // TODO(leviw): This should be a stack | |
140 Vector<CSSParserToken> tokens = value->getVariableDataValue()->tokens(); | |
141 | |
142 CSSVariableResolver resolver(state.style()->variables()); | |
143 | |
144 resolver.resolveVariableReferencesFromTokens(tokens); | |
145 | |
146 if (!tokens.size()) | |
147 return; | |
148 | |
149 CSSParserContext context(HTMLStandardMode, 0); | |
150 | |
151 WillBeHeapVector<CSSProperty, 256> parsedProperties; | |
152 | |
153 CSSParserValueList valueList = CSSParserValueList(CSSParserTokenRange(tokens )); | |
154 | |
155 if (!valueList.size()) | |
156 return; | |
157 | |
158 CSSPropertyParser::parseValue(id, false, &valueList, context, parsedProperti es, StyleRule::Type::Style); | |
159 | |
160 unsigned parsedPropertiesCount = parsedProperties.size(); | |
161 for (unsigned i = 0; i < parsedPropertiesCount; ++i) | |
162 StyleBuilder::applyProperty(parsedProperties[i].id(), state, parsedPrope rties[i].value()); | |
163 } | |
164 | |
165 void CSSVariableResolver::resolveVariableDefinitions(StyleResolverState& state) | |
166 { | |
167 StyleVariableData* variables = state.style()->variables(); | |
168 if (!variables) | |
169 return; | |
170 | |
171 for (auto& variable : variables->m_data) { | |
172 if (!variable.value->needsVariableResolution()) | |
173 continue; | |
174 Vector<CSSParserToken>& tokens = variable.value->tokens(); | |
175 | |
176 CSSVariableResolver resolver(variables, variable.key); | |
177 resolver.resolveVariableReferencesFromTokens(tokens); | |
178 | |
179 variable.value->setNeedsVariableResolution(false); | |
alancutter (OOO until 2018)
2015/08/05 08:01:43
Isn't this mutating the original CSSCustomVariable
| |
180 } | |
181 } | |
182 | |
183 CSSVariableResolver::CSSVariableResolver(const StyleVariableData* styleVariableD ata) | |
184 : m_styleVariableData(styleVariableData) | |
185 , m_cycleDetected(false) | |
186 { | |
187 } | |
188 | |
189 CSSVariableResolver::CSSVariableResolver(const StyleVariableData* styleVariableD ata, AtomicString& variable) | |
190 : m_styleVariableData(styleVariableData) | |
191 , m_cycleDetected(false) | |
192 { | |
193 m_variablesSeen.add(variable); | |
194 } | |
195 | |
196 } // namespace blink | |
OLD | NEW |