diff --git a/Libraries/LibWeb/DOM/Node.cpp b/Libraries/LibWeb/DOM/Node.cpp index a455f7f897..106aed60ed 100644 --- a/Libraries/LibWeb/DOM/Node.cpp +++ b/Libraries/LibWeb/DOM/Node.cpp @@ -2228,7 +2228,17 @@ ErrorOr Node::name_or_description(NameOrDescription target, Document con if (is_element()) { auto const* element = static_cast(this); - auto role = element->role_or_default(); + auto role = element->role_from_role_attribute_value(); + // Per https://w3c.github.io/html-aam/#el-aside and https://w3c.github.io/html-aam/#el-section, computing a + // default role for an aside element or section element requires first computing its accessible name — that is, + // calling into this name_or_description code. But if we then try to determine a default role for the aside + // element or section element here, that’d then end up calling right back into this name_or_description code — + // which would cause the calls to loop infinitely. So to avoid that, we only compute a default role here if this + // isn’t an aside element or section element. + // https://github.com/w3c/aria/issues/2391 + if (!role.has_value() && element->local_name() != HTML::TagNames::aside && element->local_name() != HTML::TagNames::section) + role = element->default_role(); + // 2. Compute the text alternative for the current node: // A. Hidden Not Referenced: If the current node is hidden and is: diff --git a/Libraries/LibWeb/HTML/HTMLElement.cpp b/Libraries/LibWeb/HTML/HTMLElement.cpp index c7d1f0fdf3..1aabe35873 100644 --- a/Libraries/LibWeb/HTML/HTMLElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLElement.cpp @@ -734,8 +734,16 @@ Optional HTMLElement::default_role() const if (local_name() == TagNames::article) return ARIA::Role::article; // https://www.w3.org/TR/html-aria/#el-aside - if (local_name() == TagNames::aside) + if (local_name() == TagNames::aside) { + // https://w3c.github.io/html-aam/#el-aside + for (auto const* ancestor = parent_element(); ancestor; ancestor = ancestor->parent_element()) { + if (first_is_one_of(ancestor->local_name(), TagNames::article, TagNames::aside, TagNames::nav, TagNames::section) + && accessible_name(document()).value().is_empty()) + return ARIA::Role::generic; + } + // https://w3c.github.io/html-aam/#el-aside-ancestorbodymain return ARIA::Role::complementary; + } // https://www.w3.org/TR/html-aria/#el-b if (local_name() == TagNames::b) return ARIA::Role::generic; @@ -758,16 +766,22 @@ Optional HTMLElement::default_role() const if (local_name() == TagNames::figure) return ARIA::Role::figure; // https://www.w3.org/TR/html-aria/#el-footer - if (local_name() == TagNames::footer) { - // TODO: If not a descendant of an article, aside, main, nav or section element, or an element with role=article, complementary, main, navigation or region then role=contentinfo - // Otherwise, role=generic - return ARIA::Role::generic; - } // https://www.w3.org/TR/html-aria/#el-header - if (local_name() == TagNames::header) { - // TODO: If not a descendant of an article, aside, main, nav or section element, or an element with role=article, complementary, main, navigation or region then role=banner - // Otherwise, role=generic - return ARIA::Role::generic; + if (local_name() == TagNames::footer || local_name() == TagNames::header) { + // If not a descendant of an article, aside, main, nav or section element, or an element with role=article, + // complementary, main, navigation or region then (footer) role=contentinfo (header) role=banner. Otherwise, + // role=generic. + for (auto const* ancestor = parent_element(); ancestor; ancestor = ancestor->parent_element()) { + if (first_is_one_of(ancestor->local_name(), TagNames::article, TagNames::aside, TagNames::main, TagNames::nav, TagNames::section)) + return ARIA::Role::generic; + if (first_is_one_of(ancestor->role_or_default(), ARIA::Role::article, ARIA::Role::complementary, ARIA::Role::main, ARIA::Role::navigation, ARIA::Role::region)) + return ARIA::Role::generic; + } + // then (footer) role=contentinfo. + if (local_name() == TagNames::footer) + return ARIA::Role::contentinfo; + // (header) role=banner + return ARIA::Role::banner; } // https://www.w3.org/TR/html-aria/#el-hgroup if (local_name() == TagNames::hgroup) @@ -792,9 +806,11 @@ Optional HTMLElement::default_role() const return ARIA::Role::search; // https://www.w3.org/TR/html-aria/#el-section if (local_name() == TagNames::section) { - // TODO: role=region if the section element has an accessible name - // Otherwise, no corresponding role - return ARIA::Role::region; + // role=region if the section element has an accessible name + if (!accessible_name(document()).value().is_empty()) + return ARIA::Role::region; + // Otherwise, role=generic + return ARIA::Role::generic; } // https://www.w3.org/TR/html-aria/#el-small if (local_name() == TagNames::small) diff --git a/Tests/LibWeb/Text/expected/wpt-import/html-aam/roles-contextual.txt b/Tests/LibWeb/Text/expected/wpt-import/html-aam/roles-contextual.txt new file mode 100644 index 0000000000..d272dff749 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html-aam/roles-contextual.txt @@ -0,0 +1,24 @@ +Harness status: OK + +Found 19 tests + +19 Pass +Pass el-a +Pass el-aside +Pass el-aside-in-main +Pass el-aside-in-article-in-main-with-name +Pass el-aside-in-article-with-name +Pass el-aside-in-aside-with-name +Pass el-aside-in-nav-with-name +Pass el-aside-in-nav-with-role +Pass el-aside-in-section-with-name +Pass el-footer-ancestorbody +Pass el-header-ancestorbody +Pass el-section +Pass el-a-no-href +Pass el-aside-in-article-in-main +Pass el-aside-in-article +Pass el-aside-in-aside +Pass el-aside-in-nav +Pass el-aside-in-section +Pass el-section-no-name \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/html-aam/roles-contextual.html b/Tests/LibWeb/Text/input/wpt-import/html-aam/roles-contextual.html new file mode 100644 index 0000000000..5fd897c1e6 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html-aam/roles-contextual.html @@ -0,0 +1,76 @@ + + + + HTML-AAM Contextual-Specific Role Verification Tests + + + + + + + + + + +

Tests contextual computedrole mappings defined in HTML-AAM, where the returned computed role is expected to change based on the context. Most test names correspond to a unique ID defined in the spec.

+ +

These should remain in alphabetical order.

+ + + +x +x + + + +
+ +
+ + +
+
+
+ + +
+ + + +
+ + +
+ + + +
x
+ + + + +
x
+ + + +
x
+
x
+ + + + + + \ No newline at end of file