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 CSSVariableData::checkVariablesForCyclesWithRange(CSSParserTokenRange range, CustomPropertyValueMap& customProperties, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const { while (!range.atEnd()) { if (range.peek().functionId() == CSSValueVar) { CSSParserTokenRange block = range.consumeBlock(); block.consumeWhitespace(); ASSERT(block.peek().type() == IdentToken); AtomicString variableName = block.consumeIncludingWhitespace().value().toAtomicString(); ASSERT(block.atEnd() || block.peek().type() == CommaToken); if (seenProperties.contains(variableName)) return false; RefPtr<CSSCustomPropertyValue> value = customProperties.get(variableName); if (value && value->containsVariables() && !value->checkVariablesForCycles(variableName, customProperties, seenProperties, invalidProperties)) return false; if (range.peek().type() == CommaToken) { // Fallback. range.consume(); return checkVariablesForCyclesWithRange(block, customProperties, seenProperties, invalidProperties); } } else range.consume(); } return true; }
bool CSSVariableData::resolveVariableReference(const CustomPropertyValueMap& customProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result) const { range.consumeWhitespace(); ASSERT(range.peek().type() == IdentToken); AtomicString variableName = range.consumeIncludingWhitespace().value().toAtomicString(); ASSERT(range.atEnd() || (range.peek().type() == CommaToken)); RefPtr<CSSCustomPropertyValue> property = customProperties.get(variableName); if (!property || !property->value()) return resolveVariableFallback(customProperties, range, result); if (property->containsVariables()) { // FIXME: Avoid doing this work more than once. RefPtr<CSSVariableData> resolvedData = property->value()->resolveVariableReferences(customProperties); if (!resolvedData) return false; result.appendVector(resolvedData->tokens()); } else result.appendVector(property->value()->tokens()); return true; }