1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-28 17:27:46 +00:00

LibWeb: Track quote-nesting level while building the layout tree

This makes multiple levels of quote actually use different quotation
marks, instead of always the first available pair of them.

Each Layout::Node remembers what the quote-nesting level was before its
content was evaluated, so that we can re-use this number in
`apply_style()`. This is a bit hacky, since we end up converting the
`content` value into a string twice.

`StyleProperties::content()` now takes an initial quote-nesting level,
and returns the final level after that content.
This commit is contained in:
Sam Atkins 2023-09-18 15:41:17 +01:00 committed by Andreas Kling
parent 493dd5d93c
commit 9e99368694
8 changed files with 372 additions and 14 deletions

View file

@ -646,11 +646,13 @@ Optional<CSS::Clear> StyleProperties::clear() const
return value_id_to_clear(value->to_identifier());
}
CSS::ContentData StyleProperties::content() const
StyleProperties::ContentDataAndQuoteNestingLevel StyleProperties::content(u32 initial_quote_nesting_level) const
{
auto value = property(CSS::PropertyID::Content);
auto quotes_data = quotes();
auto quote_nesting_level = initial_quote_nesting_level;
auto get_quote_string = [&](bool open, auto depth) {
switch (quotes_data.type) {
case QuotesData::Type::None:
@ -684,18 +686,24 @@ CSS::ContentData StyleProperties::content() const
} 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));
builder.append(get_quote_string(true, quote_nesting_level++));
break;
case ValueID::CloseQuote:
// FIXME: Track nesting level and decrement it here.
builder.append(get_quote_string(false, 1));
// A 'close-quote' or 'no-close-quote' that would make the depth negative is in error and is ignored
// (at rendering time): the depth stays at 0 and no quote mark is rendered (although the rest of the
// 'content' property's value is still inserted).
// - https://www.w3.org/TR/CSS21/generate.html#quotes-insert
// (This is missing from the CONTENT-3 spec.)
if (quote_nesting_level > 0)
builder.append(get_quote_string(false, --quote_nesting_level));
break;
case ValueID::NoOpenQuote:
// FIXME: Track nesting level and increment it here.
quote_nesting_level++;
break;
case ValueID::NoCloseQuote:
// FIXME: Track nesting level and decrement it here.
// NOTE: See CloseQuote
if (quote_nesting_level > 0)
quote_nesting_level--;
break;
default:
dbgln("`{}` is not supported in `content` (yet?)", item->to_string());
@ -721,19 +729,19 @@ CSS::ContentData StyleProperties::content() const
content_data.alt_text = MUST(alt_text_builder.to_string());
}
return content_data;
return { content_data, quote_nesting_level };
}
switch (value->to_identifier()) {
case ValueID::None:
return { ContentData::Type::None };
return { { ContentData::Type::None }, quote_nesting_level };
case ValueID::Normal:
return { ContentData::Type::Normal };
return { { ContentData::Type::Normal }, quote_nesting_level };
default:
break;
}
return CSS::ContentData {};
return { {}, quote_nesting_level };
}
Optional<CSS::Cursor> StyleProperties::cursor() const

View file

@ -64,7 +64,11 @@ public:
CSS::Display display() const;
Optional<CSS::Float> float_() const;
Optional<CSS::Clear> clear() const;
CSS::ContentData content() const;
struct ContentDataAndQuoteNestingLevel {
CSS::ContentData content_data;
u32 final_quote_nesting_level { 0 };
};
ContentDataAndQuoteNestingLevel content(u32 initial_quote_nesting_level) const;
Optional<CSS::Cursor> cursor() const;
Optional<CSS::WhiteSpace> white_space() const;
Optional<CSS::LineStyle> line_style(CSS::PropertyID) const;

View file

@ -743,7 +743,8 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
if (auto outline_width = computed_style.property(CSS::PropertyID::OutlineWidth); outline_width->is_length())
computed_values.set_outline_width(outline_width->as_length().length());
computed_values.set_content(computed_style.content());
// FIXME: Stop generating the content twice. (First time is in TreeBuilder.)
computed_values.set_content(computed_style.content(initial_quote_nesting_level()).content_data);
computed_values.set_grid_auto_columns(computed_style.grid_auto_columns());
computed_values.set_grid_auto_rows(computed_style.grid_auto_rows());
computed_values.set_grid_template_columns(computed_style.grid_template_columns());

View file

@ -175,6 +175,9 @@ public:
SelectionState selection_state() const { return m_selection_state; }
void set_selection_state(SelectionState state) { m_selection_state = state; }
u32 initial_quote_nesting_level() const { return m_initial_quote_nesting_level; }
void set_initial_quote_nesting_level(u32 value) { m_initial_quote_nesting_level = value; }
protected:
Node(DOM::Document&, DOM::Node*);
@ -199,6 +202,8 @@ private:
bool m_is_grid_item { false };
GeneratedFor m_generated_for { GeneratedFor::NotGenerated };
u32 m_initial_quote_nesting_level { 0 };
};
class NodeWithStyle : public Node {

View file

@ -181,7 +181,9 @@ ErrorOr<void> TreeBuilder::create_pseudo_element_if_needed(DOM::Element& element
if (!pseudo_element_style)
return {};
auto pseudo_element_content = pseudo_element_style->content();
auto initial_quote_nesting_level = m_quote_nesting_level;
auto [pseudo_element_content, final_quote_nesting_level] = pseudo_element_style->content(initial_quote_nesting_level);
m_quote_nesting_level = final_quote_nesting_level;
auto pseudo_element_display = pseudo_element_style->display();
// ::before and ::after only exist if they have content. `content: normal` computes to `none` for them.
// We also don't create them if they are `display: none`.
@ -204,6 +206,7 @@ ErrorOr<void> TreeBuilder::create_pseudo_element_if_needed(DOM::Element& element
}
pseudo_element_node->set_generated_for(generated_for, element);
pseudo_element_node->set_initial_quote_nesting_level(initial_quote_nesting_level);
// FIXME: Handle images, and multiple values
if (pseudo_element_content.type == CSS::ContentData::Type::String) {
@ -452,6 +455,7 @@ JS::GCPtr<Layout::Node> TreeBuilder::build(DOM::Node& dom_node)
VERIFY(dom_node.is_document());
Context context;
m_quote_nesting_level = 0;
MUST(create_layout_tree(dom_node, context)); // FIXME propagate errors
if (auto* root = dom_node.document().layout_node())

View file

@ -51,6 +51,8 @@ private:
JS::GCPtr<Layout::Node> m_layout_root;
Vector<JS::NonnullGCPtr<Layout::NodeWithStyle>> m_ancestor_stack;
u32 m_quote_nesting_level { 0 };
};
}