Bug 1778795: role="form" with empty accessible name returns native role, r=Jamie

The ARIA spec requires that, for certain landmark roles with no author-specified
names, user agents must treat such elements as if no role had been provided.
This revision accomplishes that task by carving out an exception in
ARIATransformRole and ComputeARIARole for roles::FORM, similar to the existing
carveout for roles::REGION. This revision also implements a NameIsEmpty function
which is helpful for checking name emptiness (since we do this in a few places)
without leaving a "name" variable on the stack. Finally, this revision updates a
expected WPT failure (by removing it).

Differential Revision: https://phabricator.services.mozilla.com/D202778
This commit is contained in:
Nathan LaPre 2024-02-29 00:33:10 +00:00
parent d91da7450d
commit dbe5ffd60b
7 changed files with 40 additions and 24 deletions

View file

@ -539,17 +539,13 @@ nsStaticAtom* Accessible::LandmarkRole() const {
}
if (tagName == nsGkAtoms::section) {
nsAutoString name;
Name(name);
if (!name.IsEmpty()) {
if (!NameIsEmpty()) {
return nsGkAtoms::region;
}
}
if (tagName == nsGkAtoms::form) {
nsAutoString name;
Name(name);
if (!name.IsEmpty()) {
if (!NameIsEmpty()) {
return nsGkAtoms::form;
}
}
@ -567,8 +563,10 @@ nsStaticAtom* Accessible::LandmarkRole() const {
nsStaticAtom* Accessible::ComputedARIARole() const {
const nsRoleMapEntry* roleMap = ARIARoleMap();
if (roleMap && roleMap->roleAtom != nsGkAtoms::_empty &&
// region has its own Gecko role and it needs to be handled specially.
// region and form have their own Gecko roles and need to be handled
// specially.
roleMap->roleAtom != nsGkAtoms::region &&
roleMap->roleAtom != nsGkAtoms::form &&
(roleMap->roleRule == kUseNativeRole || roleMap->IsOfType(eLandmark) ||
roleMap->roleAtom == nsGkAtoms::alertdialog ||
roleMap->roleAtom == nsGkAtoms::feed ||
@ -651,6 +649,12 @@ void Accessible::ApplyImplicitState(uint64_t& aState) const {
}
}
bool Accessible::NameIsEmpty() const {
nsAutoString name;
Name(name);
return name.IsEmpty();
}
////////////////////////////////////////////////////////////////////////////////
// KeyBinding class

View file

@ -720,6 +720,11 @@ class Accessible {
*/
void ApplyImplicitState(uint64_t& aState) const;
/*
* Return true if the accessible name is empty.
*/
bool NameIsEmpty() const;
private:
static const uint8_t kTypeBits = 6;
static const uint8_t kGenericTypesBits = 18;

View file

@ -1842,8 +1842,10 @@ bool LocalAccessible::SetCurValue(double aValue) {
role LocalAccessible::ARIATransformRole(role aRole) const {
// Beginning with ARIA 1.1, user agents are expected to use the native host
// language role of the element when the region role is used without a name.
// https://rawgit.com/w3c/aria/master/core-aam/core-aam.html#role-map-region
// language role of the element when the form or region roles are used without
// a name. Says the spec, "the user agent MUST treat such elements as if no
// role had been provided."
// https://w3c.github.io/aria/#document-handling_author-errors_roles
//
// XXX: While the name computation algorithm can be non-trivial in the general
// case, it should not be especially bad here: If the author hasn't used the
@ -1851,10 +1853,8 @@ role LocalAccessible::ARIATransformRole(role aRole) const {
// calculation rule excludes name from content. That said, this use case is
// another example of why we should consider caching the accessible name. See:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1378235.
if (aRole == roles::REGION) {
nsAutoString name;
Name(name);
return name.IsEmpty() ? NativeRole() : aRole;
if (aRole == roles::REGION || aRole == roles::FORM) {
return NameIsEmpty() ? NativeRole() : aRole;
}
// XXX: these unfortunate exceptions don't fit into the ARIA table. This is

View file

@ -47,6 +47,7 @@ addAccessibleTask(
<div id="complementary" role="complementary"></div>
<div id="contentinfo" role="contentinfo"></div>
<div id="form" role="form"></div>
<div id="form_label" aria-label="form" role="form"></div>
<div id="main" role="main"></div>
<div id="navigation" role="navigation"></div>
<div id="search" role="search"></div>
@ -149,7 +150,8 @@ addAccessibleTask(
"AXLandmarkComplementary"
);
testRoleAndSubRole(accDoc, "contentinfo", null, "AXLandmarkContentInfo");
testRoleAndSubRole(accDoc, "form", null, "AXLandmarkForm");
testRoleAndSubRole(accDoc, "form", null, "AXApplicationGroup");
testRoleAndSubRole(accDoc, "form_label", null, "AXLandmarkForm");
testRoleAndSubRole(accDoc, "main", null, "AXLandmarkMain");
testRoleAndSubRole(accDoc, "navigation", null, "AXLandmarkNavigation");
testRoleAndSubRole(accDoc, "search", null, "AXLandmarkSearch");

View file

@ -56,8 +56,8 @@
testRole("aria_directory_mixed", ROLE_LIST);
testRole("aria_document", ROLE_NON_NATIVE_DOCUMENT);
testRole("aria_document_mixed", ROLE_NON_NATIVE_DOCUMENT);
testRole("aria_form", ROLE_FORM);
testRole("aria_form_mixed", ROLE_FORM);
testRole("aria_form", ROLE_TEXT);
testRole("aria_form_mixed", ROLE_TEXT);
testRole("aria_form_with_label", ROLE_FORM);
testRole("aria_form_with_label_mixed", ROLE_FORM);
testRole("aria_feed", ROLE_GROUPING);
@ -184,8 +184,10 @@
testRole("articlemain", ROLE_LANDMARK);
testRole("articlemain_mixed", ROLE_LANDMARK);
testRole("articleform", ROLE_FORM);
testRole("articleform_mixed", ROLE_FORM);
testRole("articleform", ROLE_ARTICLE);
testRole("articleform_mixed", ROLE_ARTICLE);
testRole("articleform_label", ROLE_FORM);
testRole("articleform_label_mixed", ROLE_FORM);
// Test article exposed as article
testRole("testArticle", ROLE_ARTICLE);
@ -204,8 +206,10 @@
// strong landmark
testRole("application", ROLE_APPLICATION);
testRole("application_mixed", ROLE_APPLICATION);
testRole("form", ROLE_FORM);
testRole("form_mixed", ROLE_FORM);
testRole("form", ROLE_SECTION);
testRole("form_mixed", ROLE_SECTION);
testRole("form_label", ROLE_FORM);
testRole("form_label_mixed", ROLE_FORM);
testRole("application_table", ROLE_APPLICATION);
testRole("application_table_mixed", ROLE_APPLICATION);
@ -535,6 +539,8 @@
<article id="articlemain_mixed" role="mAIn">a main area</article>
<article id="articleform" role="form">a form area</article>
<article id="articleform_mixed" role="fORm">a form area</article>
<article id="articleform_label" aria-label="form" role="form">a form area</article>
<article id="articleform_label_mixed" aria-label="form" role="fORm">a form area</article>
<div id="testArticle" role="article" title="Test article">
<p>This is a paragraph inside the article.</p>
@ -561,6 +567,8 @@
<div role="aPPLICATIOn" id="application_mixed">application</div>
<div role="form" id="form">form</div>
<div role="fORm" id="form_mixed">form</div>
<div role="form" id="form_label" aria-label="form">form</div>
<div role="fORm" id="form_label_mixed" aria-label="form">form</div>
<!-- weak landmarks -->
<div role="banner" id="banner">banner</div>

View file

@ -160,7 +160,7 @@
<!-- natural and aria-owns hierarchy -->
<div id="t5_2" role="note"><div aria-owns="t5_3" role="heading"></div></div>
<div id="t5_1"><div aria-owns="t5_2" role="group"></div></div>
<div id="t5_3" role="form"><div aria-owns="t5_1" role="tooltip"></div></div>
<div id="t5_3" aria-label="form" role="form"><div aria-owns="t5_1" role="tooltip"></div></div>
<!-- rearrange children -->
<div id="t6_1" aria-owns="t6_3 t6_2">

View file

@ -1,3 +0,0 @@
[form-roles.html]
[form without label]
expected: FAIL