mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 19:45:12 +00:00
LibWeb: Ignore “orphaned” ARIA roles
This change causes explicitly-specified role attributes to be ignored in the case where the specified role is “orphaned” — that is, when its element lacks a required ancestor with an appropriate role.
This commit is contained in:
parent
56c7857053
commit
2cb7baa581
Notes:
github-actions[bot]
2025-01-09 14:09:47 +00:00
Author: https://github.com/sideshowbarker Commit: https://github.com/LadybirdBrowser/ladybird/commit/2cb7baa5813 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2972 Reviewed-by: https://github.com/AtkinsSJ ✅
13 changed files with 334 additions and 1 deletions
|
@ -25,7 +25,6 @@ Optional<Role> ARIAMixin::role_from_role_attribute_value() const
|
|||
|
||||
// 3. Compare the substrings to all the names of the non-abstract WAI-ARIA roles. Case-sensitivity of the comparison inherits from the case-sensitivity of the host language.
|
||||
for (auto const& role_name : role_list) {
|
||||
// 4. Use the first such substring in textual order that matches the name of a non-abstract WAI-ARIA role.
|
||||
auto role = role_from_string(role_name);
|
||||
if (!role.has_value())
|
||||
continue;
|
||||
|
@ -43,6 +42,95 @@ Optional<Role> ARIAMixin::role_from_role_attribute_value() const
|
|||
// "synonym presentation role == computedrole none" test that expects "none", not "presentation".
|
||||
if (role == Role::presentation)
|
||||
return Role::none;
|
||||
// https://w3c.github.io/core-aam/#roleMappingComputedRole
|
||||
// When an element has a role but is not contained in the required context (for example, an orphaned listitem
|
||||
// without the required accessible parent of role list), User Agents MUST ignore the role token, and return the
|
||||
// computedrole as if the ignored role token had not been included.
|
||||
if (role == ARIA::Role::columnheader) {
|
||||
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
|
||||
if (ancestor->role_or_default() == ARIA::Role::row)
|
||||
return ARIA::Role::columnheader;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (role == ARIA::Role::gridcell) {
|
||||
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
|
||||
if (ancestor->role_or_default() == ARIA::Role::row)
|
||||
return ARIA::Role::gridcell;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (role == ARIA::Role::listitem) {
|
||||
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
|
||||
if (first_is_one_of(ancestor->role_or_default(), ARIA::Role::directory, ARIA::Role::list))
|
||||
return ARIA::Role::listitem;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (role == ARIA::Role::menuitem) {
|
||||
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
|
||||
if (first_is_one_of(ancestor->role_or_default(), ARIA::Role::menu, ARIA::Role::menubar))
|
||||
return ARIA::Role::menuitem;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (role == ARIA::Role::menuitemcheckbox) {
|
||||
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
|
||||
if (first_is_one_of(ancestor->role_or_default(), ARIA::Role::menu, ARIA::Role::menubar))
|
||||
return ARIA::Role::menuitemcheckbox;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (role == ARIA::Role::menuitemradio) {
|
||||
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
|
||||
if (first_is_one_of(ancestor->role_or_default(), ARIA::Role::menu, ARIA::Role::menubar))
|
||||
return ARIA::Role::menuitemradio;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (role == ARIA::Role::option) {
|
||||
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
|
||||
if (ancestor->role_or_default() == ARIA::Role::listbox)
|
||||
return ARIA::Role::option;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (role == ARIA::Role::row) {
|
||||
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
|
||||
if (first_is_one_of(ancestor->role_or_default(), ARIA::Role::table, ARIA::Role::grid, ARIA::Role::treegrid))
|
||||
return ARIA::Role::row;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (role == ARIA::Role::rowgroup) {
|
||||
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
|
||||
if (first_is_one_of(ancestor->role_or_default(), ARIA::Role::table, ARIA::Role::grid, ARIA::Role::treegrid))
|
||||
return ARIA::Role::rowgroup;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (role == ARIA::Role::rowheader) {
|
||||
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
|
||||
if (ancestor->role_or_default() == ARIA::Role::row)
|
||||
return ARIA::Role::rowheader;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (role == ARIA::Role::tab) {
|
||||
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
|
||||
if (ancestor->role_or_default() == ARIA::Role::tablist)
|
||||
return ARIA::Role::tab;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (role == ARIA::Role::treeitem) {
|
||||
for (auto const* ancestor = to_element()->parent_element(); ancestor; ancestor = ancestor->parent_element()) {
|
||||
if (ancestor->role_or_default() == ARIA::Role::tree)
|
||||
return ARIA::Role::treeitem;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 4. Use the first such substring in textual order that matches the name of a non-abstract WAI-ARIA role.
|
||||
if (!is_abstract_role(*role))
|
||||
return *role;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 6 tests
|
||||
|
||||
6 Pass
|
||||
Pass orphaned button with gridcell role outside the context of row
|
||||
Pass orphaned row outside the context of table
|
||||
Pass orphaned rowgroup outside the context of row
|
||||
Pass orphaned div with gridcell role outside the context of row
|
||||
Pass orphaned rowheader outside the context of row
|
||||
Pass orphaned columnheader outside the context of row
|
|
@ -0,0 +1,7 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 2 tests
|
||||
|
||||
2 Pass
|
||||
Pass orphan p with listitem role
|
||||
Pass orphan div with listitem role
|
|
@ -0,0 +1,6 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 1 tests
|
||||
|
||||
1 Pass
|
||||
Pass orphaned option outside the context of listbox
|
|
@ -0,0 +1,14 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 9 tests
|
||||
|
||||
9 Pass
|
||||
Pass orphaned menuitem outside the context of menu/menubar
|
||||
Pass orphaned menuitemradio outside the context of menu/menubar
|
||||
Pass orphaned menuitemcheckbox outside the context of menu/menubar
|
||||
Pass orphan button with menuitem role
|
||||
Pass orphan button with menuitemcheckbox role
|
||||
Pass orphan button with menuitemradio role
|
||||
Pass orphan div with menuitem role
|
||||
Pass orphan div with menuitemcheckbox role
|
||||
Pass orphan div with menuitemradio role
|
|
@ -0,0 +1,7 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 2 tests
|
||||
|
||||
2 Pass
|
||||
Pass orphan button with tab role
|
||||
Pass orphan span with tab role
|
|
@ -0,0 +1,7 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 2 tests
|
||||
|
||||
2 Pass
|
||||
Pass orphaned treeitem outside the context of tree
|
||||
Pass orphaned button with treeitem role outside tree context
|
|
@ -0,0 +1,33 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tentative: Grid Role Verification Tests</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>
|
||||
|
||||
<!--
|
||||
CORE-AAM requires that, for elements with roles not contained in the
|
||||
required context, user agents must ignore the role token and return the
|
||||
computed role as if the ignored role token had not been included.
|
||||
See https://w3c.github.io/core-aam/#roleMappingComputedRole
|
||||
-->
|
||||
<span role="row" data-testname="orphaned row outside the context of table" class="ex-generic">x</span>
|
||||
<span role="rowgroup" data-testname="orphaned rowgroup outside the context of row" class="ex-generic">x</span>
|
||||
<div role="gridcell" data-testname="orphaned div with gridcell role outside the context of row" class="ex-generic">x</div>
|
||||
<button role="gridcell" data-testname="orphaned button with gridcell role outside the context of row" data-expectedrole="button" class="ex">x</button>
|
||||
<div role="rowheader" data-testname="orphaned rowheader outside the context of row" class="ex-generic">x</div>
|
||||
<div role="columnheader" data-testname="orphaned columnheader outside the context of row" class="ex-generic">x</div>
|
||||
|
||||
<script>
|
||||
AriaUtils.verifyRolesBySelector(".ex");
|
||||
AriaUtils.verifyGenericRolesBySelector(".ex-generic");
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,29 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tentative: List-related Role Verification Tests</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>
|
||||
|
||||
<!--
|
||||
CORE-AAM requires that, for elements with roles not contained in the
|
||||
required context, user agents must ignore the role token and return the
|
||||
computed role as if the ignored role token had not been included.
|
||||
See https://w3c.github.io/core-aam/#roleMappingComputedRole
|
||||
-->
|
||||
<div role="listitem" data-testname="orphan div with listitem role" class="ex-generic">x</div>
|
||||
<p role="listitem" data-testname="orphan p with listitem role" data-expectedrole="paragraph" class="ex">x</p>
|
||||
|
||||
<script>
|
||||
AriaUtils.verifyRolesBySelector(".ex");
|
||||
AriaUtils.verifyGenericRolesBySelector(".ex-generic");
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,29 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tentative: Listbox-related Role Verification Tests</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>
|
||||
|
||||
<!--
|
||||
CORE-AAM requires that, for elements with roles not contained in the
|
||||
required context, user agents must ignore the role token and return the
|
||||
computed role as if the ignored role token had not been included.
|
||||
See https://w3c.github.io/core-aam/#roleMappingComputedRole
|
||||
-->
|
||||
<nav role="option" data-testname="orphaned option outside the context of listbox" data-expectedrole="navigation"
|
||||
class="ex">x
|
||||
</nav>
|
||||
|
||||
<script>
|
||||
AriaUtils.verifyRolesBySelector(".ex");
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,45 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tentative: Menu-related Role Verification Tests</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>
|
||||
|
||||
<!--
|
||||
CORE-AAM requires that, for elements with roles not contained in the
|
||||
required context, user agents must ignore the role token and return the
|
||||
computed role as if the ignored role token had not been included.
|
||||
See https://w3c.github.io/core-aam/#roleMappingComputedRole
|
||||
-->
|
||||
<nav role="menuitem" data-testname="orphaned menuitem outside the context of menu/menubar" data-expectedrole="navigation"
|
||||
class="ex">x
|
||||
</nav>
|
||||
<nav role="menuitemradio" data-testname="orphaned menuitemradio outside the context of menu/menubar" data-expectedrole="navigation"
|
||||
class="ex">x
|
||||
</nav>
|
||||
<nav role="menuitemcheckbox" data-testname="orphaned menuitemcheckbox outside the context of menu/menubar" data-expectedrole="navigation"
|
||||
class="ex">x
|
||||
</nav>
|
||||
|
||||
<button role="menuitem" data-testname="orphan button with menuitem role" data-expectedrole="button" class="ex">x</button>
|
||||
<div role="menuitem" data-testname="orphan div with menuitem role" class="ex-generic">x</div>
|
||||
|
||||
<button role="menuitemcheckbox" data-testname="orphan button with menuitemcheckbox role" data-expectedrole="button" class="ex">x</button>
|
||||
<div role="menuitemcheckbox" data-testname="orphan div with menuitemcheckbox role" class="ex-generic">x</div>
|
||||
|
||||
<button role="menuitemradio" data-testname="orphan button with menuitemradio role" data-expectedrole="button" class="ex">x</button>
|
||||
<div role="menuitemradio" data-testname="orphan div with menuitemradio role" class="ex-generic">x</div>
|
||||
|
||||
<script>
|
||||
AriaUtils.verifyRolesBySelector(".ex");
|
||||
AriaUtils.verifyGenericRolesBySelector(".ex-generic");
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,29 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tentative: Tab-related Role Verification Tests</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>
|
||||
|
||||
<!--
|
||||
CORE-AAM requires that, for elements with roles not contained in the
|
||||
required context, user agents must ignore the role token and return the
|
||||
computed role as if the ignored role token had not been included.
|
||||
See https://w3c.github.io/core-aam/#roleMappingComputedRole
|
||||
-->
|
||||
<button role="tab" data-testname="orphan button with tab role" data-expectedrole="button" class="ex">x</button>
|
||||
<span role="tab" data-testname="orphan span with tab role" class="ex-generic">x</span>
|
||||
|
||||
<script>
|
||||
AriaUtils.verifyRolesBySelector(".ex");
|
||||
AriaUtils.verifyGenericRolesBySelector(".ex-generic");
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,28 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tentative: Tree related Role Verification Tests</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>
|
||||
|
||||
<!--
|
||||
CORE-AAM requires that, for elements with roles not contained in the
|
||||
required context, user agents must ignore the role token and return the
|
||||
computed role as if the ignored role token had not been included.
|
||||
See https://w3c.github.io/core-aam/#roleMappingComputedRole
|
||||
-->
|
||||
<nav role="treeitem" data-testname="orphaned treeitem outside the context of tree" data-expectedrole="navigation" class="ex">x</nav>
|
||||
<button role="treeitem" data-testname="orphaned button with treeitem role outside tree context" data-expectedrole="button" class="ex">x</button>
|
||||
|
||||
<script>
|
||||
AriaUtils.verifyRolesBySelector(".ex");
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Reference in a new issue