diff --git a/LayoutTests/accessibility/aria-hidden-false-ignored-expected.txt b/LayoutTests/accessibility/aria-hidden-false-ignored-expected.txt new file mode 100644 index 0000000000000..e63849b34ade3 --- /dev/null +++ b/LayoutTests/accessibility/aria-hidden-false-ignored-expected.txt @@ -0,0 +1,11 @@ +This test ensures that aria-hidden=false is treated like an undefined value. + +PASS: !hiddenText === true +PASS: !invisibleText === true +PASS: !ariaHiddenText === true + +PASS successfullyParsed is true + +TEST COMPLETE + +This text is hidden with aria-hidden diff --git a/LayoutTests/accessibility/aria-hidden-false-ignored.html b/LayoutTests/accessibility/aria-hidden-false-ignored.html new file mode 100644 index 0000000000000..8e1a58248ae71 --- /dev/null +++ b/LayoutTests/accessibility/aria-hidden-false-ignored.html @@ -0,0 +1,38 @@ + + + + + + + + +
+

This text should be hidden

+
+ +
+

This text should be invisible

+
+ + + + + + + diff --git a/LayoutTests/accessibility/aria-hidden-false-works-in-subtrees.html b/LayoutTests/accessibility/aria-hidden-false-works-in-subtrees.html deleted file mode 100644 index 417ab9a6bdd52..0000000000000 --- a/LayoutTests/accessibility/aria-hidden-false-works-in-subtrees.html +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - - - -
- - - - - - - - - - -
- -
- - - - - - - -
- -
- -
- - - - diff --git a/LayoutTests/accessibility/aria-hidden-negates-no-visibility.html b/LayoutTests/accessibility/aria-hidden-negates-no-visibility.html deleted file mode 100644 index 40b3f74ef81de..0000000000000 --- a/LayoutTests/accessibility/aria-hidden-negates-no-visibility.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - -

heading1.1

-

heading1.2

-

heading1.3

- -

heading2

-

heading3

-

heading4

- - - - -
-
- -
-
- -

-
- - - - - - 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 @@ + + + + + + + + + + + + + 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 @@ - - - - - - - - -
- - - - - - - - - - - cake - - - - - - - - -
- - - -
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Main content
- - - - - - - - - - - - - - - - - - - - - - - -
- - - - - 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 a2597daf2a6b6..4f3bda6667372 100644 --- a/Source/WebCore/accessibility/AXObjectCache.cpp +++ b/Source/WebCore/accessibility/AXObjectCache.cpp @@ -422,7 +422,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(); @@ -446,10 +447,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. @@ -882,7 +880,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 && (!element || !element->focused())) return nullptr; } @@ -4658,44 +4656,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 a0e52a4b16a5d..8746a6aa2eb56 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 3fdf4e0d16e08..230c28bd48c63 100644 --- a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp +++ b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp @@ -2317,22 +2317,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() && !isAXHidden() ? 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() || isAXHidden()) && (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 2a4e9158e4bf3..9b09f91995c7c 100644 --- a/Source/WebCore/accessibility/AccessibilityObject.cpp +++ b/Source/WebCore/accessibility/AccessibilityObject.cpp @@ -3949,10 +3949,6 @@ AccessibilityObjectInclusion AccessibilityObject::defaultObjectInclusion() const 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; - return AccessibilityObjectInclusion::IgnoreObject; } } diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp index 3d8539b002b03..67f740ac0d5c5 100644 --- a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp +++ b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp @@ -2405,7 +2405,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() { @@ -2424,7 +2424,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) || isAXHidden()) continue; unsigned previousSize = m_children.size();