Bug 240978

Summary: AX: WebKit does not trap user focus inside modals that have been DOM moved
Product: WebKit Reporter: Tyler Wilcock <tyler_w>
Component: AccessibilityAssignee: Tyler Wilcock <tyler_w>
Status: RESOLVED FIXED    
Severity: Normal CC: aboxhall, andresg_22, apinheiro, cfleizach, dmazzoni, ews-watchlist, giacomo.petri, jcraig, jdiggs, samuel_white, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Attachments:
Description Flags
Patch
ews-feeder: commit-queue-
Patch
none
Patch
none
Patch none

Description Tyler Wilcock 2022-05-26 14:46:26 PDT
WebKit does not trap user focus inside modals that have been DOM moved. This pattern is used in the WAI-ARIA aria-modal usage example: https://w3c.github.io/aria-practices/examples/dialog-modal/dialog.html
Comment 1 Radar WebKit Bug Importer 2022-05-26 14:46:38 PDT
<rdar://problem/93996539>
Comment 2 Tyler Wilcock 2022-05-26 15:12:04 PDT
Created attachment 459796 [details]
Patch
Comment 3 Tyler Wilcock 2022-05-26 15:35:52 PDT
Created attachment 459798 [details]
Patch
Comment 4 Tyler Wilcock 2022-06-01 14:10:02 PDT
Created attachment 459933 [details]
Patch
Comment 5 Andres Gonzalez 2022-06-02 08:01:07 PDT
(In reply to Tyler Wilcock from comment #4)
> Created attachment 459933 [details]
> Patch

--- a/Source/WebCore/accessibility/AXObjectCache.cpp
+++ a/Source/WebCore/accessibility/AXObjectCache.cpp

-Element* AXObjectCache::currentModalNode()
+Element* AXObjectCache::recomputeCurrentModalNode()

Wouldn't it make more sense to name it updateCurrentModalNode or updateCurrentModalElement and have a void return? Since in most calls the returned value is not used. We have another method named modalNode that can do the update if needed and return the stored pointer.

+{
+    auto* previousModal = m_currentModalElement.get();
+    m_currentModalElement = recomputeCurrentModalNodeInner();
+    if (previousModal != m_currentModalElement.get()) {
+        childrenChanged(rootWebArea());
+#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
+        // Because the presence of a modal affects every element on the page,
+        // regenerate the entire isolated tree with the next cache update.
+        m_deferredRegenerateIsolatedTree = true;
+#endif

Would it be more appropriate to do the update of the isolated tree in the childrenChanged or handleChildrenChanged as everything else? Perhaps we need to add an AXNotification for a new modal and update the isolated tree upon that notification.

+Element* AXObjectCache::recomputeCurrentModalNodeInner()

See comment above about update versus recompute prefix. I think Internal is more customary than Inner as a suffix for a private method name that is invoked by another possibly public method, but I don't feel strongly either way.

--- a/Source/WebCore/accessibility/AXObjectCache.h
+++ a/Source/WebCore/accessibility/AXObjectCache.h

-    void handleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode);
+    enum class RecomputeModal : bool { No, Yes };
+    void handleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode, RecomputeModal = RecomputeModal::Yes);

RecomputeModal -> UpdateModal ?

+#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
+    bool m_deferredRegenerateIsolatedTree { false };
+#endif

If we handle this as an AXNotification as the rest of all isolated tree updates, I don't think we would need this.
Comment 6 Tyler Wilcock 2022-06-03 10:32:25 PDT
Created attachment 460016 [details]
Patch
Comment 7 EWS 2022-06-07 14:45:34 PDT
Committed r295366 (251374@main): <https://commits.webkit.org/251374@main>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 460016 [details].
Comment 8 Giacomo Petri 2022-06-13 00:14:11 PDT
Hi,

there is still a bug on Safari mobile + VO. I'm not sure if the issue is related to this topic or more to this one https://bugs.webkit.org/show_bug.cgi?id=239295, but on mobile users are still not trapped within the modal.

Giacomo