blob: a522dcc5fb07d0d812f1cade7946bf343588228f [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "config.h"
#include "core/css/resolver/CSSVariableResolver.h"
#include "core/CSSPropertyNames.h"
#include "core/CSSValueKeywords.h"
#include "core/StyleBuilderFunctions.h"
#include "core/css/CSSVariableData.h"
#include "core/css/CSSVariableReferenceValue.h"
#include "core/css/parser/CSSParserToken.h"
#include "core/css/parser/CSSParserTokenRange.h"
#include "core/css/parser/CSSParserValues.h"
#include "core/css/parser/CSSPropertyParser.h"
#include "core/css/resolver/StyleBuilder.h"
#include "core/css/resolver/StyleResolverState.h"
#include "core/style/StyleVariableData.h"
#include "wtf/Vector.h"
namespace blink {
void CSSVariableResolver::resolveFallback(CSSParserTokenRange range,
Vector<CSSParserToken>& result, CSSVariableResolver::ResolutionState& context)
{
if (!range.atEnd()) {
range.consume();
resolveVariableReferencesFromTokens(range, result, context);
} else {
context.success = false;
}
}
void CSSVariableResolver::resolveVariableTokensRecursive(CSSParserTokenRange range,
Vector<CSSParserToken>& result, CSSVariableResolver::ResolutionState& context)
{
Vector<CSSParserToken> trash;
range.consumeWhitespace();
ASSERT(range.peek().type() == IdentToken);
AtomicString variableName = range.consumeIncludingWhitespace().value();
ASSERT(range.atEnd() || (range.peek().type() == CommaToken));
// Cycle detection.
if (m_variablesSeen.contains(variableName)) {
context.success = false;
context.cycleStartPoints.add(variableName);
resolveFallback(range, trash, context);
return;
}
CSSVariableData* variableData = m_styleVariableData ? m_styleVariableData->getVariable(variableName) : nullptr;
if (variableData) {
Vector<CSSParserToken> tokens;
if (variableData->needsVariableResolution()) {
m_variablesSeen.add(variableName);
resolveVariableReferencesFromTokens(variableData->tokens(), result, context);
m_variablesSeen.remove(variableName);
// The old variable data holds onto the backing string the new resolved CSSVariableData
// relies on. Ensure it will live beyond us overwriting the RefPtr in StyleVariableData.
ASSERT(variableData->refCount() > 1);
m_styleVariableData->setVariable(variableName, CSSVariableData::createResolved(tokens, variableData));
if (!context.cycleStartPoints.isEmpty()) {
if (context.cycleStartPoints.contains(variableName))
context.cycleStartPoints.remove(variableName);
if (!context.cycleStartPoints.isEmpty()) {
resolveFallback(range, trash, context);
return;
}
}
} else {
tokens = variableData->tokens();
}
if (!tokens.isEmpty()) {
// Check that loops are not induced by the fallback.
resolveFallback(range, trash, context);
if (context.cycleStartPoints.isEmpty()) {
// It's OK if the fallback fails to resolve - we're not actually taking it.
context.success = true;
result.appendVector(tokens);
}
return;
}
}
// We're legitimately falling back, so reset success flag.
context.success = true;
resolveFallback(range, result, context);
}
void CSSVariableResolver::resolveVariableReferencesFromTokens(CSSParserTokenRange range,
Vector<CSSParserToken>& result, CSSVariableResolver::ResolutionState& context)
{
while (!range.atEnd()) {
if (range.peek().functionId() != CSSValueVar) {
result.append(range.consume());
} else {
resolveVariableTokensRecursive(range.consumeBlock(), result, context);
}
}
if (!context.success)
result.clear();
return;
}
void CSSVariableResolver::resolveAndApplyVariableReferences(StyleResolverState& state, CSSPropertyID id, const CSSVariableReferenceValue& value)
{
// TODO(leviw): This should be a stack
CSSVariableResolver resolver(state.style()->variables());
Vector<CSSParserToken> tokens;
ResolutionState resolutionContext;
resolver.resolveVariableReferencesFromTokens(value.variableDataValue()->tokens(), tokens, resolutionContext);
if (!resolutionContext.success)
return;
CSSParserContext context(HTMLStandardMode, 0);
WillBeHeapVector<CSSProperty, 256> parsedProperties;
CSSPropertyParser::parseValue(id, false, CSSParserTokenRange(tokens), context, parsedProperties, StyleRule::Type::Style);
unsigned parsedPropertiesCount = parsedProperties.size();
for (unsigned i = 0; i < parsedPropertiesCount; ++i)
StyleBuilder::applyProperty(parsedProperties[i].id(), state, parsedProperties[i].value());
}
void CSSVariableResolver::resolveVariableDefinitions(StyleVariableData* variables)
{
if (!variables)
return;
for (auto& variable : variables->m_data) {
if (!variable.value->needsVariableResolution())
continue;
Vector<CSSParserToken> resolvedTokens;
CSSVariableResolver resolver(variables, variable.key);
ResolutionState context;
resolver.resolveVariableReferencesFromTokens(variable.value->tokens(), resolvedTokens, context);
variable.value = CSSVariableData::createResolved(resolvedTokens, variable.value);
}
}
CSSVariableResolver::CSSVariableResolver(StyleVariableData* styleVariableData)
: m_styleVariableData(styleVariableData)
{
}
CSSVariableResolver::CSSVariableResolver(StyleVariableData* styleVariableData, AtomicString& variable)
: m_styleVariableData(styleVariableData)
{
m_variablesSeen.add(variable);
}
} // namespace blink