RESOLVED FIXED Bug 237163
AX: WebKit should ignore empty modals rather than trapping focus inside them
https://bugs.webkit.org/show_bug.cgi?id=237163
Summary AX: WebKit should ignore empty modals rather than trapping focus inside them
Tyler Wilcock
Reported 2022-02-24 15:27:05 PST
Given this markup: <div role="dialog" aria-modal="true"> <div aria-hidden="true"> <button>Close modal (inside modal)</button> </div> </div> There is no accessible content inside this modal, but WebKit traps user focus inside, making all of the rest of the page completely inaccessible. medium.com articles (e.g. https://medium.com/@emilymenonbender/no-llms-arent-like-people-with-disabilities-and-it-s-problematic-to-argue-that-they-are-a2ac0df0e435) have similar markup, and are thus entirely inaccessible.
Attachments
Patch (9.67 KB, patch)
2022-02-24 15:52 PST, Tyler Wilcock
no flags
Patch (65.17 KB, patch)
2022-02-26 09:40 PST, Tyler Wilcock
no flags
Patch (64.25 KB, patch)
2022-02-26 10:24 PST, Tyler Wilcock
no flags
Alternate approach (16.60 KB, patch)
2022-02-27 12:45 PST, Tyler Wilcock
no flags
Patch (17.16 KB, patch)
2022-02-28 12:50 PST, Tyler Wilcock
no flags
Radar WebKit Bug Importer
Comment 1 2022-02-24 15:27:19 PST
Tyler Wilcock
Comment 2 2022-02-24 15:52:08 PST
chris fleizach
Comment 3 2022-02-24 16:26:32 PST
Comment on attachment 453145 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=453145&action=review > Source/WebCore/accessibility/AXObjectCache.cpp:277 > + RenderObject* renderer = node->renderer(); auto* > Source/WebCore/accessibility/AXObjectCache.cpp:305 > +{ This seems very expensive now. Determining aria modal will have to visit every node. Is this something we could determine another way?
Tyler Wilcock
Comment 4 2022-02-24 16:38:00 PST
> > Source/WebCore/accessibility/AXObjectCache.cpp:305 > > +{ > > This seems very expensive now. Determining aria modal will have to visit > every node. Is this something we could determine another way? The only additional work we're doing is on elements within m_modalElementsSet, which is limited only to page elements with role="dialog" and aria-modal="true", or <dialog> elements. And from there, the additional work we do is a scan of the direct children of the modal (i.e. one layer traversal vs. an entire subtree traversal). And then the result is cached in m_currentModalElement, so we shouldn't have to re-compute this often. So I don't think it's much more expensive. What do you think?
Andres Gonzalez
Comment 5 2022-02-24 17:14:11 PST
(In reply to Tyler Wilcock from comment #2) > Created attachment 453145 [details] > Patch --- a/Source/WebCore/accessibility/AXObjectCache.cpp +++ a/Source/WebCore/accessibility/AXObjectCache.cpp +static bool isNodeVisible(Node* node) +{ + if (!is<Element>(node)) + return false; I think static text will return false here, don't see the need for this check, only when you are going to check for element attributes like aria-hidden. Ah I see down below that this was a class method and you turned it into a static function. Still I don't think that the check for is<Element> is good.
chris fleizach
Comment 6 2022-02-24 22:35:50 PST
(In reply to Tyler Wilcock from comment #4) > > > Source/WebCore/accessibility/AXObjectCache.cpp:305 > > > +{ > > > > This seems very expensive now. Determining aria modal will have to visit > > every node. Is this something we could determine another way? > The only additional work we're doing is on elements within > m_modalElementsSet, which is limited only to page elements with > role="dialog" and aria-modal="true", or <dialog> elements. > > And from there, the additional work we do is a scan of the direct children > of the modal (i.e. one layer traversal vs. an entire subtree traversal). Will a one level traversal be enough or do we need to keep digging to determine if we still have accessible content? > > And then the result is cached in m_currentModalElement, so we shouldn't have > to re-compute this often. > > So I don't think it's much more expensive. What do you think?
Aakash Jain
Comment 7 2022-02-25 06:41:22 PST
I cancelled https://ews-build.webkit.org/#/builders/68/builds/9127 to speed up ios-wk2 queue. It finished running layout-test and indicated some failures, please have a look.
Tyler Wilcock
Comment 8 2022-02-25 08:51:39 PST
> Will a one level traversal be enough or do we need to keep digging to > determine if we still have accessible content? One layer is enough, since aria-hidden="true", visibility: hidden, and display: none all inherently make the layers beneath also invisible to AX. So we only need to find one thing in the first layer that is none of those to consider the modal valid.
chris fleizach
Comment 9 2022-02-25 09:00:19 PST
(In reply to Tyler Wilcock from comment #8) > > Will a one level traversal be enough or do we need to keep digging to > > determine if we still have accessible content? > One layer is enough, since aria-hidden="true", visibility: hidden, and > display: none all inherently make the layers beneath also invisible to AX. > So we only need to find one thing in the first layer that is none of those > to consider the modal valid. What if we had (apologies for code typed on a phone) <div modal=true> <div> <div> <div hidden> Content
Tyler Wilcock
Comment 10 2022-02-25 09:11:15 PST
> What if we had (apologies for code typed on a phone) > > <div modal=true> > <div> > <div> > <div hidden> > Content You're right, we do need to traverse multiple levels in this case. I'll fix it.
Tyler Wilcock
Comment 11 2022-02-26 09:40:18 PST
chris fleizach
Comment 12 2022-02-26 10:00:46 PST
Comment on attachment 453297 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=453297&action=review > Source/WebCore/accessibility/ShouldConsiderModals.h:30 > +enum class ShouldConsiderModals : uint8_t { No, Yes }; I think we could drop the should from this and still have same clarity. Also can we put this enum in some accessibility file? Seems like a little for just one yes/no
Tyler Wilcock
Comment 13 2022-02-26 10:08:05 PST
(In reply to chris fleizach from comment #12) > Comment on attachment 453297 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=453297&action=review > > > Source/WebCore/accessibility/ShouldConsiderModals.h:30 > > +enum class ShouldConsiderModals : uint8_t { No, Yes }; > > I think we could drop the should from this and still have same clarity. > > Also can we put this enum in some accessibility file? Seems like a little > for just one yes/no I tried putting it in AccessibilityObjectInterface.h, which worked everywhere except when it's used in AXObjectCache.cpp which doesn't include the interface header. They don't share any includes that would make sense to put this new enum in, so I went for a separate file. ConsiderModals sounds good to me, will update.
Tyler Wilcock
Comment 14 2022-02-26 10:24:23 PST
Andres Gonzalez
Comment 15 2022-02-26 12:06:20 PST
(In reply to Tyler Wilcock from comment #14) > Created attachment 453301 [details] > Patch --- a/Source/WebCore/accessibility/AccessibilityObjectInterface.h +++ a/Source/WebCore/accessibility/AccessibilityObjectInterface.h - virtual AccessibilityObjectInclusion defaultObjectInclusion() const = 0; - virtual bool accessibilityIsIgnoredByDefault() const = 0; + virtual AccessibilityObjectInclusion defaultObjectInclusion(ConsiderModals = ConsiderModals::Yes) const = 0; + virtual bool accessibilityIsIgnoredByDefault(ConsiderModals = ConsiderModals::Yes) const = 0; I think this is the wrong implementation. If modal elements need to be special cased, it would be a lot cleaner to have a subclass of AccessibilityObject for them.
Tyler Wilcock
Comment 16 2022-02-27 12:45:00 PST
Created attachment 453353 [details] Alternate approach
chris fleizach
Comment 17 2022-02-27 13:29:03 PST
New approach looks better than the last one.
Andres Gonzalez
Comment 18 2022-02-28 05:42:20 PST
(In reply to Tyler Wilcock from comment #16) > Created attachment 453353 [details] > Alternate approach Yes, much better approach. --- a/LayoutTests/accessibility/ignore-modals-without-any-content.html +++ a/LayoutTests/accessibility/ignore-modals-without-any-content.html + async function findTextInsideId(id) { + const containerElement = accessibilityController.accessibleElementById(id); + testOutput += `\nBeginning search from #${id} element.\n`; + + let searchResult = null; + while (true) { + searchResult = containerElement.uiElementForSearchPredicate(searchResult, true, "AXAnyTypeSearchKey", "", false); + if (!searchResult) + break; + + if (searchResult.role.includes("StaticText")) { + testOutput += `\n${searchResult.role}`; + const textContent = accessibilityController.platformName === "ios" ? searchResult.description : searchResult.stringValue; + testOutput += `\n${textContent}\n`; + } + } + } + Do we need a while loop here?
Tyler Wilcock
Comment 19 2022-02-28 12:50:50 PST
EWS
Comment 20 2022-02-28 23:47:18 PST
Committed r290630 (247903@main): <https://commits.webkit.org/247903@main> All reviewed patches have been landed. Closing bug and clearing flags on attachment 453415 [details].
Note You need to log in before you can comment on or make changes to this bug.