| // 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 |