-
-
-
-
-
-
-
-
-
diff --git a/LayoutTests/accessibility/aria-hidden-subtree-expected.txt b/LayoutTests/accessibility/aria-hidden-subtree-expected.txt
new file mode 100644
index 0000000000000..b53638fd4ac79
--- /dev/null
+++ b/LayoutTests/accessibility/aria-hidden-subtree-expected.txt
@@ -0,0 +1,19 @@
+This test ensures that the entire subtree of an aria-hidden object is hidden.
+
+DIV is aria-hidden=true:
+PASS: !headingElement === true
+PASS: !paragraphElement === true
+PASS: !buttonElement === true
+DIV has aria-hidden unset:
+PASS: !!headingElement === true
+PASS: !!paragraphElement === true
+PASS: !!buttonElement === true
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Heading
+
+This is a nested paragraph
+
+Done
diff --git a/LayoutTests/accessibility/aria-hidden-subtree.html b/LayoutTests/accessibility/aria-hidden-subtree.html
new file mode 100644
index 0000000000000..032e713e07033
--- /dev/null
+++ b/LayoutTests/accessibility/aria-hidden-subtree.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
Heading
+
+
This is a nested paragraph
+
+
+
+
+
+
+
diff --git a/LayoutTests/accessibility/aria-modal-expected.txt b/LayoutTests/accessibility/aria-modal-expected.txt
index 66074671d1b7a..73b3af618ccb5 100644
--- a/LayoutTests/accessibility/aria-modal-expected.txt
+++ b/LayoutTests/accessibility/aria-modal-expected.txt
@@ -16,7 +16,7 @@ Dialog is displaying
PASS backgroundAccessible() is false
Dialog is displaying and aria-hidden=true
PASS backgroundAccessible() is true
-Dialog is displaying and aria-hidden=false
+Dialog is displaying and removed aria-hidden
PASS backgroundAccessible() is false
Dialog is not displaying with opacity 0
PASS backgroundAccessible() is true
diff --git a/LayoutTests/accessibility/aria-modal.html b/LayoutTests/accessibility/aria-modal.html
index 82c3cae0857c8..39bdb7ef7c7e8 100644
--- a/LayoutTests/accessibility/aria-modal.html
+++ b/LayoutTests/accessibility/aria-modal.html
@@ -75,12 +75,12 @@
Just an example.
debug("Dialog is displaying and aria-hidden=true")
shouldBeTrue("backgroundAccessible()");
- // Set aria-hidden=false.
- document.getElementById("box").setAttribute("aria-hidden", "false");
+ // Unset aria hidden.
+ document.getElementById("box").removeAttribute("aria-hidden");
await waitFor(() => {
return !backgroundAccessible();
});
- debug("Dialog is displaying and aria-hidden=false");
+ debug("Dialog is displaying and removed aria-hidden");
shouldBeFalse("backgroundAccessible()");
// Set opacity to 0 which should make the dialog invisible.
diff --git a/LayoutTests/accessibility/aria-visible-element-roles.html b/LayoutTests/accessibility/aria-visible-element-roles.html
deleted file mode 100644
index a905a04ed7093..0000000000000
--- a/LayoutTests/accessibility/aria-visible-element-roles.html
+++ /dev/null
@@ -1,202 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/LayoutTests/accessibility/datetime/input-date-field-labels-and-value-changes.html b/LayoutTests/accessibility/datetime/input-date-field-labels-and-value-changes.html
index b70f5d842888b..0511ff569d4fb 100644
--- a/LayoutTests/accessibility/datetime/input-date-field-labels-and-value-changes.html
+++ b/LayoutTests/accessibility/datetime/input-date-field-labels-and-value-changes.html
@@ -73,6 +73,7 @@
output += "\nSet the date via JavaScript.\n";
dateInput.value = "2023-12-31";
+ await waitFor(() => axDateInput.childAtIndex(0).childAtIndex(0).children[4] != null);
await expectAsync("axDateInput.childAtIndex(0).childAtIndex(0).children[4].intValue", "2023");
getAndLogSubfields(axDateInput);
diff --git a/Source/WebCore/accessibility/AXObjectCache.cpp b/Source/WebCore/accessibility/AXObjectCache.cpp
index b946303d7d9aa..c69e1a4325ddc 100644
--- a/Source/WebCore/accessibility/AXObjectCache.cpp
+++ b/Source/WebCore/accessibility/AXObjectCache.cpp
@@ -352,7 +352,8 @@ bool AXObjectCache::modalElementHasAccessibleContent(Element& element)
// When using ATSPI, an accessibility object with 'StaticText' role is ignored.
// Its content is exposed by its parent.
// Treat such elements as having accessible content.
- if (axObject->roleValue() == AccessibilityRole::StaticText)
+ // FIXME: This may not be sufficient for visibility:hidden or inert (https://bugs.webkit.org/show_bug.cgi?id=280914).
+ if (axObject->roleValue() == AccessibilityRole::StaticText && !axObject->isAXHidden())
return true;
#endif
}
@@ -435,7 +436,8 @@ void AXObjectCache::updateCurrentModalNode(WillRecomputeFocus willRecomputeFocus
bool AXObjectCache::isNodeVisible(Node* node) const
{
- if (!is(node))
+ RefPtr element = dynamicDowncast(node);
+ if (!element)
return false;
RenderObject* renderer = node->renderer();
@@ -459,10 +461,7 @@ bool AXObjectCache::isNodeVisible(Node* node) const
}
// We also need to consider aria hidden status.
- if (!isNodeAriaVisible(*node))
- return false;
-
- return true;
+ return !equalLettersIgnoringASCIICase(element->attributeWithDefaultARIA(aria_hiddenAttr), "true"_s) || element->focused();
}
// This function returns the valid aria modal node.
@@ -895,7 +894,7 @@ AccessibilityObject* AXObjectCache::getOrCreate(Node& node, IsPartOfRelation isP
auto* element = dynamicDowncast(node);
bool hasDisplayContents = element && element->hasDisplayContents();
bool isPopover = element && element->hasAttributeWithoutSynchronization(popoverAttr);
- if (!inCanvasSubtree && !insideMeterElement && !hasDisplayContents && !isPopover && !isNodeAriaVisible(node))
+ if (!inCanvasSubtree && !insideMeterElement && !hasDisplayContents && !isPopover && !isNodeFocused(node))
return nullptr;
}
@@ -4693,44 +4692,9 @@ void AXObjectCache::deferTextReplacementNotificationForTextControl(HTMLTextFormC
m_deferredTextFormControlValue.add(formControlElement, previousValue);
}
-bool isNodeAriaVisible(Node* node)
-{
- return node ? isNodeAriaVisible(*node) : false;
-}
-
-bool isNodeAriaVisible(Node& node)
+bool isNodeFocused(Node& node)
{
- // If an element is focused, it should not be hidden.
- if (RefPtr element = dynamicDowncast(node); element && element->focused())
- return true;
-
- // ARIA Node visibility is controlled by aria-hidden
- // 1) if aria-hidden=true, the whole subtree is hidden
- // 2) if aria-hidden=false, and the object is rendered, there's no effect
- // 3) if aria-hidden=false, and the object is NOT rendered, then it must have
- // aria-hidden=false on each parent until it gets to a rendered object
- // 3b) a text node inherits a parents aria-hidden value
- bool requiresAriaHiddenFalse = !node.renderer();
- bool ariaHiddenFalsePresent = false;
- for (auto* testNode = &node; testNode; testNode = testNode->parentNode()) {
- if (RefPtr element = dynamicDowncast(*testNode)) {
- const auto& ariaHiddenValue = element->attributeWithoutSynchronization(aria_hiddenAttr);
- if (equalLettersIgnoringASCIICase(ariaHiddenValue, "true"_s))
- return false;
-
- // We should break early when it gets to the body.
- if (testNode->hasTagName(bodyTag))
- break;
-
- bool ariaHiddenFalse = equalLettersIgnoringASCIICase(ariaHiddenValue, "false"_s);
- if (!testNode->renderer() && !ariaHiddenFalse)
- return false;
- if (!ariaHiddenFalsePresent && ariaHiddenFalse)
- ariaHiddenFalsePresent = true;
- }
- }
-
- return !requiresAriaHiddenFalse || ariaHiddenFalsePresent;
+ return is(node) && uncheckedDowncast(node).focused();
}
// DOM component of hidden definition.
diff --git a/Source/WebCore/accessibility/AXObjectCache.h b/Source/WebCore/accessibility/AXObjectCache.h
index 63b94d698999a..7cbc2e26d85ed 100644
--- a/Source/WebCore/accessibility/AXObjectCache.h
+++ b/Source/WebCore/accessibility/AXObjectCache.h
@@ -907,9 +907,7 @@ bool nodeHasTableRole(Node*);
// https://www.w3.org/TR/accname-1.2/
bool hasAccNameAttribute(Element&);
-// This will let you know if aria-hidden was explicitly set to false.
-bool isNodeAriaVisible(Node&);
-bool isNodeAriaVisible(Node*);
+bool isNodeFocused(Node&);
bool isDOMHidden(const RenderStyle*);
diff --git a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp
index b362d674c156e..9e841d75eed56 100644
--- a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp
+++ b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp
@@ -2314,22 +2314,16 @@ static bool shouldPrependSpace(AXCoreObject& object, AXCoreObject* previousObjec
String AccessibilityNodeObject::textUnderElement(TextUnderElementMode mode) const
{
- auto isAriaVisible = [this] () {
- return Accessibility::findAncestor(*this, true, [] (const auto& object) {
- return equalLettersIgnoringASCIICase(object.getAttribute(aria_hiddenAttr), "false"_s);
- }) != nullptr;
- };
-
RefPtr node = this->node();
if (auto* text = dynamicDowncast(node.get()))
- return !mode.isHidden() || isAriaVisible() ? text->wholeText() : emptyString();
+ return !mode.isHidden() ? text->wholeText() : emptyString();
const auto* style = this->style();
mode.inHiddenSubtree = WebCore::isDOMHidden(style);
// The Accname specification states that if the current node is hidden, and not directly
// referenced by aria-labelledby or aria-describedby, and is not a host language text
// alternative, the empty string should be returned.
- if (mode.isHidden() && !isAriaVisible() && (node && !ancestorsOfType(*node).first())) {
+ if (mode.isHidden() && node && !ancestorsOfType(*node).first()) {
if (!labelForObjects().isEmpty() || !descriptionForObjects().isEmpty()) {
// This object is a hidden label or description for another object, so ignore hidden states for our
// subtree text under element traversals too.
diff --git a/Source/WebCore/accessibility/AccessibilityObject.cpp b/Source/WebCore/accessibility/AccessibilityObject.cpp
index a157318086f65..f6bda07cff0c6 100644
--- a/Source/WebCore/accessibility/AccessibilityObject.cpp
+++ b/Source/WebCore/accessibility/AccessibilityObject.cpp
@@ -4048,13 +4048,8 @@ AccessibilityObjectInclusion AccessibilityObject::defaultObjectInclusion() const
if (auto* style = this->style()) {
if (style->effectiveInert())
return AccessibilityObjectInclusion::IgnoreObject;
- if (style->usedVisibility() != Visibility::Visible) {
- // aria-hidden is meant to override visibility as the determinant in AX hierarchy inclusion.
- if (equalLettersIgnoringASCIICase(getAttribute(aria_hiddenAttr), "false"_s))
- return AccessibilityObjectInclusion::DefaultBehavior;
-
+ if (style->usedVisibility() != Visibility::Visible)
return AccessibilityObjectInclusion::IgnoreObject;
- }
}
bool useParentData = !m_isIgnoredFromParentData.isNull();
diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp
index 83f363b7494d9..50fd084287210 100644
--- a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp
+++ b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp
@@ -2404,7 +2404,7 @@ void AccessibilityRenderObject::updateAttachmentViewParents()
#endif
// Some elements don't have an associated render object, meaning they won't be picked up by a walk of the render tree.
-// For example, nodes that are `aria-hidden="false"` and `hidden`, or elements with `display: contents`.
+// For example, elements with `display: contents`, or aria-hidden=true elements that are focused.
// This function will find and add these elements to the AX tree.
void AccessibilityRenderObject::addNodeOnlyChildren()
{
@@ -2423,7 +2423,7 @@ void AccessibilityRenderObject::addNodeOnlyChildren()
if (child->renderer())
continue;
- if (nodeHasDisplayContents(*child) || isNodeAriaVisible(*child)) {
+ if (nodeHasDisplayContents(*child) || isNodeFocused(*child)) {
hasNodeOnlyChildren = true;
break;
}
@@ -2456,7 +2456,7 @@ void AccessibilityRenderObject::addNodeOnlyChildren()
continue;
}
- if (!nodeHasDisplayContents(*child) && !isNodeAriaVisible(*child))
+ if (!nodeHasDisplayContents(*child))
continue;
unsigned previousSize = m_children.size();