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