diff --git a/Tests/LibWeb/Ref/css-quotes.html b/Tests/LibWeb/Ref/css-quotes.html
new file mode 100644
index 0000000000..92f7cfb365
--- /dev/null
+++ b/Tests/LibWeb/Ref/css-quotes.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/Tests/LibWeb/Ref/reference/css-quotes-ref.html b/Tests/LibWeb/Ref/reference/css-quotes-ref.html
new file mode 100644
index 0000000000..31ebbb8c84
--- /dev/null
+++ b/Tests/LibWeb/Ref/reference/css-quotes-ref.html
@@ -0,0 +1,4 @@
+
+Well, hello friends!
+“Well, hello friends!”
+/* Well, hello friends! */
diff --git a/Userland/Libraries/LibWeb/CSS/Identifiers.json b/Userland/Libraries/LibWeb/CSS/Identifiers.json
index 7ba577e50e..8a695ed7a6 100644
--- a/Userland/Libraries/LibWeb/CSS/Identifiers.json
+++ b/Userland/Libraries/LibWeb/CSS/Identifiers.json
@@ -101,6 +101,7 @@
"center",
"circle",
"clip",
+ "close-quote",
"coarse",
"col-resize",
"collapse",
@@ -240,7 +241,9 @@
"ne-resize",
"nearest",
"nesw-resize",
+ "no-close-quote",
"no-drop",
+ "no-open-quote",
"no-preference",
"no-repeat",
"none",
@@ -253,6 +256,7 @@
"nwse-resize",
"oblique",
"opaque",
+ "open-quote",
"optimizequality",
"optimizespeed",
"outset",
diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json
index b1e4268529..cd487ecc97 100644
--- a/Userland/Libraries/LibWeb/CSS/Properties.json
+++ b/Userland/Libraries/LibWeb/CSS/Properties.json
@@ -699,7 +699,11 @@
],
"valid-identifiers": [
"normal",
- "none"
+ "none",
+ "open-quote",
+ "close-quote",
+ "no-open-quote",
+ "no-close-quote"
]
},
"cursor": {
diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
index 67f7f10924..94c1167cac 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
+++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp
@@ -649,6 +649,25 @@ Optional StyleProperties::clear() const
CSS::ContentData StyleProperties::content() const
{
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()) {
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()) {
if (item->is_string()) {
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 {
- // 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.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()) {
StringBuilder alt_text_builder;
@@ -677,7 +717,7 @@ CSS::ContentData StyleProperties::content() const
// 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;