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