blob: 17b3d4a5d07b38819b7681b1142ac36d33c7e998 [file] [log] [blame]
// Copyright 2017 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_custom_property_declaration.h"
#include "third_party/blink/renderer/core/css/css_font_face_src_value.h"
#include "third_party/blink/renderer/core/css/css_id_selector_value.h"
#include "third_party/blink/renderer/core/css/css_string_value.h"
#include "third_party/blink/renderer/core/css/css_unicode_range_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_mode.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
#include "third_party/blink/renderer/core/css/parser/css_tokenizer.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/css/properties/css_property.h"
namespace blink {
namespace {
CSSValue* ConsumeFontVariantList(CSSParserTokenRange& range) {
CSSValueList* values = CSSValueList::CreateCommaSeparated();
do {
if (range.Peek().Id() == CSSValueID::kAll) {
// FIXME: CSSPropertyParser::ParseFontVariant() implements
// the old css3 draft:
// http://www.w3.org/TR/2002/WD-css3-webfonts-20020802/#font-variant
// 'all' is only allowed in @font-face and with no other values.
if (values->length())
return nullptr;
return css_parsing_utils::ConsumeIdent(range);
}
CSSIdentifierValue* font_variant =
css_parsing_utils::ConsumeFontVariantCSS21(range);
if (font_variant)
values->Append(*font_variant);
} while (css_parsing_utils::ConsumeCommaIncludingWhitespace(range));
if (values->length())
return values;
return nullptr;
}
CSSIdentifierValue* ConsumeFontDisplay(CSSParserTokenRange& range) {
return css_parsing_utils::ConsumeIdent<
CSSValueID::kAuto, CSSValueID::kBlock, CSSValueID::kSwap,
CSSValueID::kFallback, CSSValueID::kOptional>(range);
}
CSSValueList* ConsumeFontFaceUnicodeRange(CSSParserTokenRange& range) {
CSSValueList* values = CSSValueList::CreateCommaSeparated();
do {
const CSSParserToken& token = range.ConsumeIncludingWhitespace();
if (token.GetType() != kUnicodeRangeToken)
return nullptr;
UChar32 start = token.UnicodeRangeStart();
UChar32 end = token.UnicodeRangeEnd();
if (start > end)
return nullptr;
values->Append(
*MakeGarbageCollected<cssvalue::CSSUnicodeRangeValue>(start, end));
} while (css_parsing_utils::ConsumeCommaIncludingWhitespace(range));
return values;
}
CSSValue* ConsumeFontFaceSrcURI(CSSParserTokenRange& range,
const CSSParserContext& context) {
String url =
css_parsing_utils::ConsumeUrlAsStringView(range, context).ToString();
if (url.IsNull())
return nullptr;
CSSFontFaceSrcValue* uri_value(CSSFontFaceSrcValue::Create(
url, context.CompleteURL(url), context.GetReferrer(),
context.JavascriptWorld(),
context.IsOriginClean() ? OriginClean::kTrue : OriginClean::kFalse,
context.IsAdRelated()));
if (range.Peek().FunctionId() != CSSValueID::kFormat)
return uri_value;
// FIXME: https://drafts.csswg.org/css-fonts says that format() contains a
// comma-separated list of strings, but CSSFontFaceSrcValue stores only one
// format. Allowing one format for now.
CSSParserTokenRange args = css_parsing_utils::ConsumeFunction(range);
const CSSParserToken& arg = args.ConsumeIncludingWhitespace();
if ((arg.GetType() != kStringToken) || !args.AtEnd())
return nullptr;
uri_value->SetFormat(arg.Value().ToString());
return uri_value;
}
CSSValue* ConsumeFontFaceSrcLocal(CSSParserTokenRange& range,
const CSSParserContext& context) {
CSSParserTokenRange args = css_parsing_utils::ConsumeFunction(range);
if (args.Peek().GetType() == kStringToken) {
const CSSParserToken& arg = args.ConsumeIncludingWhitespace();
if (!args.AtEnd())
return nullptr;
return CSSFontFaceSrcValue::CreateLocal(
arg.Value().ToString(), context.JavascriptWorld(),
context.IsOriginClean() ? OriginClean::kTrue : OriginClean::kFalse,
context.IsAdRelated());
}
if (args.Peek().GetType() == kIdentToken) {
String family_name = css_parsing_utils::ConcatenateFamilyName(args);
if (!args.AtEnd())
return nullptr;
return CSSFontFaceSrcValue::CreateLocal(
family_name, context.JavascriptWorld(),
context.IsOriginClean() ? OriginClean::kTrue : OriginClean::kFalse,
context.IsAdRelated());
}
return nullptr;
}
CSSValueList* ConsumeFontFaceSrc(CSSParserTokenRange& range,
const CSSParserContext& context) {
CSSValueList* values = CSSValueList::CreateCommaSeparated();
range.ConsumeWhitespace();
do {
const CSSParserToken& token = range.Peek();
CSSValue* parsed_value = nullptr;
if (token.FunctionId() == CSSValueID::kLocal)
parsed_value = ConsumeFontFaceSrcLocal(range, context);
else
parsed_value = ConsumeFontFaceSrcURI(range, context);
if (!parsed_value)
return nullptr;
values->Append(*parsed_value);
} while (css_parsing_utils::ConsumeCommaIncludingWhitespace(range));
return values;
}
CSSValue* ConsumeScrollTimelineSource(CSSParserTokenRange& range) {
if (auto* selector_function =
css_parsing_utils::ConsumeSelectorFunction(range)) {
return selector_function;
}
return css_parsing_utils::ConsumeIdent<CSSValueID::kAuto, CSSValueID::kNone>(
range);
}
CSSValue* ConsumeScrollTimelineOrientation(CSSParserTokenRange& range) {
return css_parsing_utils::ConsumeIdent<
CSSValueID::kAuto, CSSValueID::kBlock, CSSValueID::kInline,
CSSValueID::kHorizontal, CSSValueID::kVertical>(range);
}
CSSValue* ConsumeTimeRange(CSSParserTokenRange& range,
const CSSParserContext& context) {
if (auto* value = css_parsing_utils::ConsumeIdent<CSSValueID::kAuto>(range))
return value;
return css_parsing_utils::ConsumeTime(range, context, kValueRangeAll);
}
CSSValue* ConsumeDescriptor(StyleRule::RuleType rule_type,
AtRuleDescriptorID id,
const CSSTokenizedValue& tokenized_value,
const CSSParserContext& context) {
using Parser = AtRuleDescriptorParser;
CSSParserTokenRange range = tokenized_value.range;
switch (rule_type) {
case StyleRule::kFontFace:
return Parser::ParseFontFaceDescriptor(id, range, context);
case StyleRule::kProperty:
return Parser::ParseAtPropertyDescriptor(id, tokenized_value, context);
case StyleRule::kCounterStyle:
return Parser::ParseAtCounterStyleDescriptor(id, range, context);
case StyleRule::kScrollTimeline:
return Parser::ParseAtScrollTimelineDescriptor(id, range, context);
case StyleRule::kCharset:
case StyleRule::kContainer:
case StyleRule::kStyle:
case StyleRule::kImport:
case StyleRule::kMedia:
case StyleRule::kPage:
case StyleRule::kKeyframes:
case StyleRule::kKeyframe:
case StyleRule::kNamespace:
case StyleRule::kSupports:
case StyleRule::kViewport:
// TODO(andruud): Handle other descriptor types here.
NOTREACHED();
return nullptr;
}
}
CSSValue* ConsumeFontMetricOverride(CSSParserTokenRange& range,
const CSSParserContext& context) {
if (!RuntimeEnabledFeatures::CSSFontMetricsOverrideEnabled())
return nullptr;
if (CSSIdentifierValue* normal =
css_parsing_utils::ConsumeIdent<CSSValueID::kNormal>(range)) {
return normal;
}
return css_parsing_utils::ConsumePercent(range, context,
kValueRangeNonNegative);
}
CSSValue* ConsumeAdvanceOverride(CSSParserTokenRange& range,
const CSSParserContext& context) {
if (!RuntimeEnabledFeatures::CSSFontFaceAdvanceOverrideEnabled())
return nullptr;
if (CSSIdentifierValue* normal =
css_parsing_utils::ConsumeIdent<CSSValueID::kNormal>(range)) {
return normal;
}
CSSValue* override_horizontal =
css_parsing_utils::ConsumePercent(range, context, kValueRangeNonNegative);
if (!override_horizontal)
return nullptr;
CSSValue* override_vertical_upright =
css_parsing_utils::ConsumePercent(range, context, kValueRangeNonNegative);
if (!override_vertical_upright)
override_vertical_upright = override_horizontal;
return MakeGarbageCollected<CSSValuePair>(override_horizontal,
override_vertical_upright,
CSSValuePair::kDropIdenticalValues);
}
} // namespace
CSSValue* AtRuleDescriptorParser::ParseFontFaceDescriptor(
AtRuleDescriptorID id,
CSSParserTokenRange& range,
const CSSParserContext& context) {
CSSValue* parsed_value = nullptr;
range.ConsumeWhitespace();
switch (id) {
case AtRuleDescriptorID::FontFamily:
if (css_parsing_utils::ConsumeGenericFamily(range))
return nullptr;
parsed_value = css_parsing_utils::ConsumeFamilyName(range);
break;
case AtRuleDescriptorID::Src: // This is a list of urls or local
// references.
parsed_value = ConsumeFontFaceSrc(range, context);
break;
case AtRuleDescriptorID::UnicodeRange:
parsed_value = ConsumeFontFaceUnicodeRange(range);
break;
case AtRuleDescriptorID::FontDisplay:
parsed_value = ConsumeFontDisplay(range);
break;
case AtRuleDescriptorID::FontStretch: {
CSSParserContext::ParserModeOverridingScope scope(context,
kCSSFontFaceRuleMode);
parsed_value = css_parsing_utils::ConsumeFontStretch(range, context);
break;
}
case AtRuleDescriptorID::FontStyle: {
CSSParserContext::ParserModeOverridingScope scope(context,
kCSSFontFaceRuleMode);
parsed_value = css_parsing_utils::ConsumeFontStyle(range, context);
break;
}
case AtRuleDescriptorID::FontVariant:
parsed_value = ConsumeFontVariantList(range);
break;
case AtRuleDescriptorID::FontWeight: {
CSSParserContext::ParserModeOverridingScope scope(context,
kCSSFontFaceRuleMode);
parsed_value = css_parsing_utils::ConsumeFontWeight(range, context);
break;
}
case AtRuleDescriptorID::FontFeatureSettings:
parsed_value =
css_parsing_utils::ConsumeFontFeatureSettings(range, context);
break;
case AtRuleDescriptorID::AscentOverride:
case AtRuleDescriptorID::DescentOverride:
case AtRuleDescriptorID::LineGapOverride:
parsed_value = ConsumeFontMetricOverride(range, context);
break;
case AtRuleDescriptorID::AdvanceOverride:
parsed_value = ConsumeAdvanceOverride(range, context);
break;
default:
break;
}
if (!parsed_value || !range.AtEnd())
return nullptr;
return parsed_value;
}
CSSValue* AtRuleDescriptorParser::ParseFontFaceDescriptor(
AtRuleDescriptorID id,
const String& string,
const CSSParserContext& context) {
CSSTokenizer tokenizer(string);
Vector<CSSParserToken, 32> tokens = tokenizer.TokenizeToEOF();
CSSParserTokenRange range = CSSParserTokenRange(tokens);
return ParseFontFaceDescriptor(id, range, context);
}
CSSValue* AtRuleDescriptorParser::ParseFontFaceDeclaration(
CSSParserTokenRange& range,
const CSSParserContext& context) {
DCHECK_EQ(range.Peek().GetType(), kIdentToken);
const CSSParserToken& token = range.ConsumeIncludingWhitespace();
AtRuleDescriptorID id = token.ParseAsAtRuleDescriptorID();
if (range.Consume().GetType() != kColonToken)
return nullptr; // Parse error
return ParseFontFaceDescriptor(id, range, context);
}
CSSValue* AtRuleDescriptorParser::ParseAtPropertyDescriptor(
AtRuleDescriptorID id,
const CSSTokenizedValue& tokenized_value,
const CSSParserContext& context) {
CSSValue* parsed_value = nullptr;
CSSParserTokenRange range = tokenized_value.range;
switch (id) {
case AtRuleDescriptorID::Syntax:
range.ConsumeWhitespace();
parsed_value = css_parsing_utils::ConsumeString(range);
break;
case AtRuleDescriptorID::InitialValue: {
// Note that we must retain leading whitespace here.
return CSSVariableParser::ParseDeclarationValue(
g_null_atom, tokenized_value, false /* is_animation_tainted */,
context);
}
case AtRuleDescriptorID::Inherits:
range.ConsumeWhitespace();
parsed_value = css_parsing_utils::ConsumeIdent<CSSValueID::kTrue,
CSSValueID::kFalse>(range);
break;
default:
break;
}
if (!parsed_value || !range.AtEnd())
return nullptr;
return parsed_value;
}
CSSValue* AtRuleDescriptorParser::ParseAtScrollTimelineDescriptor(
AtRuleDescriptorID id,
CSSParserTokenRange& range,
const CSSParserContext& context) {
CSSValue* parsed_value = nullptr;
range.ConsumeWhitespace();
switch (id) {
case AtRuleDescriptorID::Source:
parsed_value = ConsumeScrollTimelineSource(range);
break;
case AtRuleDescriptorID::Orientation:
parsed_value = ConsumeScrollTimelineOrientation(range);
break;
case AtRuleDescriptorID::Start:
case AtRuleDescriptorID::End:
parsed_value = css_parsing_utils::ConsumeScrollOffset(range, context);
break;
case AtRuleDescriptorID::TimeRange:
parsed_value = ConsumeTimeRange(range, context);
break;
default:
break;
}
if (!parsed_value || !range.AtEnd())
return nullptr;
return parsed_value;
}
bool AtRuleDescriptorParser::ParseAtRule(
StyleRule::RuleType rule_type,
AtRuleDescriptorID id,
const CSSTokenizedValue& tokenized_value,
const CSSParserContext& context,
HeapVector<CSSPropertyValue, 256>& parsed_descriptors) {
CSSValue* result = ConsumeDescriptor(rule_type, id, tokenized_value, context);
if (!result)
return false;
// Convert to CSSPropertyID for legacy compatibility,
// TODO(crbug.com/752745): Refactor CSSParserImpl to avoid using
// the CSSPropertyID.
CSSPropertyID equivalent_property_id = AtRuleDescriptorIDAsCSSPropertyID(id);
parsed_descriptors.push_back(
CSSPropertyValue(CSSPropertyName(equivalent_property_id), *result));
return true;
}
} // namespace blink