| /* |
| * (C) 1999-2003 Lars Knoll (knoll@kde.org) |
| * Copyright (C) 2004, 2005, 2006, 2007, 2010, 2013 Apple Inc. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| #include "CSSValueList.h" |
| |
| #include "CSSFunctionValue.h" |
| #include "CSSParserValues.h" |
| #include "CSSPrimitiveValue.h" |
| #include "CSSVariableDependentValue.h" |
| #include "CSSVariableValue.h" |
| #include <wtf/text/StringBuilder.h> |
| |
| namespace WebCore { |
| |
| CSSValueList::CSSValueList(ClassType classType, ValueListSeparator listSeparator) |
| : CSSValue(classType) |
| { |
| m_valueListSeparator = listSeparator; |
| } |
| |
| CSSValueList::CSSValueList(ValueListSeparator listSeparator) |
| : CSSValue(ValueListClass) |
| { |
| m_valueListSeparator = listSeparator; |
| } |
| |
| CSSValueList::CSSValueList(CSSParserValueList& parserValues) |
| : CSSValue(ValueListClass) |
| { |
| m_valueListSeparator = SpaceSeparator; |
| m_values.reserveInitialCapacity(parserValues.size()); |
| for (unsigned i = 0, size = parserValues.size(); i < size; ++i) { |
| RefPtr<CSSValue> value = parserValues.valueAt(i)->createCSSValue(); |
| ASSERT(value); |
| m_values.uncheckedAppend(value.releaseNonNull()); |
| } |
| } |
| |
| bool CSSValueList::removeAll(CSSValue* value) |
| { |
| // FIXME: Why even take a pointer? |
| if (!value) |
| return false; |
| |
| return m_values.removeAllMatching([value] (const Ref<CSSValue>& current) { |
| return current->equals(*value); |
| }) > 0; |
| } |
| |
| bool CSSValueList::hasValue(CSSValue* val) const |
| { |
| // FIXME: Why even take a pointer? |
| if (!val) |
| return false; |
| |
| for (unsigned i = 0, size = m_values.size(); i < size; ++i) { |
| if (m_values[i].get().equals(*val)) |
| return true; |
| } |
| return false; |
| } |
| |
| PassRefPtr<CSSValueList> CSSValueList::copy() |
| { |
| RefPtr<CSSValueList> newList; |
| switch (m_valueListSeparator) { |
| case SpaceSeparator: |
| newList = createSpaceSeparated(); |
| break; |
| case CommaSeparator: |
| newList = createCommaSeparated(); |
| break; |
| case SlashSeparator: |
| newList = createSlashSeparated(); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| for (auto& value : m_values) |
| newList->append(value.get()); |
| return newList.release(); |
| } |
| |
| String CSSValueList::customCSSText() const |
| { |
| StringBuilder result; |
| String separator; |
| switch (m_valueListSeparator) { |
| case SpaceSeparator: |
| separator = ASCIILiteral(" "); |
| break; |
| case CommaSeparator: |
| separator = ASCIILiteral(", "); |
| break; |
| case SlashSeparator: |
| separator = ASCIILiteral(" / "); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| |
| for (auto& value : m_values) { |
| bool suppressSeparator = false; |
| if (m_valueListSeparator == SpaceSeparator && value->isPrimitiveValue()) { |
| auto* primitiveValue = &downcast<CSSPrimitiveValue>(*value.ptr()); |
| if (primitiveValue->parserOperator() == ',') |
| suppressSeparator = true; |
| } |
| if (!suppressSeparator && !result.isEmpty()) |
| result.append(separator); |
| result.append(value.get().cssText()); |
| } |
| |
| return result.toString(); |
| } |
| |
| bool CSSValueList::equals(const CSSValueList& other) const |
| { |
| if (m_valueListSeparator != other.m_valueListSeparator) |
| return false; |
| |
| if (m_values.size() != other.m_values.size()) |
| return false; |
| |
| for (unsigned i = 0, size = m_values.size(); i < size; ++i) { |
| if (!m_values[i].get().equals(other.m_values[i])) |
| return false; |
| } |
| return true; |
| } |
| |
| bool CSSValueList::equals(const CSSValue& other) const |
| { |
| if (m_values.size() != 1) |
| return false; |
| |
| return m_values[0].get().equals(other); |
| } |
| |
| void CSSValueList::addSubresourceStyleURLs(ListHashSet<URL>& urls, const StyleSheetContents* styleSheet) const |
| { |
| for (unsigned i = 0, size = m_values.size(); i < size; ++i) |
| m_values[i].get().addSubresourceStyleURLs(urls, styleSheet); |
| } |
| |
| bool CSSValueList::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const |
| { |
| for (unsigned i = 0; i < m_values.size(); ++i) { |
| if (m_values[i].get().traverseSubresources(handler)) |
| return true; |
| } |
| return false; |
| } |
| |
| CSSValueList::CSSValueList(const CSSValueList& cloneFrom) |
| : CSSValue(cloneFrom.classType(), /* isCSSOMSafe */ true) |
| { |
| m_valueListSeparator = cloneFrom.m_valueListSeparator; |
| m_values.reserveInitialCapacity(cloneFrom.m_values.size()); |
| for (unsigned i = 0, size = cloneFrom.m_values.size(); i < size; ++i) |
| m_values.uncheckedAppend(*cloneFrom.m_values[i]->cloneForCSSOM()); |
| } |
| |
| Ref<CSSValueList> CSSValueList::cloneForCSSOM() const |
| { |
| return adoptRef(*new CSSValueList(*this)); |
| } |
| |
| |
| bool CSSValueList::containsVariables() const |
| { |
| for (unsigned i = 0; i < m_values.size(); i++) { |
| if (m_values[i]->isVariableValue()) |
| return true; |
| if (m_values[i]->isFunctionValue()) { |
| auto& functionValue = downcast<CSSFunctionValue>(*item(i)); |
| CSSValueList* args = functionValue.arguments(); |
| if (args && args->containsVariables()) |
| return true; |
| } else if (m_values[i]->isValueList()) { |
| auto& listValue = downcast<CSSValueList>(*item(i)); |
| if (listValue.containsVariables()) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool CSSValueList::checkVariablesForCycles(CustomPropertyValueMap& customProperties, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const |
| { |
| for (unsigned i = 0; i < m_values.size(); i++) { |
| auto* value = item(i); |
| if (value->isVariableValue()) { |
| auto& variableValue = downcast<CSSVariableValue>(*value); |
| if (seenProperties.contains(variableValue.name())) |
| return false; |
| RefPtr<CSSValue> value = customProperties.get(variableValue.name()); |
| if (value && value->isVariableDependentValue() && !downcast<CSSVariableDependentValue>(*value).checkVariablesForCycles(variableValue.name(), customProperties, seenProperties, invalidProperties)) |
| return false; |
| |
| // Have to check the fallback values. |
| auto* fallbackArgs = variableValue.fallbackArguments(); |
| if (!fallbackArgs || !fallbackArgs->length()) |
| continue; |
| |
| if (!fallbackArgs->checkVariablesForCycles(customProperties, seenProperties, invalidProperties)) |
| return false; |
| } else if (value->isFunctionValue()) { |
| auto& functionValue = downcast<CSSFunctionValue>(*value); |
| auto* args = functionValue.arguments(); |
| if (args && !args->checkVariablesForCycles(customProperties, seenProperties, invalidProperties)) |
| return false; |
| } else if (value->isValueList()) { |
| auto& listValue = downcast<CSSValueList>(*value); |
| if (!listValue.checkVariablesForCycles(customProperties, seenProperties, invalidProperties)) |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool CSSValueList::buildParserValueSubstitutingVariables(CSSParserValue* result, const CustomPropertyValueMap& customProperties) const |
| { |
| result->id = CSSValueInvalid; |
| result->unit = CSSParserValue::ValueList; |
| result->valueList = new CSSParserValueList(); |
| return buildParserValueListSubstitutingVariables(result->valueList, customProperties); |
| } |
| |
| bool CSSValueList::buildParserValueListSubstitutingVariables(CSSParserValueList* parserList, const CustomPropertyValueMap& customProperties) const |
| { |
| for (unsigned i = 0; i < m_values.size(); ++i) { |
| CSSParserValue result; |
| result.id = CSSValueInvalid; |
| switch (m_values[i]->classType()) { |
| case FunctionClass: |
| if (!downcast<CSSFunctionValue>(*m_values[i].ptr()).buildParserValueSubstitutingVariables(&result, customProperties)) |
| return false; |
| parserList->addValue(result); |
| break; |
| case ValueListClass: |
| if (!downcast<CSSValueList>(*m_values[i].ptr()).buildParserValueSubstitutingVariables(&result, customProperties)) |
| return false; |
| parserList->addValue(result); |
| break; |
| case VariableClass: { |
| if (!downcast<CSSVariableValue>(*m_values[i].ptr()).buildParserValueListSubstitutingVariables(parserList, customProperties)) |
| return false; |
| break; |
| } |
| case PrimitiveClass: |
| // FIXME: Will have to change this if we start preserving invalid tokens. |
| if (downcast<CSSPrimitiveValue>(*m_values[i].ptr()).buildParserValue(&result)) |
| parserList->addValue(result); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| break; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| } // namespace WebCore |