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

  Changed paths:
    M Source/WebCore/accessibility/AXCoreObject.cpp
    M Source/WebCore/accessibility/AXCoreObject.h
    M Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.cpp
    M Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.h
    M Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp
    M Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.h
    M Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm

  Log Message:
  -----------
  AX: Cache unignored children on AXIsolatedTree to eliminate redundant subtree 
walks
https://bugs.webkit.org/show_bug.cgi?id=314655
rdar://176904705

Reviewed by Dominic Mazzoni.

Cache stitchedUnignoredChildren() results in a HashMap<AXID, 
Vector<Ref<AXCoreObject>>>
owned by AXIsolatedTree. The cache is populated lazily on first access and 
cleared
conditionally in applyPendingChangesFromSnapshot when the incoming snapshot 
carries a
structural change (appends, subtreeRemovals, childrenUpdates, parentUpdates) or 
a property
update that affects the unignored-children walk (IsIgnored, IsExposableTable, 
StitchGroups).

Based on logging I added while testing this change, the hit rate is extremely 
good:
75% on one webpage, 90% on another.

Computing unignored children is by far our most expensive operation, made worse 
by the introduction
of text stitching which adds more work on top, so saving work here is extremely 
valuable.
On a large GitHub page, holding VO-Right to move to a pre-defined spot in the 
middle of the page took
1min 11s before this change, and 18 seconds faster (a 3.94x speedup).

This is especially beneficial because within the scope of a single AppKit 
selector, AppKit may call
multiple methods on our wrapper that used to each compute the unignored 
children from scratch.

Additional optimizations built on the cache:

  - stitchedUnignoredChildrenCount(): returns count from cache without copying 
the vector.

  - cachedStitchedUnignoredChildren(): returns a pointer to the cached vector 
for callers
    that can iterate without copying (accessibilityIndexOfChild, 
handleRowsAttribute,
    handleVisibleRowsAttribute, hasUnignoredChild).

  - crossFrameUnignoredChildrenInRange(): builds only the requested subrange 
from the cache
    with per-element cross-frame patching, used by 
_accessibilityChildrenFromIndex:. This
    used to build the entire children and then pick the subrange.

  - accessibilityArrayAttributeCount: now handles AXChildrenInNavigationOrder 
alongside
    AXChildren, avoiding a fallthrough to AppKit's default implementation that 
rebuilds
    the full children NSArray just to count it. This showed up in samples.

* Source/WebCore/accessibility/AXCoreObject.cpp:
(WebCore::AXCoreObject::tabChildren):
(WebCore::AXCoreObject::unignoredChildren):
(WebCore::AXCoreObject::hasUnignoredChild):
(WebCore::AXCoreObject::stitchedUnignoredChildrenCount):
(WebCore::AXCoreObject::crossFrameUnignoredChildrenInRange):
(WebCore::AXCoreObject::selectedRadioButton):
(WebCore::AXCoreObject::selectedTabItem):
(WebCore::AXCoreObject::selectedChildren):
(WebCore::AXCoreObject::listboxSelectedChildren):
(WebCore::AXCoreObject::selectedListItems):
(WebCore::AXCoreObject::ariaTreeRows):
(WebCore::AXCoreObject::columnHeader):
(WebCore::AXCoreObject::rowHeader):
(WebCore::AXCoreObject::appendRadioButtonDescendants const):
* Source/WebCore/accessibility/AXCoreObject.h:
(WebCore::AXCoreObject::cachedStitchedUnignoredChildren):
(WebCore::AXCoreObject::unignoredChildrenFromCacheIfAvailable):
* Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.cpp:
(WebCore::AXIsolatedObject::ensureCachedStitchedUnignoredChildren):
(WebCore::AXIsolatedObject::stitchedUnignoredChildren):
(WebCore::AXIsolatedObject::stitchedUnignoredChildrenCount):
(WebCore::AXIsolatedObject::cachedStitchedUnignoredChildren):
(WebCore::AXIsolatedObject::crossFrameUnignoredChildrenInRange):
(WebCore::AXIsolatedObject::approximateHitTest const):
(WebCore::AXIsolatedObject::relativeFrame const):
(WebCore::AXIsolatedObject::relativeFrameFromChildren const):
(WebCore::AXIsolatedObject::tableHeaderContainer):
* Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.h:
* Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp:
(WebCore::AXIsolatedTree::applyPendingChangesFromSnapshot):
(WebCore::AXIsolatedTree::lastMarker):
* Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.h:
(WebCore::AXIsolatedTree::cachedStitchedUnignoredChildren):
* Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
(handleRowsAttribute):
(handleVisibleRowsAttribute):
(-[WebAccessibilityObjectWrapper accessibilityIndexOfChild:]):
(-[WebAccessibilityObjectWrapper accessibilityArrayAttributeCount:]):
(-[WebAccessibilityObjectWrapper 
_accessibilityChildrenFromIndex:maxCount:returnPlatformElements:]):

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



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

Reply via email to