blob: 0eb3cd61e1f2ab0679d22bc4b359e76b9bd02275 [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 "core/css/parser/CSSVariableParser.h"
#include "core/css/CSSCustomPropertyDeclaration.h"
#include "core/css/CSSVariableReferenceValue.h"
#include "core/css/parser/CSSParserTokenRange.h"
namespace blink {
namespace {
bool IsValidVariableReference(CSSParserTokenRange);
bool ClassifyBlock(CSSParserTokenRange range, bool& has_references) {
size_t block_stack_size = 0;
while (!range.AtEnd()) {
// First check if this is a valid variable reference, then handle the next
// token accordingly.
if (range.Peek().GetBlockType() == CSSParserToken::kBlockStart &&
range.Peek().FunctionId() == CSSValueVar) {
CSSParserTokenRange block = range.ConsumeBlock();
if (!IsValidVariableReference(block))
return false; // Bail if any references are invalid
has_references = true;
continue;
}
const CSSParserToken& token = range.Consume();
if (token.GetBlockType() == CSSParserToken::kBlockStart) {
++block_stack_size;
} else if (token.GetBlockType() == CSSParserToken::kBlockEnd) {
--block_stack_size;
} else {
switch (token.GetType()) {
case kDelimiterToken: {
if (token.Delimiter() == '!' && block_stack_size == 0)
return false;
break;
}
case kRightParenthesisToken:
case kRightBraceToken:
case kRightBracketToken:
case kBadStringToken:
case kBadUrlToken:
return false;
case kSemicolonToken:
if (block_stack_size == 0)
return false;
break;
default:
break;
}
}
}
return true;
}
bool IsValidVariableReference(CSSParserTokenRange range) {
range.ConsumeWhitespace();
if (!CSSVariableParser::IsValidVariableName(
range.ConsumeIncludingWhitespace()))
return false;
if (range.AtEnd())
return true;
if (range.Consume().GetType() != kCommaToken)
return false;
if (range.AtEnd())
return false;
bool has_references = false;
return ClassifyBlock(range, has_references);
}
CSSValueID ClassifyVariableRange(CSSParserTokenRange range,
bool& has_references) {
has_references = false;
range.ConsumeWhitespace();
if (range.Peek().GetType() == kIdentToken) {
CSSValueID id = range.ConsumeIncludingWhitespace().Id();
if (range.AtEnd() &&
(id == CSSValueInherit || id == CSSValueInitial || id == CSSValueUnset))
return id;
}
if (ClassifyBlock(range, has_references))
return CSSValueInternalVariableValue;
return CSSValueInvalid;
}
} // namespace
bool CSSVariableParser::IsValidVariableName(const CSSParserToken& token) {
if (token.GetType() != kIdentToken)
return false;
StringView value = token.Value();
return value.length() >= 2 && value[0] == '-' && value[1] == '-';
}
bool CSSVariableParser::IsValidVariableName(const String& string) {
return string.length() >= 2 && string[0] == '-' && string[1] == '-';
}
bool CSSVariableParser::ContainsValidVariableReferences(
CSSParserTokenRange range) {
bool has_references;
CSSValueID type = ClassifyVariableRange(range, has_references);
return type == CSSValueInternalVariableValue && has_references;
}
CSSCustomPropertyDeclaration* CSSVariableParser::ParseDeclarationValue(
const AtomicString& variable_name,
CSSParserTokenRange range,
bool is_animation_tainted) {
if (range.AtEnd())
return nullptr;
bool has_references;
CSSValueID type = ClassifyVariableRange(range, has_references);
if (type == CSSValueInvalid)
return nullptr;
if (type == CSSValueInternalVariableValue) {
return CSSCustomPropertyDeclaration::Create(
variable_name,
CSSVariableData::Create(range, is_animation_tainted, has_references));
}
return CSSCustomPropertyDeclaration::Create(variable_name, type);
}
CSSVariableReferenceValue* CSSVariableParser::ParseRegisteredPropertyValue(
CSSParserTokenRange range,
const CSSParserContext& context,
bool require_var_reference,
bool is_animation_tainted) {
if (range.AtEnd())
return nullptr;
bool has_references;
CSSValueID type = ClassifyVariableRange(range, has_references);
if (type != CSSValueInternalVariableValue)
return nullptr; // Invalid or a css-wide keyword
if (require_var_reference && !has_references)
return nullptr;
return CSSVariableReferenceValue::Create(
CSSVariableData::Create(range, is_animation_tainted, has_references),
context);
}
} // namespace blink