1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 03:27:45 +00:00

LibWeb: Support quotes in content values

This adds initial support for `open-quote`, `close-quote`,
`no-open-quote` and `no-close-quote`. We don't yet track the "nesting
level" so we always use the first pair of quotes from the `quotes`
property.
This commit is contained in:
Sam Atkins 2023-09-12 14:22:46 +01:00 committed by Andrew Kaster
parent dc7a52957e
commit f0a4baabc7
5 changed files with 75 additions and 4 deletions

View file

@ -0,0 +1,19 @@
<!doctype html>
<link rel="match" href="reference/css-quotes-ref.html" />
<style>
div::before {
content: open-quote "Well, hello friends!" close-quote;
}
.a {
quotes: none;
}
.b {
quotes: auto;
}
.c {
quotes: "/* " " */";
}
</style>
<div class="a"></div>
<div class="b"></div>
<div class="c"></div>

View file

@ -0,0 +1,4 @@
<!doctype html>
<div class="a">Well, hello friends!</div>
<div class="b">“Well, hello friends!”</div>
<div class="c">/* Well, hello friends! */</div>

View file

@ -101,6 +101,7 @@
"center", "center",
"circle", "circle",
"clip", "clip",
"close-quote",
"coarse", "coarse",
"col-resize", "col-resize",
"collapse", "collapse",
@ -240,7 +241,9 @@
"ne-resize", "ne-resize",
"nearest", "nearest",
"nesw-resize", "nesw-resize",
"no-close-quote",
"no-drop", "no-drop",
"no-open-quote",
"no-preference", "no-preference",
"no-repeat", "no-repeat",
"none", "none",
@ -253,6 +256,7 @@
"nwse-resize", "nwse-resize",
"oblique", "oblique",
"opaque", "opaque",
"open-quote",
"optimizequality", "optimizequality",
"optimizespeed", "optimizespeed",
"outset", "outset",

View file

@ -699,7 +699,11 @@
], ],
"valid-identifiers": [ "valid-identifiers": [
"normal", "normal",
"none" "none",
"open-quote",
"close-quote",
"no-open-quote",
"no-close-quote"
] ]
}, },
"cursor": { "cursor": {

View file

@ -649,6 +649,25 @@ Optional<CSS::Clear> StyleProperties::clear() const
CSS::ContentData StyleProperties::content() const CSS::ContentData StyleProperties::content() const
{ {
auto value = property(CSS::PropertyID::Content); auto value = property(CSS::PropertyID::Content);
auto quotes_data = quotes();
auto get_quote_string = [&](bool open, auto depth) {
switch (quotes_data.type) {
case QuotesData::Type::None:
return String {};
case QuotesData::Type::Auto:
// FIXME: "A typographically appropriate used value for quotes is automatically chosen by the UA
// based on the content language of the element and/or its parent."
if (open)
return depth % 2 ? ""_string : ""_string;
return depth % 2 ? ""_string : ""_string;
case QuotesData::Type::Specified:
auto& level = quotes_data.strings[depth % quotes_data.strings.size()];
return open ? level[0] : level[1];
}
VERIFY_NOT_REACHED();
};
if (value->is_content()) { if (value->is_content()) {
auto& content_style_value = value->as_content(); auto& content_style_value = value->as_content();
@ -661,12 +680,33 @@ CSS::ContentData StyleProperties::content() const
for (auto const& item : content_style_value.content().values()) { for (auto const& item : content_style_value.content().values()) {
if (item->is_string()) { if (item->is_string()) {
builder.append(item->as_string().string_value()); builder.append(item->as_string().string_value());
} else if (item->is_identifier()) {
switch (item->to_identifier()) {
case ValueID::OpenQuote:
// FIXME: Track nesting level and increment it here.
builder.append(get_quote_string(true, 1));
break;
case ValueID::CloseQuote:
// FIXME: Track nesting level and decrement it here.
builder.append(get_quote_string(false, 1));
break;
case ValueID::NoOpenQuote:
// FIXME: Track nesting level and increment it here.
break;
case ValueID::NoCloseQuote:
// FIXME: Track nesting level and decrement it here.
break;
default:
dbgln("`{}` is not supported in `content` (yet?)", item->to_string());
break;
}
} else { } else {
// TODO: Implement quotes, counters, images, and other things. // TODO: Implement counters, images, and other things.
dbgln("`{}` is not supported in `content` (yet?)", item->to_string());
} }
} }
content_data.type = ContentData::Type::String; content_data.type = ContentData::Type::String;
content_data.data = builder.to_string().release_value_but_fixme_should_propagate_errors(); content_data.data = MUST(builder.to_string());
if (content_style_value.has_alt_text()) { if (content_style_value.has_alt_text()) {
StringBuilder alt_text_builder; StringBuilder alt_text_builder;
@ -677,7 +717,7 @@ CSS::ContentData StyleProperties::content() const
// TODO: Implement counters // TODO: Implement counters
} }
} }
content_data.alt_text = alt_text_builder.to_string().release_value_but_fixme_should_propagate_errors(); content_data.alt_text = MUST(alt_text_builder.to_string());
} }
return content_data; return content_data;