LibWeb: Fix accessible-name computation for pseudo-element content

This change fixes handling for substep ii of the “F. Name From Content”
step at https://w3c.github.io/accname/#step2F in the “Accessible Name
and Description Computation” spec — to correctly include any ::before
and ::after pseudo-element content in the computation of accessible
names. Otherwise, without this change, accessible names unexpectedly
don’t include that pseudo-element content.
This commit is contained in:
sideshowbarker 2024-11-04 14:47:55 +09:00 committed by Andrew Kaster
parent 3ba7c53668
commit b1587cc60f
Notes: github-actions[bot] 2024-11-11 21:58:09 +00:00
3 changed files with 24 additions and 14 deletions

View file

@ -330,7 +330,7 @@ struct ContentData {
// FIXME: Data is a list of identifiers, strings and image values.
String data {};
String alt_text {};
Optional<String> alt_text {};
};
struct CounterData {

View file

@ -2333,17 +2333,20 @@ ErrorOr<String> Node::name_or_description(NameOrDescription target, Document con
if ((role.has_value() && ARIA::allows_name_from_content(role.value())) || is_descendant == IsDescendant::Yes) {
// i. Set the accumulated text to the empty string.
total_accumulated_text.clear();
// ii. Check for CSS generated textual content associated with the current node and include it in the accumulated text. The CSS :before and :after pseudo elements [CSS2] can provide textual content for elements that have a content model.
auto before = element->get_pseudo_element_node(CSS::Selector::PseudoElement::Type::Before);
auto after = element->get_pseudo_element_node(CSS::Selector::PseudoElement::Type::After);
// - For :before pseudo elements, User agents MUST prepend CSS textual content, without a space, to the textual content of the current node.
if (before)
TRY(Node::prepend_without_space(total_accumulated_text, before->computed_values().content().data));
// - For :after pseudo elements, User agents MUST append CSS textual content, without a space, to the textual content of the current node.
if (after)
TRY(Node::append_without_space(total_accumulated_text, after->computed_values().content().data));
// ii. Name From Generated Content: Check for CSS generated textual content associated with the current node and include
// it in the accumulated text. The CSS ::before and ::after pseudo elements [CSS2] can provide textual content for
// elements that have a content model.
// a. For ::before pseudo elements, User agents MUST prepend CSS textual content, without a space, to the textual
// content of the current node.
// b. For ::after pseudo elements, User agents MUST append CSS textual content, without a space, to the textual content
// of the current node. NOTE: The code for handling the ::after pseudo elements case is further below,
// following the “iii. For each child node of the current node” code.
if (auto before = element->get_pseudo_element_node(CSS::Selector::PseudoElement::Type::Before)) {
if (before->computed_values().content().alt_text.has_value())
total_accumulated_text.append(before->computed_values().content().alt_text.release_value());
else
total_accumulated_text.append(before->computed_values().content().data);
}
// iii. For each child node of the current node:
element->for_each_child([&total_accumulated_text, current_node, target, &document, &visited_nodes](
DOM::Node const& child_node) mutable {
@ -2377,8 +2380,14 @@ ErrorOr<String> Node::name_or_description(NameOrDescription target, Document con
return IterationDecision::Continue;
});
// NOTE: See step ii.b above.
if (auto after = element->get_pseudo_element_node(CSS::Selector::PseudoElement::Type::After)) {
if (after->computed_values().content().alt_text.has_value())
total_accumulated_text.append(after->computed_values().content().alt_text.release_value());
else
total_accumulated_text.append(after->computed_values().content().data);
}
// v. Return the accumulated text if it is not the empty string ("").
if (!total_accumulated_text.is_empty())
return total_accumulated_text.to_string();
// Important: Each node in the subtree is consulted only once. If text has been collected from a descendant, but is referenced by another IDREF in some descendant node, then that second, or subsequent, reference is not followed. This is done to avoid infinite loops.
}

View file

@ -254,6 +254,7 @@ void TreeBuilder::create_pseudo_element_if_needed(DOM::Element& element, CSS::Se
element.set_pseudo_element_node({}, pseudo_element, pseudo_element_node);
insert_node_into_inline_or_block_ancestor(*pseudo_element_node, pseudo_element_display, mode);
pseudo_element_node->mutable_computed_values().set_content(pseudo_element_content);
}
static bool is_ignorable_whitespace(Layout::Node const& node)