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

  Changed paths:
    M Source/WebCore/accessibility/AXObjectCache.cpp
    M Source/WebCore/accessibility/AXObjectCache.h
    M Source/WebCore/accessibility/AXObjectCacheInlines.h
    M Source/WebCore/accessibility/AXUtilities.cpp
    M Source/WebCore/accessibility/AXUtilities.h
    M Source/WebCore/accessibility/AccessibilityScrollView.cpp
    M Source/WebCore/accessibility/AccessibilityScrollView.h

  Log Message:
  -----------
  AX: Reduce per-call allocations and recomputation in the accessibility 
isIgnored() hot path
https://bugs.webkit.org/show_bug.cgi?id=315867
rdar://178267741

Reviewed by Dominic Mazzoni.

This patch features a few optimizations based on a sample taken from a
page that exercised isIgnored() a lot:

- ARIA role checks (AXListHelpers::isAccessibilityList, AXTableHelpers::
  isTableCellElement -> hasCellARIARole) allocated a lowercased copy of the role
  attribute on every comparison, via 
SpaceSplitString::spaceSplitStringContainsValue()
  with ShouldFoldCase::Yes -- and hasAnyRole() did so once per candidate role, 
so a
  single isTableCellElement() check could allocate four times. hasAnyRole() also
  heap-allocated a Vector for its candidate list on every call.

- AccessibilityScrollView::isRoot() recomputed a downcast plus a virtual 
document()
  comparison on every call, even though the answer is invariant for the object's
  lifetime. It is called from many hot paths (computeIsIgnored, parentObject,
  isARIAHidden, isWithinHiddenWebArea, elementRect, ...).

- AXObjectCache::getOrCreate(Widget&) was out-of-line, so even cache hits (the 
common
  case from parentObject()/containingWebArea()) paid a cross-function call 
before
  reaching the already-inline get().

This patch addresses each with no change in observable behavior:

- hasRole()/hasAnyRole() now lowercase the role value once via
  AtomString::convertToASCIILowercase() -- which returns *this without 
allocating
  when the string is already lowercase, the common case for ARIA roles -- and 
match
  with ShouldFoldCase::No. This is equivalent to the previous 
ShouldFoldCase::Yes
  path (which folded the haystack internally) since all callers pass lowercase
  values. hasAnyRole() takes a std::initializer_list<StringView> instead of a 
Vector,
  removing the per-call backing-store allocation. SpaceSplitString is left 
untouched.

- AccessibilityScrollView::isRoot() is memoized in a std::optional<bool>. The 
value
  is invariant for the object's lifetime: AXObjectCache::m_document is const, 
there is
  one cache per document under ACCESSIBILITY_LOCAL_FRAME (computeIsIgnored 
relies on
  this), and isMainFrame() is fixed for a given frame otherwise. The 
cache-is-null
  teardown case is intentionally not memoized.

- AXObjectCache::getOrCreate(Widget&) is now an inline fast path (get() +
  getOrCreateSlow(Widget&)), matching the existing getOrCreate(Node&)/(Element&)
  pattern in AXObjectCacheInlines.h.

These yield a 0.79% improvement on our Speedometer 3.1 score.

* Source/WebCore/accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::getOrCreateSlow):
* Source/WebCore/accessibility/AXObjectCache.h:
* Source/WebCore/accessibility/AXObjectCacheInlines.h:
(WebCore::AXObjectCache::getOrCreate):
* Source/WebCore/accessibility/AXUtilities.cpp:
(WebCore::hasRole):
(WebCore::hasAnyRole):
* Source/WebCore/accessibility/AXUtilities.h:
* Source/WebCore/accessibility/AccessibilityScrollView.cpp:
(WebCore::AccessibilityScrollView::isRoot const):
* Source/WebCore/accessibility/AccessibilityScrollView.h:

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



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

Reply via email to