diff --git a/LayoutTests/accessibility/mac/expanded-notification-expected.txt b/LayoutTests/accessibility/mac/expanded-notification-expected.txt index e3f21bd5c6f94..d8faa3f4a4f22 100644 --- a/LayoutTests/accessibility/mac/expanded-notification-expected.txt +++ b/LayoutTests/accessibility/mac/expanded-notification-expected.txt @@ -1,11 +1,19 @@ -This tests that aria-expanded changes will send notifications. +This tests that expanded notifications will be sent when the appropriate changes occur. Initial expanded status: false Received notification: AXExpandedChanged Expanded status: true Received notification: AXExpandedChanged Expanded status: false +PASS: accessibilityController.accessibleElementById('show-popover-btn').isExpanded === false +PASS: accessibilityController.accessibleElementById('hide-popover-btn').isExpanded === false +Received notification: AXExpandedChanged +Expanded status: true +PASS: accessibilityController.accessibleElementById('hide-popover-btn').isExpanded === true +Received notification: AXExpandedChanged +Expanded status: false +PASS: accessibilityController.accessibleElementById('show-popover-btn').isExpanded === false PASS successfullyParsed is true TEST COMPLETE - +Show popover Hide popover diff --git a/LayoutTests/accessibility/mac/expanded-notification.html b/LayoutTests/accessibility/mac/expanded-notification.html index e1b1853a41f29..6b8e015352d73 100644 --- a/LayoutTests/accessibility/mac/expanded-notification.html +++ b/LayoutTests/accessibility/mac/expanded-notification.html @@ -8,46 +8,56 @@ + +
Popover content
+ diff --git a/Source/WebCore/accessibility/AXObjectCache.cpp b/Source/WebCore/accessibility/AXObjectCache.cpp index a0fa72b5496fc..0e31e4adc5281 100644 --- a/Source/WebCore/accessibility/AXObjectCache.cpp +++ b/Source/WebCore/accessibility/AXObjectCache.cpp @@ -1472,6 +1472,11 @@ void AXObjectCache::onFocusChange(Node* oldNode, Node* newNode) handleFocusedUIElementChanged(oldNode, newNode); } +void AXObjectCache::onPopoverTargetToggle(const HTMLFormControlElement& popoverInvokerElement) +{ + postNotification(get(const_cast(&popoverInvokerElement)), &document(), AXExpandedChanged); +} + void AXObjectCache::deferMenuListValueChange(Element* element) { if (!element) diff --git a/Source/WebCore/accessibility/AXObjectCache.h b/Source/WebCore/accessibility/AXObjectCache.h index 9b2caf9e087df..b3afd42056e87 100644 --- a/Source/WebCore/accessibility/AXObjectCache.h +++ b/Source/WebCore/accessibility/AXObjectCache.h @@ -182,6 +182,7 @@ class AXObjectCache : public CanMakeWeakPtr, public CanMakeChecke void childrenChanged(RenderObject*, RenderObject* newChild = nullptr); void childrenChanged(AccessibilityObject*); void onFocusChange(Node* oldFocusedNode, Node* newFocusedNode); + void onPopoverTargetToggle(const HTMLFormControlElement&); void onScrollbarFrameRectChange(const Scrollbar&); void onSelectedChanged(Node*); void onTextSecurityChanged(HTMLInputElement&); diff --git a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp index a757a63957520..49cf28c3b8453 100644 --- a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp +++ b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp @@ -1107,6 +1107,12 @@ Element* AccessibilityNodeObject::anchorElement() const return nullptr; } +Element* AccessibilityNodeObject::popoverTargetElement() const +{ + WeakPtr formControlElement = dynamicDowncast(node()); + return formControlElement ? formControlElement->popoverTargetElement() : nullptr; +} + AccessibilityObject* AccessibilityNodeObject::internalLinkElement() const { // We don't currently support ARIA links as internal link elements, so exit early if anchorElement() is not a native HTMLAnchorElement. diff --git a/Source/WebCore/accessibility/AccessibilityNodeObject.h b/Source/WebCore/accessibility/AccessibilityNodeObject.h index 713e61e45584a..ce0ea4038ef7a 100644 --- a/Source/WebCore/accessibility/AccessibilityNodeObject.h +++ b/Source/WebCore/accessibility/AccessibilityNodeObject.h @@ -121,6 +121,7 @@ class AccessibilityNodeObject : public AccessibilityObject { Element* actionElement() const override; Element* mouseButtonListener(MouseButtonListenerResultFilter = ExcludeBodyElement) const; Element* anchorElement() const override; + Element* popoverTargetElement() const final; AccessibilityObject* internalLinkElement() const; void addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const; void addRadioButtonGroupChildren(AXCoreObject&, AccessibilityChildrenVector&) const; diff --git a/Source/WebCore/accessibility/AccessibilityObject.cpp b/Source/WebCore/accessibility/AccessibilityObject.cpp index 59e684f27a907..44788750036cf 100644 --- a/Source/WebCore/accessibility/AccessibilityObject.cpp +++ b/Source/WebCore/accessibility/AccessibilityObject.cpp @@ -3269,6 +3269,10 @@ bool AccessibilityObject::supportsPressed() const bool AccessibilityObject::supportsExpanded() const { + // If this object can toggle an HTML popover, it supports the reporting of its expanded state (which is based on the expanded / collapsed state of that popover). + if (popoverTargetElement()) + return true; + switch (roleValue()) { case AccessibilityRole::Button: case AccessibilityRole::CheckBox: @@ -3322,8 +3326,11 @@ bool AccessibilityObject::isExpanded() const return parent->isExpanded(); } - if (supportsExpanded()) + if (supportsExpanded()) { + if (WeakPtr popoverTargetElement = this->popoverTargetElement()) + return popoverTargetElement->isPopoverShowing(); return equalLettersIgnoringASCIICase(getAttribute(aria_expandedAttr), "true"_s); + } return false; } diff --git a/Source/WebCore/accessibility/AccessibilityObject.h b/Source/WebCore/accessibility/AccessibilityObject.h index ca3a33c47d53a..bda2ccd281336 100644 --- a/Source/WebCore/accessibility/AccessibilityObject.h +++ b/Source/WebCore/accessibility/AccessibilityObject.h @@ -423,6 +423,7 @@ class AccessibilityObject : public AXCoreObject, public CanMakeWeakPtrpopoverData()->visibilityState() == PopoverVisibilityState::Showing; bool canShow = action == showAtom() || action == toggleAtom(); - if (canHide && target->popoverData()->visibilityState() == PopoverVisibilityState::Showing) + bool shouldShow = canShow && target->popoverData()->visibilityState() == PopoverVisibilityState::Hidden; + + if (shouldHide) target->hidePopover(); - else if (canShow && target->popoverData()->visibilityState() == PopoverVisibilityState::Hidden) + else if (shouldShow) target->showPopover(this); + + if (shouldHide || shouldShow) { + // Accessibility needs to know that the invoker (this) toggled popover visibility state. + if (auto* cache = document().existingAXObjectCache()) + cache->onPopoverTargetToggle(*this); + } } // FIXME: We should remove the quirk once is fixed.