blob: 4188af07a731206eaa1c36c95ec22209736f120e [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/properties/longhands/content.h"
#include "third_party/blink/renderer/core/css/css_counter_value.h"
#include "third_party/blink/renderer/core/css/css_function_value.h"
#include "third_party/blink/renderer/core/css/css_string_value.h"
#include "third_party/blink/renderer/core/css/css_value_list.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h"
#include "third_party/blink/renderer/core/css/properties/computed_style_utils.h"
#include "third_party/blink/renderer/core/css_value_keywords.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
namespace blink {
namespace {
CSSValue* ConsumeAttr(CSSParserTokenRange args,
const CSSParserContext& context) {
if (args.Peek().GetType() != kIdentToken)
return nullptr;
AtomicString attr_name =
args.ConsumeIncludingWhitespace().Value().ToAtomicString();
if (!args.AtEnd())
return nullptr;
if (context.IsHTMLDocument())
attr_name = attr_name.LowerASCII();
CSSFunctionValue* attr_value = CSSFunctionValue::Create(CSSValueAttr);
attr_value->Append(*CSSCustomIdentValue::Create(attr_name));
return attr_value;
}
CSSValue* ConsumeCounterContent(CSSParserTokenRange args,
const CSSParserContext& context,
bool counters) {
CSSCustomIdentValue* identifier =
css_property_parser_helpers::ConsumeCustomIdent(args, context);
if (!identifier)
return nullptr;
CSSStringValue* separator = nullptr;
if (!counters) {
separator = CSSStringValue::Create(String());
} else {
if (!css_property_parser_helpers::ConsumeCommaIncludingWhitespace(args) ||
args.Peek().GetType() != kStringToken)
return nullptr;
separator = CSSStringValue::Create(
args.ConsumeIncludingWhitespace().Value().ToString());
}
CSSIdentifierValue* list_style = nullptr;
if (css_property_parser_helpers::ConsumeCommaIncludingWhitespace(args)) {
CSSValueID id = args.Peek().Id();
if ((id != CSSValueNone &&
(id < CSSValueDisc || id > CSSValueKatakanaIroha)))
return nullptr;
list_style = css_property_parser_helpers::ConsumeIdent(args);
} else {
list_style = CSSIdentifierValue::Create(CSSValueDecimal);
}
if (!args.AtEnd())
return nullptr;
return cssvalue::CSSCounterValue::Create(identifier, list_style, separator);
}
} // namespace
namespace css_longhand {
const CSSValue* Content::ParseSingleValue(CSSParserTokenRange& range,
const CSSParserContext& context,
const CSSParserLocalContext&) const {
if (css_property_parser_helpers::IdentMatches<CSSValueNone, CSSValueNormal>(
range.Peek().Id()))
return css_property_parser_helpers::ConsumeIdent(range);
CSSValueList* values = CSSValueList::CreateSpaceSeparated();
do {
CSSValue* parsed_value =
css_property_parser_helpers::ConsumeImage(range, &context);
if (!parsed_value) {
parsed_value = css_property_parser_helpers::ConsumeIdent<
CSSValueOpenQuote, CSSValueCloseQuote, CSSValueNoOpenQuote,
CSSValueNoCloseQuote>(range);
}
if (!parsed_value)
parsed_value = css_property_parser_helpers::ConsumeString(range);
if (!parsed_value) {
if (range.Peek().FunctionId() == CSSValueAttr) {
parsed_value = ConsumeAttr(
css_property_parser_helpers::ConsumeFunction(range), context);
} else if (range.Peek().FunctionId() == CSSValueCounter) {
parsed_value = ConsumeCounterContent(
css_property_parser_helpers::ConsumeFunction(range), context,
false);
} else if (range.Peek().FunctionId() == CSSValueCounters) {
parsed_value = ConsumeCounterContent(
css_property_parser_helpers::ConsumeFunction(range), context, true);
}
if (!parsed_value)
return nullptr;
}
values->Append(*parsed_value);
} while (!range.AtEnd());
return values;
}
const CSSValue* Content::CSSValueFromComputedStyleInternal(
const ComputedStyle& style,
const SVGComputedStyle&,
const LayoutObject*,
Node* styled_node,
bool allow_visited_style) const {
return ComputedStyleUtils::ValueForContentData(style);
}
void Content::ApplyInitial(StyleResolverState& state) const {
state.Style()->SetContent(nullptr);
}
void Content::ApplyInherit(StyleResolverState& state) const {
// FIXME: In CSS3, it will be possible to inherit content. In CSS2 it is not.
// This note is a reminder that eventually "inherit" needs to be supported.
}
void Content::ApplyValue(StyleResolverState& state,
const CSSValue& value) const {
if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
DCHECK(identifier_value->GetValueID() == CSSValueNormal ||
identifier_value->GetValueID() == CSSValueNone);
state.Style()->SetContent(nullptr);
return;
}
ContentData* first_content = nullptr;
ContentData* prev_content = nullptr;
for (auto& item : To<CSSValueList>(value)) {
ContentData* next_content = nullptr;
if (item->IsImageGeneratorValue() || item->IsImageSetValue() ||
item->IsImageValue()) {
next_content = ContentData::Create(
state.GetStyleImage(CSSPropertyID::kContent, *item));
} else if (const auto* counter_value =
DynamicTo<cssvalue::CSSCounterValue>(item.Get())) {
const auto list_style_type =
CssValueIDToPlatformEnum<EListStyleType>(counter_value->ListStyle());
std::unique_ptr<CounterContent> counter =
std::make_unique<CounterContent>(
AtomicString(counter_value->Identifier()), list_style_type,
AtomicString(counter_value->Separator()));
next_content = ContentData::Create(std::move(counter));
} else if (auto* item_identifier_value =
DynamicTo<CSSIdentifierValue>(item.Get())) {
QuoteType quote_type;
switch (item_identifier_value->GetValueID()) {
default:
NOTREACHED();
FALLTHROUGH;
case CSSValueOpenQuote:
quote_type = QuoteType::kOpen;
break;
case CSSValueCloseQuote:
quote_type = QuoteType::kClose;
break;
case CSSValueNoOpenQuote:
quote_type = QuoteType::kNoOpen;
break;
case CSSValueNoCloseQuote:
quote_type = QuoteType::kNoClose;
break;
}
next_content = ContentData::Create(quote_type);
} else {
String string;
if (const auto* function_value =
DynamicTo<CSSFunctionValue>(item.Get())) {
DCHECK_EQ(function_value->FunctionType(), CSSValueAttr);
state.Style()->SetHasAttrContent();
// TODO: Can a namespace be specified for an attr(foo)?
QualifiedName attr(
g_null_atom,
To<CSSCustomIdentValue>(function_value->Item(0)).Value(),
g_null_atom);
const AtomicString& attr_value = state.GetElement()->getAttribute(attr);
string = attr_value.IsNull() ? g_empty_string : attr_value.GetString();
} else {
string = To<CSSStringValue>(*item).Value();
}
if (prev_content && prev_content->IsText()) {
TextContentData* text_content = To<TextContentData>(prev_content);
text_content->SetText(text_content->GetText() + string);
continue;
}
next_content = ContentData::Create(string);
}
if (!first_content)
first_content = next_content;
else
prev_content->SetNext(next_content);
prev_content = next_content;
}
DCHECK(first_content);
state.Style()->SetContent(first_content);
}
} // namespace css_longhand
} // namespace blink