blob: cc37c3bf827399dedef7ab168b840b528d63217e [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 "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
#include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
#include "third_party/blink/renderer/core/css/css_variable_reference_value.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
#include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
namespace blink {
namespace {
bool IsValidVariableReference(CSSParserTokenRange);
bool IsValidEnvVariableReference(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) {
const CSSParserToken& token = range.Peek();
// A block may have both var and env references. They can also be nested
// and used as fallbacks.
switch (token.FunctionId()) {
case CSSValueID::kVar:
if (!IsValidVariableReference(range.ConsumeBlock()))
return false; // Invalid reference.
has_references = true;
continue;
case CSSValueID::kEnv:
if (!IsValidEnvVariableReference(range.ConsumeBlock()))
return false; // Invalid reference.
has_references = true;
continue;
default:
break;
}
}
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);
}
bool IsValidEnvVariableReference(CSSParserTokenRange range) {
range.ConsumeWhitespace();
if (range.ConsumeIncludingWhitespace().GetType() !=
CSSParserTokenType::kIdentToken)
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() && css_parsing_utils::IsCSSWideKeyword(id))
return id;
}
if (ClassifyBlock(range, has_references))
return CSSValueID::kInternalVariableValue;
return CSSValueID::kInvalid;
}
} // 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 == CSSValueID::kInternalVariableValue && has_references;
}
CSSCustomPropertyDeclaration* CSSVariableParser::ParseDeclarationValue(
const AtomicString& variable_name,
const CSSTokenizedValue& tokenized_value,
bool is_animation_tainted,
const CSSParserContext& context) {
if (tokenized_value.range.AtEnd())
return nullptr;
bool has_references;
CSSValueID type =
ClassifyVariableRange(tokenized_value.range, has_references);
if (!IsValidCSSValueID(type))
return nullptr;
if (type == CSSValueID::kInternalVariableValue) {
return MakeGarbageCollected<CSSCustomPropertyDeclaration>(
variable_name,
CSSVariableData::Create(tokenized_value, is_animation_tainted,
has_references, context.BaseURL(),
context.Charset()));
}
return MakeGarbageCollected<CSSCustomPropertyDeclaration>(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 != CSSValueID::kInternalVariableValue)
return nullptr; // Invalid or a css-wide keyword
if (require_var_reference && !has_references)
return nullptr;
return MakeGarbageCollected<CSSVariableReferenceValue>(
CSSVariableData::Create({range, StringView()}, is_animation_tainted,
has_references, context.BaseURL(),
context.Charset()),
context);
}
} // namespace blink