| // Copyright 2016 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/css_syntax_definition.h" |
| |
| #include <utility> |
| #include "third_party/blink/renderer/core/css/css_custom_property_declaration.h" |
| #include "third_party/blink/renderer/core/css/css_syntax_component.h" |
| #include "third_party/blink/renderer/core/css/css_uri_value.h" |
| #include "third_party/blink/renderer/core/css/css_value_list.h" |
| #include "third_party/blink/renderer/core/css/css_variable_reference_value.h" |
| #include "third_party/blink/renderer/core/css/parser/css_parser_idioms.h" |
| #include "third_party/blink/renderer/core/css/parser/css_variable_parser.h" |
| #include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h" |
| #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h" |
| #include "third_party/blink/renderer/platform/runtime_enabled_features.h" |
| |
| namespace blink { |
| namespace { |
| |
| // The 'revert' and 'default' keywords are reserved. |
| // |
| // https://drafts.csswg.org/css-cascade/#default |
| // https://drafts.csswg.org/css-values-4/#identifier-value |
| // |
| // TODO(crbug.com/579788): Implement 'revert'. |
| // TODO(crbug.com/882285): Make 'default' invalid as <custom-ident>. |
| bool IsReservedIdentToken(const CSSParserToken& token) { |
| if (token.GetType() != kIdentToken) |
| return false; |
| return css_parsing_utils::IsRevertKeyword(token.Value()) || |
| css_parsing_utils::IsDefaultKeyword(token.Value()); |
| } |
| |
| bool CouldConsumeReservedKeyword(CSSParserTokenRange range) { |
| range.ConsumeWhitespace(); |
| if (IsReservedIdentToken(range.ConsumeIncludingWhitespace())) |
| return range.AtEnd(); |
| return false; |
| } |
| |
| const CSSValue* ConsumeSingleType(const CSSSyntaxComponent& syntax, |
| CSSParserTokenRange& range, |
| const CSSParserContext& context) { |
| switch (syntax.GetType()) { |
| case CSSSyntaxType::kIdent: |
| if (range.Peek().GetType() == kIdentToken && |
| range.Peek().Value() == syntax.GetString()) { |
| range.ConsumeIncludingWhitespace(); |
| return MakeGarbageCollected<CSSCustomIdentValue>( |
| AtomicString(syntax.GetString())); |
| } |
| return nullptr; |
| case CSSSyntaxType::kLength: { |
| CSSParserContext::ParserModeOverridingScope scope(context, |
| kHTMLStandardMode); |
| return css_parsing_utils::ConsumeLength(range, context, |
| ValueRange::kValueRangeAll); |
| } |
| case CSSSyntaxType::kNumber: |
| return css_parsing_utils::ConsumeNumber(range, context, |
| ValueRange::kValueRangeAll); |
| case CSSSyntaxType::kPercentage: |
| return css_parsing_utils::ConsumePercent(range, context, |
| ValueRange::kValueRangeAll); |
| case CSSSyntaxType::kLengthPercentage: { |
| CSSParserContext::ParserModeOverridingScope scope(context, |
| kHTMLStandardMode); |
| return css_parsing_utils::ConsumeLengthOrPercent( |
| range, context, ValueRange::kValueRangeAll); |
| } |
| case CSSSyntaxType::kColor: { |
| CSSParserContext::ParserModeOverridingScope scope(context, |
| kHTMLStandardMode); |
| return css_parsing_utils::ConsumeColor(range, context); |
| } |
| case CSSSyntaxType::kImage: |
| return css_parsing_utils::ConsumeImage(range, context); |
| case CSSSyntaxType::kUrl: |
| return css_parsing_utils::ConsumeUrl(range, context); |
| case CSSSyntaxType::kInteger: |
| return css_parsing_utils::ConsumeIntegerOrNumberCalc(range, context); |
| case CSSSyntaxType::kAngle: |
| return css_parsing_utils::ConsumeAngle(range, context, |
| base::Optional<WebFeature>()); |
| case CSSSyntaxType::kTime: |
| return css_parsing_utils::ConsumeTime(range, context, |
| ValueRange::kValueRangeAll); |
| case CSSSyntaxType::kResolution: |
| return css_parsing_utils::ConsumeResolution(range); |
| case CSSSyntaxType::kTransformFunction: |
| return css_parsing_utils::ConsumeTransformValue(range, context); |
| case CSSSyntaxType::kTransformList: |
| return css_parsing_utils::ConsumeTransformList(range, context); |
| case CSSSyntaxType::kCustomIdent: |
| // TODO(crbug.com/579788): Implement 'revert'. |
| // TODO(crbug.com/882285): Make 'default' invalid as <custom-ident>. |
| if (IsReservedIdentToken(range.Peek())) |
| return nullptr; |
| return css_parsing_utils::ConsumeCustomIdent(range, context); |
| default: |
| NOTREACHED(); |
| return nullptr; |
| } |
| } |
| |
| const CSSValue* ConsumeSyntaxComponent(const CSSSyntaxComponent& syntax, |
| CSSParserTokenRange range, |
| const CSSParserContext& context) { |
| // CSS-wide keywords are already handled by the CSSPropertyParser |
| if (syntax.GetRepeat() == CSSSyntaxRepeat::kSpaceSeparated) { |
| CSSValueList* list = CSSValueList::CreateSpaceSeparated(); |
| while (!range.AtEnd()) { |
| const CSSValue* value = ConsumeSingleType(syntax, range, context); |
| if (!value) |
| return nullptr; |
| list->Append(*value); |
| } |
| return list->length() ? list : nullptr; |
| } |
| if (syntax.GetRepeat() == CSSSyntaxRepeat::kCommaSeparated) { |
| CSSValueList* list = CSSValueList::CreateCommaSeparated(); |
| do { |
| const CSSValue* value = ConsumeSingleType(syntax, range, context); |
| if (!value) |
| return nullptr; |
| list->Append(*value); |
| } while (css_parsing_utils::ConsumeCommaIncludingWhitespace(range)); |
| return list->length() ? list : nullptr; |
| } |
| const CSSValue* result = ConsumeSingleType(syntax, range, context); |
| if (!range.AtEnd()) |
| return nullptr; |
| return result; |
| } |
| |
| } // namespace |
| |
| const CSSValue* CSSSyntaxDefinition::Parse(CSSParserTokenRange range, |
| const CSSParserContext& context, |
| bool is_animation_tainted) const { |
| if (IsUniversal()) { |
| // TODO(crbug.com/579788): Implement 'revert'. |
| // TODO(crbug.com/882285): Make 'default' invalid as <custom-ident>. |
| if (CouldConsumeReservedKeyword(range)) |
| return nullptr; |
| return CSSVariableParser::ParseRegisteredPropertyValue( |
| range, context, false, is_animation_tainted); |
| } |
| range.ConsumeWhitespace(); |
| for (const CSSSyntaxComponent& component : syntax_components_) { |
| if (const CSSValue* result = |
| ConsumeSyntaxComponent(component, range, context)) |
| return result; |
| } |
| return CSSVariableParser::ParseRegisteredPropertyValue(range, context, true, |
| is_animation_tainted); |
| } |
| |
| CSSSyntaxDefinition CSSSyntaxDefinition::IsolatedCopy() const { |
| Vector<CSSSyntaxComponent> syntax_components_copy; |
| syntax_components_copy.ReserveCapacity(syntax_components_.size()); |
| for (const auto& syntax_component : syntax_components_) { |
| syntax_components_copy.push_back(CSSSyntaxComponent( |
| syntax_component.GetType(), syntax_component.GetString().IsolatedCopy(), |
| syntax_component.GetRepeat())); |
| } |
| return CSSSyntaxDefinition(std::move(syntax_components_copy)); |
| } |
| |
| CSSSyntaxDefinition::CSSSyntaxDefinition(Vector<CSSSyntaxComponent> components) |
| : syntax_components_(std::move(components)) { |
| DCHECK(syntax_components_.size()); |
| } |
| |
| CSSSyntaxDefinition CSSSyntaxDefinition::CreateUniversal() { |
| Vector<CSSSyntaxComponent> components; |
| components.push_back(CSSSyntaxComponent( |
| CSSSyntaxType::kTokenStream, g_empty_string, CSSSyntaxRepeat::kNone)); |
| return CSSSyntaxDefinition(std::move(components)); |
| } |
| |
| } // namespace blink |