Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: e30fb3e304f3ec7eefa43a791d52f3ee2bc91676
      
https://github.com/WebKit/WebKit/commit/e30fb3e304f3ec7eefa43a791d52f3ee2bc91676
  Author: Tyler Wilcock <[email protected]>
  Date:   2026-05-14 (Thu, 14 May 2026)

  Changed paths:
    A 
LayoutTests/accessibility/dynamic-subtree-replacement-with-stale-parent-children-expected.txt
    A 
LayoutTests/accessibility/dynamic-subtree-replacement-with-stale-parent-children.html
    M Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp

  Log Message:
  -----------
  AX: nodeChangeForObject overwrites childrenIDs in the nodeMap, sometimes 
causing content to be missing in the isolated tree
https://bugs.webkit.org/show_bug.cgi?id=314773
rdar://176891627

Reviewed by Dominic Mazzoni.

With this sequence, content can become missing from the isolated tree:

  1. An attribute change (e.g. role) on parent X is posted as a notification
     into AXObjectCache's m_notificationsToPost, which schedules
     notificationPostTimer.

  2. X's children are dynamically replaced (innerHTML, etc.), so live
     children differ from what's stored in m_nodeMap[X].childrenIDs. The
     children-changed events sit in AXObjectCache's 
m_deferredChildrenChangedList
     waiting for performDeferredCacheUpdate to drain them into
     AXIsolatedTree's m_needsUpdateChildren.

  3. performCacheUpdate fires before notificationPostTimer, but bails
     because layout is dirty. m_deferredChildrenChangedList stays untouched,
     so AXIsolatedTree's m_needsUpdateChildren stays empty for X.

  4. notificationPostTimer fires. It (a) queues a full node update for X
     into m_needsUpdateNode via updateIsolatedTree, and (b) calls
     processQueuedIsolatedNodeUpdates synchronously inside
     postPlatformNotification (so the AT sees an up-to-date tree when
     it responds to the notification).

  5. processQueuedNodeUpdates runs with m_needsUpdateNode={X} and
     m_needsUpdateChildren={} (still empty from step 3). It calls
     resolveAppends -> nodeChangeForObject(X), which sees live children
     = NEW and stored children = OLD, and overwrites m_nodeMap[X].childrenIDs
     with the NEW IDs without registering any of them in m_nodeMap.

  6. Later, performCacheUpdate runs successfully and queues
     updateChildren(X) into m_needsUpdateChildren. The snapshot timer
     fires processQueuedNodeUpdates, which calls updateChildren(X).
     It reads oldChildrenIDs from the polluted m_nodeMap (= NEW IDs)
     and compares against newChildrenIDs from live (= the same NEW IDs).
     old == new, so it never calls collectNodeChangesForSubtree on the
     new children, and thus they never get isolated objects created for them.

  7. AXIsolatedObject::children resolves the stored childrenIDs against
     tree().objectForID(...). The unregistered NEW IDs return null and
     get silently dropped via WTF::compactMap, leaving them invisible
     to AT clients.

Fix this in the same way we fixed a similar problem for 
collectNodeChangesForSubtree
in https://commits.webkit.org/285352@main. If the object is already in the 
nodemap,
we leave the existing childrenIDs alone. Arguably we should do this even if the
object isn't in the nodemap, but rethinking this is saved for a later commit 
since
this change alone fixes the bug on the webpage.

* 
LayoutTests/accessibility/dynamic-subtree-replacement-with-stale-parent-children-expected.txt:
 Added.
* 
LayoutTests/accessibility/dynamic-subtree-replacement-with-stale-parent-children.html:
 Added.
* Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp:
(WebCore::AXIsolatedTree::nodeChangeForObject):

Canonical link: https://commits.webkit.org/313241@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications

Reply via email to