mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 23:20:20 +00:00
Merge cf7b9dd2e6
into d6bcd3fb0b
This commit is contained in:
commit
2450a28b0b
7 changed files with 501 additions and 34 deletions
|
@ -1874,6 +1874,54 @@ void Element::invalidate_style_after_attribute_change(FlyString const& attribute
|
||||||
invalidate_style(StyleInvalidationReason::ElementAttributeChange);
|
invalidate_style(StyleInvalidationReason::ElementAttributeChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Element::is_hidden() const
|
||||||
|
{
|
||||||
|
if (layout_node() == nullptr)
|
||||||
|
return true;
|
||||||
|
if ((layout_node() != nullptr) && (layout_node()->computed_values().visibility() == CSS::Visibility::Hidden || layout_node()->computed_values().visibility() == CSS::Visibility::Collapse || layout_node()->computed_values().content_visibility() == CSS::ContentVisibility::Hidden))
|
||||||
|
return true;
|
||||||
|
for (ParentNode const* self_or_ancestor = this; self_or_ancestor; self_or_ancestor = self_or_ancestor->parent_or_shadow_host()) {
|
||||||
|
if (self_or_ancestor->is_element() && static_cast<DOM::Element const*>(self_or_ancestor)->aria_hidden() == "true")
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Element::has_hidden_ancestor() const
|
||||||
|
{
|
||||||
|
for (ParentNode const* self_or_ancestor = this; self_or_ancestor; self_or_ancestor = self_or_ancestor->parent_or_shadow_host()) {
|
||||||
|
if (self_or_ancestor->is_element() && static_cast<DOM::Element const*>(self_or_ancestor)->is_hidden())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Element::is_referenced() const
|
||||||
|
{
|
||||||
|
bool is_referenced = false;
|
||||||
|
if (id().has_value()) {
|
||||||
|
this->root().for_each_in_inclusive_subtree_of_type<HTML::HTMLElement>([&](auto& element) {
|
||||||
|
auto aria_data = MUST(Web::ARIA::AriaData::build_data(element));
|
||||||
|
if (aria_data->aria_labelled_by_or_default().contains_slow(id().value())) {
|
||||||
|
is_referenced = true;
|
||||||
|
return TraversalDecision::Break;
|
||||||
|
}
|
||||||
|
return TraversalDecision::Continue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return is_referenced;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Element::has_referenced_and_hidden_ancestor() const
|
||||||
|
{
|
||||||
|
for (auto const* ancestor = parent_or_shadow_host(); ancestor; ancestor = ancestor->parent_or_shadow_host()) {
|
||||||
|
if (ancestor->is_element())
|
||||||
|
if (auto const* element = static_cast<DOM::Element const*>(ancestor); element->is_referenced() && element->is_hidden())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion
|
// https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion
|
||||||
bool Element::exclude_from_accessibility_tree() const
|
bool Element::exclude_from_accessibility_tree() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -348,6 +348,12 @@ public:
|
||||||
|
|
||||||
virtual bool include_in_accessibility_tree() const override;
|
virtual bool include_in_accessibility_tree() const override;
|
||||||
|
|
||||||
|
bool is_hidden() const;
|
||||||
|
bool has_hidden_ancestor() const;
|
||||||
|
|
||||||
|
bool is_referenced() const;
|
||||||
|
bool has_referenced_and_hidden_ancestor() const;
|
||||||
|
|
||||||
void enqueue_a_custom_element_upgrade_reaction(HTML::CustomElementDefinition& custom_element_definition);
|
void enqueue_a_custom_element_upgrade_reaction(HTML::CustomElementDefinition& custom_element_definition);
|
||||||
void enqueue_a_custom_element_callback_reaction(FlyString const& callback_name, GC::MarkedVector<JS::Value> arguments);
|
void enqueue_a_custom_element_callback_reaction(FlyString const& callback_name, GC::MarkedVector<JS::Value> arguments);
|
||||||
|
|
||||||
|
|
|
@ -2206,23 +2206,25 @@ ErrorOr<String> Node::name_or_description(NameOrDescription target, Document con
|
||||||
if (is_element()) {
|
if (is_element()) {
|
||||||
auto const* element = static_cast<DOM::Element const*>(this);
|
auto const* element = static_cast<DOM::Element const*>(this);
|
||||||
auto role = element->role_or_default();
|
auto role = element->role_or_default();
|
||||||
bool is_referenced = false;
|
|
||||||
auto id = element->id();
|
|
||||||
if (id.has_value()) {
|
|
||||||
this->root().for_each_in_inclusive_subtree_of_type<HTML::HTMLElement>([&](auto& element) {
|
|
||||||
auto aria_data = MUST(Web::ARIA::AriaData::build_data(element));
|
|
||||||
if (aria_data->aria_labelled_by_or_default().contains_slow(id.value())) {
|
|
||||||
is_referenced = true;
|
|
||||||
return TraversalDecision::Break;
|
|
||||||
}
|
|
||||||
return TraversalDecision::Continue;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// 2. Compute the text alternative for the current node:
|
// 2. Compute the text alternative for the current node:
|
||||||
// A. If the current node is hidden and is not directly referenced by aria-labelledby or aria-describedby, nor directly referenced by a native host language text alternative element (e.g. label in HTML) or attribute, return the empty string.
|
|
||||||
// FIXME: Check for references
|
// A. Hidden Not Referenced: If the current node is hidden and is:
|
||||||
if (element->aria_hidden() == "true")
|
// i. Not part of an aria-labelledby or aria-describedby traversal, where the node directly referenced by that
|
||||||
return String {};
|
// relation was hidden.
|
||||||
|
// ii. Nor part of a native host language text alternative element (e.g. label in HTML) or attribute traversal,
|
||||||
|
// where the root of that traversal was hidden.
|
||||||
|
// Return the empty string.
|
||||||
|
// NOTE: Nodes with CSS properties display:none, visibility:hidden, visibility:collapse or
|
||||||
|
// content-visibility:hidden: They are considered hidden, as they match the guidelines "not perceivable" and
|
||||||
|
// "explicitly hidden".
|
||||||
|
//
|
||||||
|
// AD-HOC: We don’t implement this step here — because strictly implementing this would cause us to return early
|
||||||
|
// whenever encountering a node (element, actually) that “is hidden and is not directly referenced by
|
||||||
|
// aria-labelledby or aria-describedby”, without traversing down through that element’s subtree to see if it has
|
||||||
|
// (1) any descendant elements that are directly referenced and/or (2) any un-hidden nodes. So we instead (in
|
||||||
|
// substep G below) traverse upward through ancestor nodes of every text node, and check in that way to do the
|
||||||
|
// equivalent of what this step seems to have been intended to do.
|
||||||
|
|
||||||
// B. Otherwise:
|
// B. Otherwise:
|
||||||
// - if computing a name, and the current node has an aria-labelledby attribute that contains at least one valid IDREF, and the current node is not already part of an aria-labelledby traversal,
|
// - if computing a name, and the current node has an aria-labelledby attribute that contains at least one valid IDREF, and the current node is not already part of an aria-labelledby traversal,
|
||||||
// process its IDREFs in the order they occur:
|
// process its IDREFs in the order they occur:
|
||||||
|
@ -2265,13 +2267,31 @@ ErrorOr<String> Node::name_or_description(NameOrDescription target, Document con
|
||||||
total_accumulated_text.append(result);
|
total_accumulated_text.append(result);
|
||||||
}
|
}
|
||||||
// iii. Return the accumulated text.
|
// iii. Return the accumulated text.
|
||||||
|
// AD-HOC: This substep in the spec doesn’t seem to explicitly require the following check for an aria-label
|
||||||
|
// value; but the “button's hidden referenced name (visibility:hidden) with hidden aria-labelledby traversal
|
||||||
|
// falls back to aria-label” subtest at https://wpt.fyi/results/accname/name/comp_labelledby.html won’t pass
|
||||||
|
// unless we do this check.
|
||||||
|
if (total_accumulated_text.to_string().value().bytes_as_string_view().is_whitespace() && target == NameOrDescription::Name && element->aria_label().has_value() && !element->aria_label()->is_empty() && !element->aria_label()->bytes_as_string_view().is_whitespace())
|
||||||
|
return element->aria_label().value();
|
||||||
return total_accumulated_text.to_string();
|
return total_accumulated_text.to_string();
|
||||||
}
|
}
|
||||||
// C. Embedded Control: Otherwise, if the current node is a control embedded
|
|
||||||
// within the label (e.g. any element directly referenced by aria-labelledby) for
|
// D. AriaLabel: Otherwise, if the current node has an aria-label attribute whose value is not undefined, not
|
||||||
// another widget, where the user can adjust the embedded control's value, then
|
// the empty string, nor, when trimmed of whitespace, is not the empty string:
|
||||||
// return the embedded control as part of the text alternative in the following
|
// AD-HOC: We’ve reordered substeps C and D from https://w3c.github.io/accname/#step2 — because
|
||||||
// manner:
|
// the more-specific per-HTML-element requirements at https://w3c.github.io/html-aam/#accname-computation
|
||||||
|
// necessitate doing so, and the “input with label for association is superceded by aria-label” subtest at
|
||||||
|
// https://wpt.fyi/results/accname/name/comp_label.html won’t pass unless we do this reordering.
|
||||||
|
// Spec PR: https://github.com/w3c/aria/pull/2377
|
||||||
|
if (target == NameOrDescription::Name && element->aria_label().has_value() && !element->aria_label()->is_empty() && !element->aria_label()->bytes_as_string_view().is_whitespace()) {
|
||||||
|
// TODO: - If traversal of the current node is due to recursion and the current node is an embedded control as defined in step 2E, ignore aria-label and skip to rule 2E.
|
||||||
|
// - Otherwise, return the value of aria-label.
|
||||||
|
return element->aria_label().value();
|
||||||
|
}
|
||||||
|
|
||||||
|
// C. Embedded Control: Otherwise, if the current node is a control embedded within the label (e.g. any element
|
||||||
|
// directly referenced by aria-labelledby) for another widget, where the user can adjust the embedded control's
|
||||||
|
// value, then return the embedded control as part of the text alternative in the following manner:
|
||||||
GC::Ptr<DOM::NodeList> labels;
|
GC::Ptr<DOM::NodeList> labels;
|
||||||
if (is<HTML::HTMLElement>(this))
|
if (is<HTML::HTMLElement>(this))
|
||||||
labels = (const_cast<HTML::HTMLElement&>(static_cast<HTML::HTMLElement const&>(*current_node))).labels();
|
labels = (const_cast<HTML::HTMLElement&>(static_cast<HTML::HTMLElement const&>(*current_node))).labels();
|
||||||
|
@ -2341,15 +2361,6 @@ ErrorOr<String> Node::name_or_description(NameOrDescription target, Document con
|
||||||
return builder.to_string();
|
return builder.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
// D. AriaLabel: Otherwise, if the current node has an aria-label attribute whose
|
|
||||||
// value is not undefined, not the empty string, nor, when trimmed of whitespace,
|
|
||||||
// is not the empty string:
|
|
||||||
if (target == NameOrDescription::Name && element->aria_label().has_value() && !element->aria_label()->is_empty() && !element->aria_label()->bytes_as_string_view().is_whitespace()) {
|
|
||||||
// TODO: - If traversal of the current node is due to recursion and the current node is an embedded control as defined in step 2E, ignore aria-label and skip to rule 2E.
|
|
||||||
// - Otherwise, return the value of aria-label.
|
|
||||||
return element->aria_label().value();
|
|
||||||
}
|
|
||||||
|
|
||||||
// E. Host Language Label: Otherwise, if the current node's native markup provides an attribute (e.g. alt) or
|
// E. Host Language Label: Otherwise, if the current node's native markup provides an attribute (e.g. alt) or
|
||||||
// element (e.g. HTML label or SVG title) that defines a text alternative, return that alternative in the form
|
// element (e.g. HTML label or SVG title) that defines a text alternative, return that alternative in the form
|
||||||
// of a flat string as defined by the host language, unless the element is marked as presentational
|
// of a flat string as defined by the host language, unless the element is marked as presentational
|
||||||
|
@ -2361,8 +2372,10 @@ ErrorOr<String> Node::name_or_description(NameOrDescription target, Document con
|
||||||
// with the spec requirements — and if not, then add handling for it here.
|
// with the spec requirements — and if not, then add handling for it here.
|
||||||
}
|
}
|
||||||
|
|
||||||
// F. Otherwise, if the current node's role allows name from content, or if the current node is referenced by aria-labelledby, aria-describedby, or is a native host language text alternative element (e.g. label in HTML), or is a descendant of a native host language text alternative element:
|
// F. Name From Content: Otherwise, if the current node's role allows name from content, or if the current node
|
||||||
if ((role.has_value() && ARIA::allows_name_from_content(role.value())) || is_referenced || is_descendant == IsDescendant::Yes) {
|
// is referenced by aria-labelledby, aria-describedby, or is a native host language text alternative element
|
||||||
|
// (e.g. label in HTML), or is a descendant of a native host language text alternative element:
|
||||||
|
if ((role.has_value() && ARIA::allows_name_from_content(role.value())) || element->is_referenced() || is_descendant == IsDescendant::Yes) {
|
||||||
// i. Set the accumulated text to the empty string.
|
// i. Set the accumulated text to the empty string.
|
||||||
total_accumulated_text.clear();
|
total_accumulated_text.clear();
|
||||||
// ii. Name From Generated Content: Check for CSS generated textual content associated with the current node and include
|
// ii. Name From Generated Content: Check for CSS generated textual content associated with the current node and include
|
||||||
|
@ -2427,13 +2440,24 @@ ErrorOr<String> Node::name_or_description(NameOrDescription target, Document con
|
||||||
}
|
}
|
||||||
|
|
||||||
// G. Text Node: Otherwise, if the current node is a Text Node, return its textual contents.
|
// G. Text Node: Otherwise, if the current node is a Text Node, return its textual contents.
|
||||||
if (is_text()) {
|
// AD-HOC: The spec doesn’t require ascending through the parent node and ancestor nodes of every text node we
|
||||||
|
// reach — the way we’re doing there. But we implement it this way because the spec algorithm as written doesn’t
|
||||||
|
// appear to achieve what it seems to be intended to achieve. Specifically, the spec algorithm as written doesn’t
|
||||||
|
// cause traversal through element subtrees in way that’s necessary to check for descendants that are referenced by
|
||||||
|
// aria-labelledby or aria-describedby and/or un-hidden. See the comment for substep A above.
|
||||||
|
if (is_text() && (parent_element()->is_referenced() || !parent_element()->is_hidden() || !parent_element()->has_hidden_ancestor() || parent_element()->has_referenced_and_hidden_ancestor())) {
|
||||||
if (layout_node() && layout_node()->is_text_node())
|
if (layout_node() && layout_node()->is_text_node())
|
||||||
return verify_cast<Layout::TextNode>(layout_node())->text_for_rendering();
|
return verify_cast<Layout::TextNode>(layout_node())->text_for_rendering();
|
||||||
return text_content().value();
|
return text_content().value();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: H. Otherwise, if the current node is a descendant of an element whose Accessible Name or Accessible Description is being computed, and contains descendants, proceed to 2F.i.
|
// H. Otherwise, if the current node is a descendant of an element whose Accessible Name or Accessible Description
|
||||||
|
// is being computed, and contains descendants, proceed to 2F.i.
|
||||||
|
// AD-HOC: We don’t implement this step here — because is essentially unreachable code in the spec algorithm.
|
||||||
|
// We could never get here without descending through every subtree of an element whose Accessible Name or
|
||||||
|
// Accessible Description is being computed. And in our implementation of substep F about, we’re anyway already
|
||||||
|
// recursively descending through all the child nodes of every element whose Accessible Name or Accessible
|
||||||
|
// Description is being computed, in a way that never leads to this substep H every being hit.
|
||||||
|
|
||||||
// I. Otherwise, if the current node has a Tooltip attribute, return its value.
|
// I. Otherwise, if the current node has a Tooltip attribute, return its value.
|
||||||
// https://www.w3.org/TR/accname-1.2/#dfn-tooltip-attribute
|
// https://www.w3.org/TR/accname-1.2/#dfn-tooltip-attribute
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
Summary
|
||||||
|
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Rerun
|
||||||
|
|
||||||
|
Found 5 tests
|
||||||
|
|
||||||
|
5 Pass
|
||||||
|
Details
|
||||||
|
Result Test Name MessagePass button containing a rendered, unreferenced element that is aria-hidden=true, an unreferenced element with the hidden host language attribute, and an unreferenced element that is unconditionally rendered
|
||||||
|
Pass button labelled by element that is aria-hidden=true
|
||||||
|
Pass button labelled by element with the hidden host language attribute
|
||||||
|
Pass link labelled by elements with assorted visibility and a11y tree exposure
|
||||||
|
Pass heading with name from content, containing element that is visibility:hidden with nested content that is visibility:visible
|
|
@ -0,0 +1,37 @@
|
||||||
|
Summary
|
||||||
|
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Rerun
|
||||||
|
|
||||||
|
Found 27 tests
|
||||||
|
|
||||||
|
27 Pass
|
||||||
|
Details
|
||||||
|
Result Test Name MessagePass button with aria-labelledby using display:none hidden span (with nested span)
|
||||||
|
Pass button with aria-labelledby using display:none hidden span (with nested spans, depth 2)
|
||||||
|
Pass button with aria-labelledby using span without display:none (with nested display:none spans, depth 2)
|
||||||
|
Pass button with aria-labelledby using display:none hidden span (with nested sibling spans)
|
||||||
|
Pass button with aria-labelledby using span without display:none (with nested display:none sibling spans)
|
||||||
|
Pass button with aria-labelledby using span with display:none (with nested display:inline sibling spans)
|
||||||
|
Pass button with aria-labelledby using visibility:hidden span (with nested span)
|
||||||
|
Pass button with aria-labelledby using visibility:hidden span (with nested spans, depth 2)
|
||||||
|
Pass button with aria-labelledby using span without visibility:hidden (with nested visibility:hidden spans, depth 2)
|
||||||
|
Pass button with aria-labelledby using visibility:hidden hidden span (with nested sibling spans)
|
||||||
|
Pass button with aria-labelledby using span without visibility:hidden (with nested visibility:hidden sibling spans)
|
||||||
|
Pass button with aria-labelledby using span with visibility:hidden (with nested visibility:visible sibling spans)
|
||||||
|
Pass button with aria-labelledby using visibility:collapse span (with nested span)
|
||||||
|
Pass button with aria-labelledby using visibility:collapse span (with nested spans, depth 2)
|
||||||
|
Pass button with aria-labelledby using span without visibility:collapse (with nested visibility:visible spans, depth 2)
|
||||||
|
Pass button with aria-labelledby using visibility:collapse span (with nested sibling spans)
|
||||||
|
Pass button with aria-labelledby using span without visibility:collapse (with nested visibility:collapse sibling spans)
|
||||||
|
Pass button with aria-labelledby using span with visibility:collapse (with nested visible sibling spans)
|
||||||
|
Pass button with aria-labelledby using aria-hidden span (with nested span)
|
||||||
|
Pass button with aria-labelledby using aria-hidden span (with nested spans, depth 2)
|
||||||
|
Pass button with aria-labelledby using span without aria-hidden (with nested aria-hidden spans, depth 2)
|
||||||
|
Pass button with aria-labelledby using aria-hidden hidden span (with nested sibling spans)
|
||||||
|
Pass button with aria-labelledby using HTML5 hidden span (with nested span)
|
||||||
|
Pass button with aria-labelledby using HTML5 hidden span (with nested spans, depth 2)
|
||||||
|
Pass button with aria-labelledby using span without HTML5 hidden (with nested HTML5 hidden spans, depth 2)
|
||||||
|
Pass button with aria-labelledby using HTML5 hidden span (with nested hidden sibling spans)
|
||||||
|
Pass button with aria-labelledby using span without HTML5 hidden (with nested hidden sibling spans)
|
|
@ -0,0 +1,92 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Name Comp: Hidden Not Referenced</title>
|
||||||
|
<script src="../../resources/testharness.js"></script>
|
||||||
|
<script src="../../resources/testharnessreport.js"></script>
|
||||||
|
<script src="../../resources/testdriver.js"></script>
|
||||||
|
<script src="../../resources/testdriver-vendor.js"></script>
|
||||||
|
<script src="../../resources/testdriver-actions.js"></script>
|
||||||
|
<script src="../../wai-aria/scripts/aria-utils.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<p>Tests the <a href="https://w3c.github.io/accname/#comp_hidden_not_referenced">#comp_hidden_not_referenced</a> portions of the AccName <em>Name Computation</em> algorithm.</p>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="ex"
|
||||||
|
data-expectedlabel="visible to all users"
|
||||||
|
data-testname="button containing a rendered, unreferenced element that is aria-hidden=true, an unreferenced element with the hidden host language attribute, and an unreferenced element that is unconditionally rendered"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">hidden,</span>
|
||||||
|
<span hidden>hidden from all users,</span>
|
||||||
|
<span>visible to all users</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="ex"
|
||||||
|
data-expectedlabel="hidden but referenced,"
|
||||||
|
data-testname="button labelled by element that is aria-hidden=true"
|
||||||
|
aria-labelledby="button-label-2"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true" id="button-label-2">hidden but referenced,</span>
|
||||||
|
<span hidden>hidden from all users,</span>
|
||||||
|
<span>visible to all users</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="ex"
|
||||||
|
data-expectedlabel="hidden from all users but referenced,"
|
||||||
|
data-testname="button labelled by element with the hidden host language attribute"
|
||||||
|
aria-labelledby="button-label-3"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">hidden,</span>
|
||||||
|
<span hidden id="button-label-3">hidden from all users but referenced,</span>
|
||||||
|
<span>visible to all users</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<a
|
||||||
|
class="ex"
|
||||||
|
data-testname="link labelled by elements with assorted visibility and a11y tree exposure"
|
||||||
|
data-expectedlabel="visible to all users, hidden but referenced, hidden from all users but referenced"
|
||||||
|
href="#"
|
||||||
|
aria-labelledby="link-label-1a link-label-1b link-label-1c"
|
||||||
|
>
|
||||||
|
<span id="link-label-1a">
|
||||||
|
<span>visible to all users,</span>
|
||||||
|
<span aria-hidden="true">hidden,</span>
|
||||||
|
</span>
|
||||||
|
<span aria-hidden="true" id="link-label-1b">hidden but referenced,</span>
|
||||||
|
<span hidden id="link-label-1c">hidden from all users but referenced</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<h2
|
||||||
|
class="ex"
|
||||||
|
data-testname="heading with name from content, containing element that is visibility:hidden with nested content that is visibility:visible"
|
||||||
|
data-expectedlabel="visible to all users, un-hidden for all users"
|
||||||
|
>
|
||||||
|
visible to all users,
|
||||||
|
<span style="visibility: hidden;">
|
||||||
|
hidden from all users,
|
||||||
|
<span style="visibility: visible;">un-hidden for all users</span>
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<!-- TODO: Test cases once https://github.com/w3c/aria/issues/1256 resolved: -->
|
||||||
|
<!-- - button labelled by an element that is aria-hidden=true which contains a nested child that is aria-hidden=false -->
|
||||||
|
<!-- - button labelled by an element that is aria-hidden=false which belongs to a parent that is aria-hidden=true -->
|
||||||
|
<!-- - heading with name from content, containing rendered content that is aria-hidden=true with nested, rendered content that is aria-hidden=false -->
|
||||||
|
<!-- - heading with name from content, containing element with the hidden host language attribute with nested content that is aria-hidden=false -->
|
||||||
|
|
||||||
|
<!-- TODO: New test case?
|
||||||
|
<!-- What is the expectation for a details element when it’s given an -->
|
||||||
|
<!-- explicit role that allows name from contents (e.g., `comment`) -->
|
||||||
|
<!-- but is also not in the open state, and therefore has contents -->
|
||||||
|
<!-- that are both not rendered and excluded from the a11y tree. -->
|
||||||
|
|
||||||
|
<script>
|
||||||
|
AriaUtils.verifyLabelsBySelector(".ex");
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,245 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Name Comp: Labelledby & Hidden Nodes</title>
|
||||||
|
<script src="../../resources/testharness.js"></script>
|
||||||
|
<script src="../../resources/testharnessreport.js"></script>
|
||||||
|
<script src="../../resources/testdriver.js"></script>
|
||||||
|
<script src="../../resources/testdriver-vendor.js"></script>
|
||||||
|
<script src="../../resources/testdriver-actions.js"></script>
|
||||||
|
<script src="../../wai-aria/scripts/aria-utils.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<p>Tests hidden node name computation as part of the <a href="https://w3c.github.io/accname/#comp_labelledby">#comp_labelledby</a> portion of the AccName <em>Name Computation</em> algorithm.</p>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
These tests verify browser conformance with the following note as part of accName computation Step 2B:
|
||||||
|
|
||||||
|
"The result of LabelledBy Recursion in combination with Hidden Not Referenced means
|
||||||
|
that user agents MUST include all nodes in the subtree as part of
|
||||||
|
the accessible name or accessible description, when the node referenced
|
||||||
|
by aria-labelledby or aria-describedby is hidden."
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<h2>Testing with <code>display:none</code></h2>
|
||||||
|
|
||||||
|
<button aria-labelledby="a11" data-expectedlabel="foo bar" data-testname="button with aria-labelledby using display:none hidden span (with nested span)" class="ex">x</button>
|
||||||
|
<span id="a11" style="display: none;">
|
||||||
|
foo
|
||||||
|
<span id="a12">bar</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="a21" data-expectedlabel="foo bar baz" data-testname="button with aria-labelledby using display:none hidden span (with nested spans, depth 2)" class="ex">x</button>
|
||||||
|
<span id="a21" style="display: none;">
|
||||||
|
foo
|
||||||
|
<span id="a22">
|
||||||
|
bar
|
||||||
|
<span id="a23">baz</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="a31" data-expectedlabel="foo" data-testname="button with aria-labelledby using span without display:none (with nested display:none spans, depth 2)" class="ex">x</button>
|
||||||
|
<span id="a31">
|
||||||
|
foo
|
||||||
|
<span id="a32" style="display: none;">
|
||||||
|
bar
|
||||||
|
<span id="a33">baz</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="a41" data-expectedlabel="foo bar baz" data-testname="button with aria-labelledby using display:none hidden span (with nested sibling spans)" class="ex">x</button>
|
||||||
|
<span id="a41" style="display: none;">
|
||||||
|
foo
|
||||||
|
<span id="a42">bar</span>
|
||||||
|
<span id="a43">baz</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="a51" data-expectedlabel="foo" data-testname="button with aria-labelledby using span without display:none (with nested display:none sibling spans)" class="ex">x</button>
|
||||||
|
<span id="a51">
|
||||||
|
foo
|
||||||
|
<span id="a52" style="display: none;">bar</span>
|
||||||
|
<span id="a53" style="display: none;">baz</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="a61" data-expectedlabel="foo bar baz" data-testname="button with aria-labelledby using span with display:none (with nested display:inline sibling spans)" class="ex">x</button>
|
||||||
|
<span id="a61" style="display: none;">
|
||||||
|
foo
|
||||||
|
<span id="a62" style="display: inline;">bar</span>
|
||||||
|
<span id="a63" style="display: inline;">baz</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<h2>Testing with <code>visibility:hidden</code></h2>
|
||||||
|
|
||||||
|
<button aria-labelledby="b11" data-expectedlabel="foo bar" data-testname="button with aria-labelledby using visibility:hidden span (with nested span)" class="ex">x</button>
|
||||||
|
<span id="b11" style="visibility: hidden;">
|
||||||
|
foo
|
||||||
|
<span id="b12">bar</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="b21" data-expectedlabel="foo bar baz" data-testname="button with aria-labelledby using visibility:hidden span (with nested spans, depth 2)" class="ex">x</button>
|
||||||
|
<span id="b21" style="visibility: hidden;">
|
||||||
|
foo
|
||||||
|
<span id="b22">
|
||||||
|
bar
|
||||||
|
<span id="b23">baz</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="b31" data-expectedlabel="foo" data-testname="button with aria-labelledby using span without visibility:hidden (with nested visibility:hidden spans, depth 2)" class="ex">x</button>
|
||||||
|
<span id="b31">
|
||||||
|
foo
|
||||||
|
<span id="b32" style="visibility: hidden;">
|
||||||
|
bar
|
||||||
|
<span id="b33">baz</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="b41" data-expectedlabel="foo bar baz" data-testname="button with aria-labelledby using visibility:hidden hidden span (with nested sibling spans)" class="ex">x</button>
|
||||||
|
<span id="b41" style="visibility: hidden;">
|
||||||
|
foo
|
||||||
|
<span id="b42">bar</span>
|
||||||
|
<span id="b43">baz</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="b51" data-expectedlabel="foo" data-testname="button with aria-labelledby using span without visibility:hidden (with nested visibility:hidden sibling spans)" class="ex">x</button>
|
||||||
|
<span id="b51">
|
||||||
|
foo
|
||||||
|
<span id="b52" style="visibility: hidden;">bar</span>
|
||||||
|
<span id="b53" style="visibility: hidden;">baz</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="b61" data-expectedlabel="foo bar baz" data-testname="button with aria-labelledby using span with visibility:hidden (with nested visibility:visible sibling spans)" class="ex">x</button>
|
||||||
|
<span id="b61" style="visibility: hidden;">
|
||||||
|
foo
|
||||||
|
<span id="b62" style="visibility: visible;">bar</span>
|
||||||
|
<span id="b63" style="visibility: visible;">baz</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<h2>Testing with <code>visibility:collapse</code></h2>
|
||||||
|
|
||||||
|
<button aria-labelledby="c11" data-expectedlabel="foo bar" data-testname="button with aria-labelledby using visibility:collapse span (with nested span)" class="ex">x</button>
|
||||||
|
<span id="c11" style="visibility: collapse;">
|
||||||
|
foo
|
||||||
|
<span id="c12">bar</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="c21" data-expectedlabel="foo bar baz" data-testname="button with aria-labelledby using visibility:collapse span (with nested spans, depth 2)" class="ex">x</button>
|
||||||
|
<span id="c21" style="visibility: collapse;">
|
||||||
|
foo
|
||||||
|
<span id="c22">
|
||||||
|
bar
|
||||||
|
<span id="c23">baz</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="c31" data-expectedlabel="foo bar baz" data-testname="button with aria-labelledby using span without visibility:collapse (with nested visibility:visible spans, depth 2)" class="ex">x</button>
|
||||||
|
<span id="c31">
|
||||||
|
foo
|
||||||
|
<span id="c32" style="visibility: visible;">
|
||||||
|
bar
|
||||||
|
<span id="c33" style="visibility: visible;">baz</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="c41" data-expectedlabel="foo bar baz" data-testname="button with aria-labelledby using visibility:collapse span (with nested sibling spans)" class="ex">x</button>
|
||||||
|
<span id="c41" style="visibility: collapse;">
|
||||||
|
foo
|
||||||
|
<span id="c42">bar</span>
|
||||||
|
<span id="c43">baz</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="c51" data-expectedlabel="foo" data-testname="button with aria-labelledby using span without visibility:collapse (with nested visibility:collapse sibling spans)" class="ex">x</button>
|
||||||
|
<span id="c51">
|
||||||
|
foo
|
||||||
|
<span id="c52" style="visibility: collapse;">bar</span>
|
||||||
|
<span id="c53" style="visibility: collapse;">baz</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="c61" data-expectedlabel="foo bar baz" data-testname="button with aria-labelledby using span with visibility:collapse (with nested visible sibling spans)" class="ex">x</button>
|
||||||
|
<span id="c61" style="visibility: collapse;">
|
||||||
|
foo
|
||||||
|
<span id="c62" style="visibility: visible;">bar</span>
|
||||||
|
<span id="c63" style="visibility: visible;">baz</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<h2>Testing with <code>aria-hidden</code></h2>
|
||||||
|
|
||||||
|
<button aria-labelledby="d11" data-expectedlabel="foo bar" data-testname="button with aria-labelledby using aria-hidden span (with nested span)" class="ex">x</button>
|
||||||
|
<span id="d11" aria-hidden="true">
|
||||||
|
foo
|
||||||
|
<span id="d12">bar</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="d21" data-expectedlabel="foo bar baz" data-testname="button with aria-labelledby using aria-hidden span (with nested spans, depth 2)" class="ex">x</button>
|
||||||
|
<span id="d21" aria-hidden="true">
|
||||||
|
foo
|
||||||
|
<span id="d22">
|
||||||
|
bar
|
||||||
|
<span id="d23">baz</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="d31" data-expectedlabel="foo" data-testname="button with aria-labelledby using span without aria-hidden (with nested aria-hidden spans, depth 2)" class="ex">x</button>
|
||||||
|
<span id="d31">
|
||||||
|
foo
|
||||||
|
<span id="d32" aria-hidden="true">
|
||||||
|
bar
|
||||||
|
<span id="d33">baz</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="d41" data-expectedlabel="foo bar baz" data-testname="button with aria-labelledby using aria-hidden hidden span (with nested sibling spans)" class="ex">x</button>
|
||||||
|
<span id="d41" aria-hidden="true">
|
||||||
|
foo
|
||||||
|
<span id="d42">bar</span>
|
||||||
|
<span id="d43">baz</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<h2>Testing with <code>hidden</code> attribute</h2>
|
||||||
|
|
||||||
|
<button aria-labelledby="e11" data-expectedlabel="foo bar" data-testname="button with aria-labelledby using HTML5 hidden span (with nested span)" class="ex">x</button>
|
||||||
|
<span id="e11" hidden>
|
||||||
|
foo
|
||||||
|
<span id="e12">bar</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="e21" data-expectedlabel="foo bar baz" data-testname="button with aria-labelledby using HTML5 hidden span (with nested spans, depth 2)" class="ex">x</button>
|
||||||
|
<span id="e21" hidden>
|
||||||
|
foo
|
||||||
|
<span id="e22">
|
||||||
|
bar
|
||||||
|
<span id="e23">baz</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="e31" data-expectedlabel="foo" data-testname="button with aria-labelledby using span without HTML5 hidden (with nested HTML5 hidden spans, depth 2)" class="ex">x</button>
|
||||||
|
<span id="e31">
|
||||||
|
foo
|
||||||
|
<span id="e32" hidden>
|
||||||
|
bar
|
||||||
|
<span id="e33">baz</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="e41" data-expectedlabel="foo bar baz" data-testname="button with aria-labelledby using HTML5 hidden span (with nested hidden sibling spans)" class="ex">x</button>
|
||||||
|
<span id="e41" hidden>
|
||||||
|
foo
|
||||||
|
<span id="e42">bar</span>
|
||||||
|
<span id="e43">baz</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<button aria-labelledby="e51" data-expectedlabel="foo" data-testname="button with aria-labelledby using span without HTML5 hidden (with nested hidden sibling spans)" class="ex">x</button>
|
||||||
|
<span id="e51">
|
||||||
|
foo
|
||||||
|
<span id="e52" hidden>bar</span>
|
||||||
|
<span id="e53" hidden>baz</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
AriaUtils.verifyLabelsBySelector(".ex");
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue