3dd0432571f3417ff6fae2a85cb0b2e2af83c48b
[WebKit-https.git] / Source / WebCore / css / CSSValueList.cpp
1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2004, 2005, 2006, 2007, 2010, 2013 Apple Inc. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include "config.h"
22 #include "CSSValueList.h"
23
24 #include "CSSFunctionValue.h"
25 #include "CSSParserValues.h"
26 #include "CSSPrimitiveValue.h"
27 #include "CSSVariableDependentValue.h"
28 #include "CSSVariableValue.h"
29 #include <wtf/text/StringBuilder.h>
30
31 namespace WebCore {
32
33 CSSValueList::CSSValueList(ClassType classType, ValueListSeparator listSeparator)
34     : CSSValue(classType)
35 {
36     m_valueListSeparator = listSeparator;
37 }
38
39 CSSValueList::CSSValueList(ValueListSeparator listSeparator)
40     : CSSValue(ValueListClass)
41 {
42     m_valueListSeparator = listSeparator;
43 }
44
45 CSSValueList::CSSValueList(CSSParserValueList& parserValues)
46     : CSSValue(ValueListClass)
47 {
48     m_valueListSeparator = SpaceSeparator;
49     m_values.reserveInitialCapacity(parserValues.size());
50     for (unsigned i = 0, size = parserValues.size(); i < size; ++i) {
51         RefPtr<CSSValue> value = parserValues.valueAt(i)->createCSSValue();
52         ASSERT(value);
53         m_values.uncheckedAppend(value.releaseNonNull());
54     }
55 }
56
57 bool CSSValueList::removeAll(CSSValue* value)
58 {
59     // FIXME: Why even take a pointer?
60     if (!value)
61         return false;
62
63     return m_values.removeAllMatching([value] (const Ref<CSSValue>& current) {
64         return current->equals(*value);
65     }) > 0;
66 }
67
68 bool CSSValueList::hasValue(CSSValue* val) const
69 {
70     // FIXME: Why even take a pointer?
71     if (!val)
72         return false;
73
74     for (unsigned i = 0, size = m_values.size(); i < size; ++i) {
75         if (m_values[i].get().equals(*val))
76             return true;
77     }
78     return false;
79 }
80
81 Ref<CSSValueList> CSSValueList::copy()
82 {
83     RefPtr<CSSValueList> newList;
84     switch (m_valueListSeparator) {
85     case SpaceSeparator:
86         newList = createSpaceSeparated();
87         break;
88     case CommaSeparator:
89         newList = createCommaSeparated();
90         break;
91     case SlashSeparator:
92         newList = createSlashSeparated();
93         break;
94     default:
95         ASSERT_NOT_REACHED();
96     }
97     for (auto& value : m_values)
98         newList->append(value.get());
99     return newList.releaseNonNull();
100 }
101
102 String CSSValueList::customCSSText() const
103 {
104     StringBuilder result;
105     String separator;
106     switch (m_valueListSeparator) {
107     case SpaceSeparator:
108         separator = ASCIILiteral(" ");
109         break;
110     case CommaSeparator:
111         separator = ASCIILiteral(", ");
112         break;
113     case SlashSeparator:
114         separator = ASCIILiteral(" / ");
115         break;
116     default:
117         ASSERT_NOT_REACHED();
118     }
119
120     for (auto& value : m_values) {
121         bool suppressSeparator = false;
122         if (m_valueListSeparator == SpaceSeparator && value->isPrimitiveValue()) {
123             auto* primitiveValue = &downcast<CSSPrimitiveValue>(*value.ptr());
124             if (primitiveValue->parserOperator() == ',')
125                 suppressSeparator = true;
126         }
127         if (!suppressSeparator && !result.isEmpty())
128             result.append(separator);
129         result.append(value.get().cssText());
130     }
131
132     return result.toString();
133 }
134
135 bool CSSValueList::equals(const CSSValueList& other) const
136 {
137     if (m_valueListSeparator != other.m_valueListSeparator)
138         return false;
139
140     if (m_values.size() != other.m_values.size())
141         return false;
142
143     for (unsigned i = 0, size = m_values.size(); i < size; ++i) {
144         if (!m_values[i].get().equals(other.m_values[i]))
145             return false;
146     }
147     return true;
148 }
149
150 bool CSSValueList::equals(const CSSValue& other) const
151 {
152     if (m_values.size() != 1)
153         return false;
154
155     return m_values[0].get().equals(other);
156 }
157
158 void CSSValueList::addSubresourceStyleURLs(ListHashSet<URL>& urls, const StyleSheetContents* styleSheet) const
159 {
160     for (unsigned i = 0, size = m_values.size(); i < size; ++i)
161         m_values[i].get().addSubresourceStyleURLs(urls, styleSheet);
162 }
163
164 bool CSSValueList::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const
165 {
166     for (unsigned i = 0; i < m_values.size(); ++i) {
167         if (m_values[i].get().traverseSubresources(handler))
168             return true;
169     }
170     return false;
171 }
172
173 CSSValueList::CSSValueList(const CSSValueList& cloneFrom)
174     : CSSValue(cloneFrom.classType(), /* isCSSOMSafe */ true)
175 {
176     m_valueListSeparator = cloneFrom.m_valueListSeparator;
177     m_values.reserveInitialCapacity(cloneFrom.m_values.size());
178     for (unsigned i = 0, size = cloneFrom.m_values.size(); i < size; ++i)
179         m_values.uncheckedAppend(*cloneFrom.m_values[i]->cloneForCSSOM());
180 }
181
182 Ref<CSSValueList> CSSValueList::cloneForCSSOM() const
183 {
184     return adoptRef(*new CSSValueList(*this));
185 }
186
187
188 bool CSSValueList::containsVariables() const
189 {
190     for (unsigned i = 0; i < m_values.size(); i++) {
191         if (m_values[i]->isVariableValue())
192             return true;
193         if (m_values[i]->isFunctionValue()) {
194             auto& functionValue = downcast<CSSFunctionValue>(*item(i));
195             CSSValueList* args = functionValue.arguments();
196             if (args && args->containsVariables())
197                 return true;
198         } else if (m_values[i]->isValueList()) {
199             auto& listValue = downcast<CSSValueList>(*item(i));
200             if (listValue.containsVariables())
201                 return true;
202         }
203     }
204     return false;
205 }
206
207 bool CSSValueList::checkVariablesForCycles(CustomPropertyValueMap& customProperties, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const
208 {
209     for (unsigned i = 0; i < m_values.size(); i++) {
210         auto* value = item(i);
211         if (value->isVariableValue()) {
212             auto& variableValue = downcast<CSSVariableValue>(*value);
213             if (seenProperties.contains(variableValue.name()))
214                 return false;
215             RefPtr<CSSValue> value = customProperties.get(variableValue.name());
216             if (value && value->isVariableDependentValue() && !downcast<CSSVariableDependentValue>(*value).checkVariablesForCycles(variableValue.name(), customProperties, seenProperties, invalidProperties))
217                 return false;
218
219             // Have to check the fallback values.
220             auto* fallbackArgs = variableValue.fallbackArguments();
221             if (!fallbackArgs || !fallbackArgs->length())
222                 continue;
223             
224             if (!fallbackArgs->checkVariablesForCycles(customProperties, seenProperties, invalidProperties))
225                 return false;
226         } else if (value->isFunctionValue()) {
227             auto& functionValue = downcast<CSSFunctionValue>(*value);
228             auto* args = functionValue.arguments();
229             if (args && !args->checkVariablesForCycles(customProperties, seenProperties, invalidProperties))
230                 return false;
231         } else if (value->isValueList()) {
232             auto& listValue = downcast<CSSValueList>(*value);
233             if (!listValue.checkVariablesForCycles(customProperties, seenProperties, invalidProperties))
234                 return false;
235         }
236     }
237     return true;
238 }
239
240 bool CSSValueList::buildParserValueSubstitutingVariables(CSSParserValue* result, const CustomPropertyValueMap& customProperties) const
241 {
242     result->id = CSSValueInvalid;
243     result->unit = CSSParserValue::ValueList;
244     result->valueList = new CSSParserValueList();
245     return buildParserValueListSubstitutingVariables(result->valueList, customProperties);
246 }
247
248 bool CSSValueList::buildParserValueListSubstitutingVariables(CSSParserValueList* parserList, const CustomPropertyValueMap& customProperties) const
249 {
250     for (unsigned i = 0; i < m_values.size(); ++i) {
251         CSSParserValue result;
252         result.id = CSSValueInvalid;
253         switch (m_values[i]->classType()) {
254         case FunctionClass:
255             if (!downcast<CSSFunctionValue>(*m_values[i].ptr()).buildParserValueSubstitutingVariables(&result, customProperties))
256                 return false;
257             parserList->addValue(result);
258             break;
259         case ValueListClass:
260             if (!downcast<CSSValueList>(*m_values[i].ptr()).buildParserValueSubstitutingVariables(&result, customProperties))
261                 return false;
262             parserList->addValue(result);
263             break;
264         case VariableClass: {
265             if (!downcast<CSSVariableValue>(*m_values[i].ptr()).buildParserValueListSubstitutingVariables(parserList, customProperties))
266                 return false;
267             break;
268         }
269         case PrimitiveClass:
270             // FIXME: Will have to change this if we start preserving invalid tokens.
271             if (downcast<CSSPrimitiveValue>(*m_values[i].ptr()).buildParserValue(&result))
272                 parserList->addValue(result);
273             break;
274         default:
275             ASSERT_NOT_REACHED();
276             break;
277             return false;
278         }
279     }
280     return true;
281 }
282     
283 } // namespace WebCore