Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: 507dba2fa32ad23cba904af464524b9d8c62af0d
      
https://github.com/WebKit/WebKit/commit/507dba2fa32ad23cba904af464524b9d8c62af0d
  Author: Tyler Wilcock <[email protected]>
  Date:   2026-08-10 (Mon, 10 Aug 2026)

  Changed paths:
    A LayoutTests/accessibility/append-and-remove-in-one-cycle-expected.txt
    A LayoutTests/accessibility/append-and-remove-in-one-cycle.html
    M Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp
    M Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.h

  Log Message:
  -----------
  AX: An append queued after a subtree removal can be incorrectly discarded as 
stale
https://bugs.webkit.org/show_bug.cgi?id=321357
rdar://184389421

Reviewed by Dominic Mazzoni.

A batch of pending changes carries appends and subtree removals in two separate
vectors, so the same AXID can appear in both, and the relative order of the two
decisions is not recoverable from the batch. removeStaleAppends has to tell a
leftover append (the object was appended, then genuinely deleted, so applying 
the
append would resurrect a dead object) apart from a reparent (the object was 
removed
from its old parent and appended under a new one, so the append must be kept), 
and
it inferred the order from parentage. Differing parents meant a move, matching
parents meant stale.

That inference is wrong when a node is removed from a parent and then 
legitimately
re-added under that same parent. The parents match, so the append is judged 
stale
and dropped, even though it is the newer decision. The object is then missing 
from
the tree while the main thread's node map still lists it as a child of that 
parent.

owner.setAttribute("aria-owns", "child");  // cycle 1: remove child from parent,
                                           //          append child under owner
owner.remove();                            // cycle 2: append child under 
natural parent again

Fix this by recording the order instead of inferring it. nextChangeSequence() 
creates a monotonic
counter onto NodeChange and NodeAndParentID at the point the main thread 
decides on
the append or removal, and removeStaleAppends now drops an append only when it 
was
decided before the removal that would invalidate it.

The test exercises the merged-batch case via aria-owns, which moves an object 
in the
accessibility tree without touching the render tree so the object keeps its 
AXID. A
DOM reparent cannot reach this code, as it destroys and recreates the renderer 
and the
new parent's append is therefore for a new AXID.

Note the test passes both with and without this change on trunk. Reaching the 
bug
requires two main-thread update cycles to land in one batch before the 
accessibility
thread applies it, which only happens deterministically with the 
browser-tick-based
batching atomic tree updates in https://bugs.webkit.org/show_bug.cgi?id=316148, 
where
this test does fail without this fix.

* LayoutTests/accessibility/append-and-remove-in-one-cycle-expected.txt: Added.
* LayoutTests/accessibility/append-and-remove-in-one-cycle.html: Added.
* Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp:
(WebCore::AXIsolatedTree::createEmptyContent):
(WebCore::AXIsolatedTree::nodeChangeForObject):
(WebCore::AXIsolatedTree::addUnconnectedNode):
(WebCore::AXIsolatedTree::resolveAppends):
(WebCore::AXIsolatedTree::collectNodeChangesForSubtree):
(WebCore::AXIsolatedTree::updateNode):
(WebCore::AXIsolatedTree::updateChildren):
(WebCore::AXIsolatedTree::removeNode):
(WebCore::AXIsolatedTree::removeStaleAppends):
(WebCore::AXIsolatedTree::processQueuedNodeUpdates):
* Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.h:
(WebCore::AXIsolatedTree::NodeChange::NodeChange):
(WebCore::AXIsolatedTree::nextChangeSequence):

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



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

Reply via email to