RESOLVED FIXED 224582
AX: `aria-activedescendant` doesn't work with a standard "listbox" pattern
https://bugs.webkit.org/show_bug.cgi?id=224582
Summary AX: `aria-activedescendant` doesn't work with a standard "listbox" pattern
Ben Cronin
Reported 2021-04-14 15:57:41 PDT
With VoiceOver running, go to the example at https://jsfiddle.net/upy3m6n7/3/ and place the cursor inside the "Editable text" field. Use the arrow keys to move through the options (the selected option will get a "selected" class and become `aria-activedescendant` of the "editor" textbox element). Expected: VoiceOver will identify each option as it is selected and becomes the `aria-activedescendant` of the "editor" element (this happens in Chrome and Firefox). Observed: VoiceOver reads out nothing as each option becomes selected; there is no indication that there is an active descendant. The spec clearly indicates that elements with "textbox" role and correct markup should roles should accommodate `aria-activedescendant` (see https://www.w3.org/TR/wai-aria/#aria-activedescendant, particularly item 2). FWIW, though, as far as I can tell, the above behavior occurs with any role applied to the "editor" node.
Attachments
Patch (10.96 KB, patch)
2022-12-25 00:08 PST, chris fleizach
no flags
Patch (11.30 KB, patch)
2022-12-25 14:27 PST, chris fleizach
no flags
Patch (11.43 KB, patch)
2022-12-27 00:06 PST, chris fleizach
no flags
Patch (11.37 KB, patch)
2023-01-04 08:45 PST, chris fleizach
no flags
Patch (11.37 KB, patch)
2023-01-04 09:20 PST, chris fleizach
no flags
Radar WebKit Bug Importer
Comment 1 2021-04-14 15:57:50 PDT
Dhawal
Comment 2 2022-04-12 02:25:47 PDT
Same issue here. Here's our implementation https://jsfiddle.net/f5Larbnk/19/ Click on button and hover on the options. Note that voiceover in the above fiddle works fine in Firefox, Chrome when active-descendant is updated.
chris fleizach
Comment 3 2022-12-25 00:08:15 PST
Tyler Wilcock
Comment 4 2022-12-25 10:55:12 PST
Comment on attachment 464203 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=464203&action=review > Source/WebCore/accessibility/AXObjectCache.cpp:1898 > + if (!element.document().frame()->selection().isFocusedAndActive()) Do you think it would be useful to have a comment stating why it's important to use the element document and not the cache document? Perhaps that will help prevent us from making the same mistake again. > Source/WebCore/accessibility/AXObjectCache.cpp:1932 > + target = Accessibility::findAncestor(*activeDescendant, true, [&object] (const auto& ancestor) { > + return object->relatedObjects(AXRelationType::ControllerFor).contains(&ancestor); > + }); Could you explain what the intention of this loop is? It's confusing to me because: 1. In the first iteration of the loop, `const auto& ancestor` is actually not an ancestor, it's a descendant (*activeDescendant) 2. Because we start iterating from a descendant, eventually we will iterate to `object`, and then above `object`, checking to see if it aria-controls itself, and then any of its ancestors. Does this make sense? I think this code works for the simple listbox example in this bug because we return true in the first iteration. But I don't understand what this loop accomplishes generally, which might just be my lack of understanding. Can you explain what the intention is here? > Source/WebCore/accessibility/AXObjectCache.cpp:4104 > + if (is<HTMLFrameOwnerElement>(element)) > + updateRelationsForTree(*downcast<HTMLFrameOwnerElement>(element).contentDocument()); Nit: this could arguably be simpler with dynamicDowncast. if (auto* frameOwnerElement = dynamicDowncast<HTMLFrameOwnerElement>(element)) updateRelationsForTree(frameOwnerElement->contentDocument()); > LayoutTests/accessibility/mac/relationships-in-frames.html:35 > + debug(output); The indentation level of this debug doesn't match its siblings.
chris fleizach
Comment 5 2022-12-25 14:24:05 PST
(In reply to Tyler Wilcock from comment #4) > Comment on attachment 464203 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=464203&action=review > > > Source/WebCore/accessibility/AXObjectCache.cpp:1898 > > + if (!element.document().frame()->selection().isFocusedAndActive()) > > Do you think it would be useful to have a comment stating why it's important > to use the element document and not the cache document? Perhaps that will > help prevent us from making the same mistake again. > > > Source/WebCore/accessibility/AXObjectCache.cpp:1932 > > + target = Accessibility::findAncestor(*activeDescendant, true, [&object] (const auto& ancestor) { > > + return object->relatedObjects(AXRelationType::ControllerFor).contains(&ancestor); > > + }); > > Could you explain what the intention of this loop is? It's confusing to me > because: > > 1. In the first iteration of the loop, `const auto& ancestor` is actually > not an ancestor, it's a descendant (*activeDescendant) > 2. Because we start iterating from a descendant, eventually we will > iterate to `object`, and then above `object`, checking to see if it > aria-controls itself, and then any of its ancestors. Does this make sense? > > I think this code works for the simple listbox example in this bug because > we return true in the first iteration. But I don't understand what this loop > accomplishes generally, which might just be my lack of understanding. Can > you explain what the intention is here? > Added better comments in the code. Basically, the target needs to be thing we send out the notification for. The active descendant needs to be a descendant of that, but the one generating the notification is the controller. So we go up the active-descendant hierarchy to see if we find the object that is being controlled by the "controller". If we find that object than we use it as the target. > > Source/WebCore/accessibility/AXObjectCache.cpp:4104 > > + if (is<HTMLFrameOwnerElement>(element)) > > + updateRelationsForTree(*downcast<HTMLFrameOwnerElement>(element).contentDocument()); > > Nit: this could arguably be simpler with dynamicDowncast. > > if (auto* frameOwnerElement = > dynamicDowncast<HTMLFrameOwnerElement>(element)) > updateRelationsForTree(frameOwnerElement->contentDocument()); > > > LayoutTests/accessibility/mac/relationships-in-frames.html:35 > > + debug(output); > > The indentation level of this debug doesn't match its siblings. Will make these changes
chris fleizach
Comment 6 2022-12-25 14:27:02 PST
Tyler Wilcock
Comment 7 2022-12-25 14:32:43 PST
Comment on attachment 464211 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=464211&action=review > Source/WebCore/accessibility/AXObjectCache.cpp:1935 > + target = Accessibility::findAncestor(*activeDescendant, false, [&object] (const auto& activeDescendantAncestor) { > + return object->relatedObjects(AXRelationType::ControllerFor).contains(&activeDescendantAncestor); > + }); Thanks for clarifying. One last question: rather than re-computing object->relatedObjects(AXRelationType::ControllerFor) every iteration, can we extract it into a local variable?
chris fleizach
Comment 8 2022-12-27 00:06:09 PST
Andres Gonzalez
Comment 9 2023-01-04 05:12:02 PST
(In reply to chris fleizach from comment #8) > Created attachment 464221 [details] > Patch --- /dev/null +++ b/LayoutTests/accessibility/mac/active-descendant-with-aria-controls.html +var output = "This test ensures objects that control objects with active descendants generate the correct notifications, while also being inside frames.\n\n"; No iframe involved in this test. Not sure that this isn't flaky because the order of the calls to debug() in the notificationCallback and main is not deterministic. There may be differences in timing between ITM on and off. I would run it with --iterations=100 to ensure.
chris fleizach
Comment 10 2023-01-04 08:45:59 PST
chris fleizach
Comment 11 2023-01-04 09:20:08 PST
EWS
Comment 12 2023-01-04 23:09:13 PST
Committed 258478@main (f78fd4847fdb): <https://commits.webkit.org/258478@main> All reviewed patches have been landed. Closing bug and clearing flags on attachment 464330 [details].
Note You need to log in before you can comment on or make changes to this bug.