blob: 00028f6e730fcd6372d9079fe2e79cfa472c08a6 [file] [log] [blame]
// Copyright 2020 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/at_rule_descriptor_parser.h"
#include "third_party/blink/renderer/core/css/css_string_value.h"
#include "third_party/blink/renderer/core/css/css_value.h"
#include "third_party/blink/renderer/core/css/css_value_pair.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_context.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"
namespace blink {
namespace {
CSSValue* ConsumeCounterStyleSymbol(CSSParserTokenRange& range,
const CSSParserContext& context) {
// <symbol> = <string> | <image> | <custom-ident>
if (CSSValue* string = css_parsing_utils::ConsumeString(range))
return string;
if (RuntimeEnabledFeatures::CSSAtRuleCounterStyleImageSymbolsEnabled()) {
if (CSSValue* image = css_parsing_utils::ConsumeImage(range, context))
return image;
}
if (CSSCustomIdentValue* custom_ident =
css_parsing_utils::ConsumeCustomIdent(range, context)) {
return custom_ident;
}
return nullptr;
}
CSSValue* ConsumeCounterStyleSystem(CSSParserTokenRange& range,
const CSSParserContext& context) {
// Syntax: cyclic | numeric | alphabetic | symbolic | additive |
// [ fixed <integer>? ] | [ extends <counter-style-name> ]
if (CSSValue* ident = css_parsing_utils::ConsumeIdent<
CSSValueID::kCyclic, CSSValueID::kSymbolic, CSSValueID::kAlphabetic,
CSSValueID::kNumeric, CSSValueID::kAdditive>(range))
return ident;
if (CSSValue* ident =
css_parsing_utils::ConsumeIdent<CSSValueID::kFixed>(range)) {
CSSValue* first_symbol_value =
css_parsing_utils::ConsumeInteger(range, context);
if (!first_symbol_value) {
first_symbol_value = CSSNumericLiteralValue::Create(
1, CSSPrimitiveValue::UnitType::kInteger);
}
return MakeGarbageCollected<CSSValuePair>(
ident, first_symbol_value, CSSValuePair::kKeepIdenticalValues);
}
if (CSSValue* ident =
css_parsing_utils::ConsumeIdent<CSSValueID::kExtends>(range)) {
CSSValue* extended =
css_parsing_utils::ConsumeCounterStyleName(range, context);
if (!extended)
return nullptr;
return MakeGarbageCollected<CSSValuePair>(
ident, extended, CSSValuePair::kKeepIdenticalValues);
}
// Internal keywords for predefined counter styles that use special
// algorithms. For example, 'simp-chinese-informal'.
if (context.Mode() == kUASheetMode) {
if (CSSValue* ident = css_parsing_utils::ConsumeIdent<
CSSValueID::kInternalHebrew,
CSSValueID::kInternalSimpChineseInformal,
CSSValueID::kInternalSimpChineseFormal,
CSSValueID::kInternalTradChineseInformal,
CSSValueID::kInternalTradChineseFormal,
CSSValueID::kInternalKoreanHangulFormal,
CSSValueID::kInternalKoreanHanjaInformal,
CSSValueID::kInternalKoreanHanjaFormal,
CSSValueID::kInternalLowerArmenian,
CSSValueID::kInternalUpperArmenian,
CSSValueID::kInternalEthiopicNumeric>(range))
return ident;
}
return nullptr;
}
CSSValue* ConsumeCounterStyleNegative(CSSParserTokenRange& range,
const CSSParserContext& context) {
// Syntax: <symbol> <symbol>?
CSSValue* prepend = ConsumeCounterStyleSymbol(range, context);
if (!prepend)
return nullptr;
if (range.AtEnd())
return prepend;
CSSValue* append = ConsumeCounterStyleSymbol(range, context);
if (!append || !range.AtEnd())
return nullptr;
return MakeGarbageCollected<CSSValuePair>(prepend, append,
CSSValuePair::kKeepIdenticalValues);
}
CSSValue* ConsumeCounterStyleRangeBound(CSSParserTokenRange& range,
const CSSParserContext& context) {
if (CSSValue* infinite =
css_parsing_utils::ConsumeIdent<CSSValueID::kInfinite>(range))
return infinite;
if (CSSValue* integer = css_parsing_utils::ConsumeInteger(range, context))
return integer;
return nullptr;
}
CSSValue* ConsumeCounterStyleRange(CSSParserTokenRange& range,
const CSSParserContext& context) {
// Syntax: [ [ <integer> | infinite ]{2} ]# | auto
if (CSSValue* auto_value =
css_parsing_utils::ConsumeIdent<CSSValueID::kAuto>(range))
return auto_value;
CSSValueList* list = CSSValueList::CreateCommaSeparated();
do {
CSSValue* lower_bound = ConsumeCounterStyleRangeBound(range, context);
if (!lower_bound)
return nullptr;
CSSValue* upper_bound = ConsumeCounterStyleRangeBound(range, context);
if (!upper_bound)
return nullptr;
// If the lower bound of any range is higher than the upper bound, the
// entire descriptor is invalid and must be ignored.
if (lower_bound->IsPrimitiveValue() && upper_bound->IsPrimitiveValue() &&
To<CSSPrimitiveValue>(lower_bound)->GetIntValue() >
To<CSSPrimitiveValue>(upper_bound)->GetIntValue())
return nullptr;
list->Append(*MakeGarbageCollected<CSSValuePair>(
lower_bound, upper_bound, CSSValuePair::kKeepIdenticalValues));
} while (css_parsing_utils::ConsumeCommaIncludingWhitespace(range));
if (!range.AtEnd() || !list->length())
return nullptr;
return list;
}
CSSValue* ConsumeCounterStylePad(CSSParserTokenRange& range,
const CSSParserContext& context) {
// Syntax: <integer [0,∞]> && <symbol>
CSSValue* integer = nullptr;
CSSValue* symbol = nullptr;
while (!integer || !symbol) {
if (!integer) {
integer = css_parsing_utils::ConsumeInteger(range, context, 0);
if (integer)
continue;
}
if (!symbol) {
symbol = ConsumeCounterStyleSymbol(range, context);
if (symbol)
continue;
}
return nullptr;
}
if (!range.AtEnd())
return nullptr;
return MakeGarbageCollected<CSSValuePair>(integer, symbol,
CSSValuePair::kKeepIdenticalValues);
}
CSSValue* ConsumeCounterStyleSymbols(CSSParserTokenRange& range,
const CSSParserContext& context) {
// Syntax: <symbol>+
CSSValueList* list = CSSValueList::CreateSpaceSeparated();
while (!range.AtEnd()) {
CSSValue* symbol = ConsumeCounterStyleSymbol(range, context);
if (!symbol)
return nullptr;
list->Append(*symbol);
}
if (!list->length())
return nullptr;
return list;
}
CSSValue* ConsumeCounterStyleAdditiveSymbols(CSSParserTokenRange& range,
const CSSParserContext& context) {
// Syntax: [ <integer [0,∞]> && <symbol> ]#
CSSValueList* list = CSSValueList::CreateCommaSeparated();
CSSPrimitiveValue* last_integer = nullptr;
do {
CSSPrimitiveValue* integer = nullptr;
CSSValue* symbol = nullptr;
while (!integer || !symbol) {
if (!integer) {
integer = css_parsing_utils::ConsumeInteger(range, context, 0);
if (integer)
continue;
}
if (!symbol) {
symbol = ConsumeCounterStyleSymbol(range, context);
if (symbol)
continue;
}
return nullptr;
}
if (last_integer) {
// The additive tuples must be specified in order of strictly descending
// weight; otherwise, the declaration is invalid and must be ignored.
if (integer->GetIntValue() >= last_integer->GetIntValue())
return nullptr;
}
last_integer = integer;
list->Append(*MakeGarbageCollected<CSSValuePair>(
integer, symbol, CSSValuePair::kKeepIdenticalValues));
} while (css_parsing_utils::ConsumeCommaIncludingWhitespace(range));
if (!range.AtEnd() || !list->length())
return nullptr;
return list;
}
CSSValue* ConsumeCounterStyleSpeakAs(CSSParserTokenRange& range,
const CSSParserContext& context) {
// Syntax: auto | bullets | numbers | words | spell-out | <counter-style-name>
if (CSSValue* ident = css_parsing_utils::ConsumeIdent<
CSSValueID::kAuto, CSSValueID::kBullets, CSSValueID::kNumbers,
CSSValueID::kWords, CSSValueID::kSpellOut>(range))
return ident;
if (CSSValue* name =
css_parsing_utils::ConsumeCounterStyleName(range, context))
return name;
return nullptr;
}
} // namespace
CSSValue* AtRuleDescriptorParser::ParseAtCounterStyleDescriptor(
AtRuleDescriptorID id,
CSSParserTokenRange& range,
const CSSParserContext& context) {
DCHECK(RuntimeEnabledFeatures::CSSAtRuleCounterStyleEnabled());
CSSValue* parsed_value = nullptr;
switch (id) {
case AtRuleDescriptorID::System:
range.ConsumeWhitespace();
parsed_value = ConsumeCounterStyleSystem(range, context);
break;
case AtRuleDescriptorID::Negative:
range.ConsumeWhitespace();
parsed_value = ConsumeCounterStyleNegative(range, context);
break;
case AtRuleDescriptorID::Prefix:
case AtRuleDescriptorID::Suffix:
range.ConsumeWhitespace();
parsed_value = ConsumeCounterStyleSymbol(range, context);
break;
case AtRuleDescriptorID::Range:
range.ConsumeWhitespace();
parsed_value = ConsumeCounterStyleRange(range, context);
break;
case AtRuleDescriptorID::Pad:
range.ConsumeWhitespace();
parsed_value = ConsumeCounterStylePad(range, context);
break;
case AtRuleDescriptorID::Fallback:
range.ConsumeWhitespace();
parsed_value = css_parsing_utils::ConsumeCounterStyleName(range, context);
break;
case AtRuleDescriptorID::Symbols:
range.ConsumeWhitespace();
parsed_value = ConsumeCounterStyleSymbols(range, context);
break;
case AtRuleDescriptorID::AdditiveSymbols:
range.ConsumeWhitespace();
parsed_value = ConsumeCounterStyleAdditiveSymbols(range, context);
break;
case AtRuleDescriptorID::SpeakAs:
range.ConsumeWhitespace();
parsed_value = ConsumeCounterStyleSpeakAs(range, context);
break;
default:
break;
}
if (!parsed_value || !range.AtEnd())
return nullptr;
return parsed_value;
}
} // namespace blink