blob: 0e369c0e034c7e46c7f1333f5bf197bc0e33acd1 [file]
/*
* Copyright (C) 2003-2025 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <WebCore/AXAnnouncementTypes.h>
#include <WebCore/AXObjectTypes.h>
#include <WebCore/AXTextStateChangeIntent.h>
#include <WebCore/AXTreeStore.h>
#include <WebCore/AccessibilityObject.h>
#include <WebCore/AccessibilityRemoteToken.h>
#include <WebCore/SimpleRange.h>
#include <WebCore/StyleChange.h>
#include <WebCore/Timer.h>
#include <WebCore/VisibleUnits.h>
#include <wtf/CheckedRef.h>
#include <wtf/Deque.h>
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
#include <wtf/ListHashSet.h>
#include <wtf/WeakHashMap.h>
#include <wtf/WeakHashSet.h>
#include <wtf/WeakListHashSet.h>
#if PLATFORM(COCOA)
#include <WebCore/AttributedString.h>
#include <wtf/RetainPtr.h>
#endif
OBJC_CLASS NSMutableArray;
OBJC_CLASS NSString;
namespace WTF {
class TextStream;
}
namespace WebCore {
class AXComputedObjectAttributeCache;
class AXGeometryManager;
class AXIsolatedTree;
class AXLiveRegionManager;
class AXRemoteFrame;
class AccessibilityNodeObject;
class AccessibilityObject;
class AccessibilityRenderObject;
class AccessibilitySpinButton;
class Document;
class HTMLAreaElement;
class HTMLCanvasElement;
class HTMLDetailsElement;
class HTMLMediaElement;
class HTMLSelectElement;
class HTMLTableElement;
class HTMLTextFormControlElement;
class Node;
class Page;
class RemoteFrame;
class RenderBlock;
class RenderBlockFlow;
class RenderImage;
class AXTextMarker;
namespace Style {
class ComputedStyle;
struct Difference;
}
class RenderObject;
class RenderText;
class RenderWidget;
class Scrollbar;
class ScrollView;
class SpinButtonElement;
class VisiblePosition;
class Widget;
struct AXTextStateChangeIntent;
struct AriaNotifyOptions;
struct TextMarkerData;
enum class AXNotification : uint8_t;
enum class AXStreamOptions : uint16_t;
enum class AXProperty : uint16_t;
enum class LiveRegionStatus: uint8_t;
struct AXTreeData {
String liveTree;
String isolatedTree;
// Captures warnings about the tree state that could result in poor user-facing
// behavior. These are gathered while capturing the structural tree data stored
// in the other member variables.
Vector<String> warnings;
void dumpToStderr() const
{
for (const String& warning : warnings)
SAFE_FPRINTF(stderr, "WARNING: %s\n", warning.utf8());
SAFE_FPRINTF(stderr, "==AX Trees (PID %d)==\n%s\n%s\n", getpid(), liveTree.utf8(), isolatedTree.utf8());
}
};
// When this is updated, WebCoreArgumentCoders.serialization.in must be updated as well.
struct AXDebugInfo {
AccessibilityMode accessibilityMode;
String liveTree;
String isolatedTree;
Vector<String> warnings;
uint64_t remoteTokenHash;
uint64_t webProcessLocalTokenHash;
};
// When this is updated, WebCoreArgumentCoders.serialization.in must be updated as well.
struct AriaNotifyData {
String message;
NotifyPriority priority { NotifyPriority::Normal };
InterruptBehavior interrupt { InterruptBehavior::None };
String language;
String debugDescription() const
{
auto priorityString = [&] {
switch (priority) {
case NotifyPriority::Normal:
return "normal"_s;
case NotifyPriority::High:
return "high"_s;
}
return "unknown"_s;
};
auto interruptString = [&] {
switch (interrupt) {
case InterruptBehavior::None:
return "none"_s;
case InterruptBehavior::All:
return "all"_s;
case InterruptBehavior::Pending:
return "pending"_s;
}
return "unknown"_s;
};
return makeString("AriaNotifyData { message: \""_s, message, "\", priority: "_s, priorityString(), ", interrupt: "_s, interruptString(), ", language: \""_s, language, "\" }"_s);
}
};
#if PLATFORM(COCOA)
// When this is updated, WebCoreArgumentCoders.serialization.in must be updated as well.
struct LiveRegionAnnouncementData {
AttributedString message;
LiveRegionStatus status { LiveRegionStatus::Polite };
String debugDescription() const
{
auto statusString = [&] {
switch (status) {
case LiveRegionStatus::Off:
return "off"_s;
case LiveRegionStatus::Polite:
return "polite"_s;
case LiveRegionStatus::Assertive:
return "assertive"_s;
}
return "unknown"_s;
};
return makeString("LiveRegionAnnouncementData { message: \""_s, message.string, "\", status: "_s, statusString(), " }"_s);
}
};
struct AXTextChangeContext {
AXTextStateChangeIntent intent;
String deletedText;
String insertedText;
VisibleSelection selection;
};
#endif // PLATFORM(COCOA)
#if ENABLE(ACCESSIBILITY_LOCAL_FRAME)
// When this is updated, WebCoreArgumentCoders.serialization.in must be updated as well.
// Describes a frame's position and scale on screen for accessibility coordinate conversion.
// Sent from the UIProcess to the WebProcess via IPC whenever the frame scrolls, moves, or resizes.
// When this is updated, WebCoreArgumentCoders.serialization.in must be updated as well.
struct AXFrameGeometry {
// The frame's content origin in screen coordinates.
// - Coordinate space: bottom-left on macOS, top-left on other platforms.
// - Points to: the top-left of the frame's document.
// - Units: display pixels.
// - Scroll: document-origin-based, so accounts for the frame's scroll position
// (e.g. scrolling down moves the document origin up on screen).
// Element rects in content space compose with this directly to produce screen coordinates.
IntPoint screenPosition;
// Scale accounts for page zoom and device scale factor, among other things.
// Applied to the element rect before adding screenPosition.
AffineTransform screenTransform;
};
#endif
struct AXNotificationWithData {
using DataVariant = Variant<std::monostate, AriaNotifyData
#if PLATFORM(COCOA)
, LiveRegionAnnouncementData
#endif
>;
AXNotification notification;
DataVariant data;
AXNotificationWithData(AXNotification notification)
: notification(notification)
, data(std::monostate { }) { }
AXNotificationWithData(AXNotification notification, const AriaNotifyData& data)
: notification(notification), data(data) { }
#if PLATFORM(COCOA)
AXNotificationWithData(AXNotification notification, const LiveRegionAnnouncementData& data)
: notification(notification), data(data) { }
#endif
String debugDescription() const;
};
class AccessibilityReplacedText {
public:
AccessibilityReplacedText() = default;
AccessibilityReplacedText(const VisibleSelection&);
void postTextStateChangeNotification(AXObjectCache*, AXTextEditType, const String&, const VisibleSelection&);
const VisiblePositionIndexRange& replacedRange() LIFETIME_BOUND { return m_replacedRange; }
protected:
String m_replacedText;
VisiblePositionIndexRange m_replacedRange;
};
enum class AXLoadingEvent : uint8_t {
Started,
Reloaded,
Failed,
Finished
};
#if !PLATFORM(COCOA)
enum class AXTextChange : uint8_t { Inserted, Deleted, Replaced, AttributesChanged };
#endif
enum class PostTarget { Element, ObservableParent };
struct DeferredNotificationData {
// The renderer or element to post a notification for.
SingleThreadWeakPtr<RenderObject> renderer { nullptr };
WeakPtr<Element, WeakPtrImplWithEventTargetData> element { nullptr };
// The notification to post.
AXNotification notification;
DeferredNotificationData() = delete;
explicit DeferredNotificationData(RenderObject& renderer, AXNotification notification)
: renderer(renderer)
, notification(notification)
{ }
};
DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(AXObjectCache);
class AXObjectCache final : public CanMakeWeakPtr<AXObjectCache>, public CanMakeCheckedPtr<AXObjectCache>
, public AXTreeStore<AXObjectCache> {
WTF_MAKE_NONCOPYABLE(AXObjectCache);
WTF_MAKE_TZONE_ALLOCATED(AXObjectCache);
WTF_OVERRIDE_DELETE_FOR_CHECKED_PTR(AXObjectCache);
friend class AXIsolatedTree;
friend class AXTextMarker;
friend WTF::TextStream& operator<<(WTF::TextStream&, AXObjectCache&);
public:
explicit AXObjectCache(LocalFrame&, Document*);
~AXObjectCache();
String debugDescription() const;
// Returns the root object for a specific frame.
WEBCORE_EXPORT AXCoreObject* rootObjectForFrame(LocalFrame&);
AccessibilityObject* rootWebArea();
#if ENABLE(ACCESSIBILITY_LOCAL_FRAME)
WEBCORE_EXPORT void setFrameInheritedState(LocalFrame&, const InheritedFrameState&);
WEBCORE_EXPORT void setFrameGeometry(LocalFrame&, const AXFrameGeometry&);
const std::optional<AXFrameGeometry>& frameGeometry() const LIFETIME_BOUND { return m_frameGeometry; }
// The scroll value that was implicitly baked into the latest frameGeometry().screenPosition.
IntPoint frameViewOriginScrollPosition() const { return m_frameViewOriginScrollPosition; }
const std::optional<AXFrameGeometry>& getAndUpdateFrameGeometry() LIFETIME_BOUND;
#endif
// Creation/retrieval of AX objects associated with a DOM or RenderTree object.
inline AccessibilityObject* getOrCreate(RenderObject* renderer)
{
return renderer ? getOrCreate(*renderer) : nullptr;
}
AccessibilityObject* getOrCreate(RenderObject&);
inline AccessibilityObject* getOrCreate(Widget* widget)
{
return widget ? getOrCreate(*widget) : nullptr;
}
AccessibilityObject* getOrCreate(Widget&);
enum class IsPartOfRelation : bool { No, Yes };
inline AccessibilityObject* getOrCreate(Node* node, IsPartOfRelation isPartOfRelation = IsPartOfRelation::No)
{
return node ? getOrCreate(*node, isPartOfRelation) : nullptr;
}
AccessibilityObject* getOrCreate(Node&, IsPartOfRelation = IsPartOfRelation::No);
AccessibilityObject* getOrCreate(Element&, IsPartOfRelation = IsPartOfRelation::No);
// Out-of-line implementations (necessary for use outside of WebCore). Our inlined
// implementations (important for performance inside WebCore) cannot be exported.
WEBCORE_EXPORT AccessibilityObject* exportedGetOrCreate(Node&);
WEBCORE_EXPORT AccessibilityObject* exportedGetOrCreate(Node*);
// used for objects without backing elements
AccessibilityObject* create(AccessibilityRole);
Ref<AccessibilitySpinButton> createSpinButton(SpinButtonElement&);
// Will only return the AccessibilityObject if it already exists.
inline AccessibilityObject* get(RenderObject* renderer) const
{
return renderer ? get(*renderer) : nullptr;
}
inline AccessibilityObject* get(RenderObject& renderer) const
{
std::optional axID = getAXID(renderer);
return axID ? m_objects.get(*axID) : nullptr;
}
inline AccessibilityObject* get(Widget* widget) const
{
return widget ? get(*widget) : nullptr;
}
inline AccessibilityObject* get(Widget& widget) const
{
auto axID = m_widgetIdMapping.getOptional(widget);
return axID ? m_objects.get(*axID) : nullptr;
}
inline AccessibilityObject* get(Node* node) const
{
return node ? get(*node) : nullptr;
}
AccessibilityObject* get(Node&) const;
inline AccessibilityObject* get(Element& element) const
{
return m_nodeObjectMapping.get(element);
}
std::optional<AXID> getAXID(RenderObject&) const;
void remove(RenderObject&);
void remove(Node&);
void remove(Widget&);
void remove(std::optional<AXID> axID)
{
if (axID)
remove(*axID);
}
void remove(AXID);
#if !PLATFORM(COCOA) && !USE(ATSPI)
void detachWrapper(AXCoreObject*, AccessibilityDetachmentType);
#endif
private:
using DOMObjectVariant = Variant<std::nullptr_t, RenderObject*, Node*, Widget*>;
enum class ShouldAttachWrapper : bool { No, Yes };
void cacheAndInitializeWrapper(AccessibilityObject&, DOMObjectVariant = nullptr, ShouldAttachWrapper = ShouldAttachWrapper::Yes);
void attachWrapper(AccessibilityObject&);
AccessibilityObject* getOrCreateSlow(Node&, IsPartOfRelation);
AccessibilityObject* getOrCreateSlow(Widget&);
#if ENABLE(ACCESSIBILITY_LOCAL_FRAME)
RefPtr<AccessibilityScrollView> scrollViewForFrame(LocalFrame&);
#endif
public:
void onPageActivityStateChange(OptionSet<ActivityState>);
void setPageActivityState(OptionSet<ActivityState> state) { m_pageActivityState = state; }
OptionSet<ActivityState> pageActivityState() const { return m_pageActivityState; }
#if ENABLE(WRITING_TOOLS)
WEBCORE_EXPORT void setWritingToolsAvailable(bool);
#endif // ENABLE(WRITING_TOOLS)
inline void childrenChanged(Node& node)
{
if (!node.renderer()) {
// We only need to handle DOM changes for things that don't have renderers.
// If something does have a renderer, we would already get children-changed notifications
// from the render tree.
childrenChanged(RefPtr { get(node) }.get());
} else if (node.renderer()->isRenderHTMLCanvas()) {
// Canvas fallback content (its DOM children) is not rendered, so we won't receive
// children-changed notifications from the render tree when those children change.
childrenChanged(RefPtr { get(node) }.get());
}
}
void childrenChanged(RenderObject&, RenderObject* newChild = nullptr);
void childrenChanged(AccessibilityObject*);
void onDetailsSummarySlotChange(const HTMLDetailsElement&);
void onDragElementChanged(Element* oldElement, Element* newElement);
void onDraggingStarted(Element&);
void onDraggingEnded(Element&);
void onDraggingEnteredDropZone(Element&);
void onDraggingExitedDropZone(Element&);
void onDraggingDropped(Element&);
void onEventListenerAdded(Node&, const AtomString& eventType);
void onEventListenerRemoved(Node&, const AtomString& eventType);
void onFocusChange(Element* oldElement, Element* newElement);
void onFrameSelectionFocusedOrActiveStateChanged(Document&);
void onInertOrVisibilityChange(RenderElement&);
#if ENABLE(VIDEO)
void onMediaElementCurrentSrcChanged(HTMLMediaElement&);
#endif
void onPopoverToggle(const HTMLElement&);
void onRadioGroupMembershipChanged(HTMLElement&);
void onRemoteFrameGainedFocus(RemoteFrame&);
void onScrollbarFrameRectChange(const Scrollbar&);
void onSelectedOptionChanged(Element&);
void onSelectedOptionChanged(HTMLSelectElement&, int optionIndex);
void onSelectedTextChanged(const VisiblePositionRange&, AccessibilityObject* = nullptr);
void onSlottedContentChange(const HTMLSlotElement&);
void onStyleChange(Element&, OptionSet<Style::Change>, const Style::ComputedStyle* oldStyle, const Style::ComputedStyle* newStyle);
void onStyleChange(RenderText&, Style::Difference, const Style::ComputedStyle* oldStyle, const Style::ComputedStyle& newStyle);
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
void onAccessibilityPaintStarted();
void onAccessibilityPaintFinished();
// Returns true if the font changes, requiring all descendants to update the Font property.
bool onFontChange(Element&, const Style::ComputedStyle*, const Style::ComputedStyle*);
// Returns true if the text color changes, requiring all descendants to update the TextColor property.
bool onTextColorChange(Element&, const Style::ComputedStyle*, const Style::ComputedStyle*);
#endif // ENABLE(ACCESSIBILITY_ISOLATED_TREE)
void onTextSecurityChanged(HTMLInputElement&);
void onTitleChange(Document&);
void onValidityChange(Element&);
void onTextCompositionChange(Node&, CompositionState, bool, const String&, size_t, bool);
void onWidgetVisibilityChanged(RenderWidget&);
void onPostRenderingUpdate();
void valueChanged(Element&);
void checkedStateChanged(Element&);
void autofillTypeChanged(HTMLInputElement&);
void handleRoleChanged(AccessibilityObject&, AccessibilityRole previousRole);
void handleReferenceTargetChanged();
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
void columnIndexChanged(AccessibilityObject&);
void rowIndexChanged(AccessibilityObject&);
#endif
void onRendererCreated(Node&);
#if PLATFORM(MAC)
void onDocumentRenderTreeCreation(const Document&);
#endif
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
void onTextRunsChanged(const RenderObject&);
#endif
void onLaidOutInlineContent(const RenderBlockFlow&);
const Vector<AXStitchGroup>* stitchGroupsOwnedBy(AccessibilityObject&);
void updateLoadingProgress(double);
void loadingFinished() { updateLoadingProgress(1); }
double loadingProgress() const { return m_loadingProgress; }
void onTopDocumentLoaded(RenderObject&);
void onNonTopDocumentLoaded(RenderObject&);
void handlePageEditibilityChanged(Document&);
void onAutocorrectionOccured(Element&);
void onEditableTextValueChanged(Node&);
void onDocumentInitialFocus(Node&);
void onLayoutComplete(RenderObject&);
struct AttributeChange {
WeakPtr<Element, WeakPtrImplWithEventTargetData> element { nullptr };
QualifiedName attrName;
AtomString oldValue;
AtomString newValue;
};
struct DeferredRemoteFrameFocus {
WeakPtr<RemoteFrame> remoteFrame;
WeakPtr<Element, WeakPtrImplWithEventTargetData> oldFocusedElement;
};
struct CanvasFocusPathBoundsChange {
// The canvas fallback element whose focus path bounds were set by
// CanvasRenderingContext2D::drawFocusIfNeeded().
WeakPtr<Element, WeakPtrImplWithEventTargetData> fallbackElement;
WeakPtr<HTMLCanvasElement, WeakPtrImplWithEventTargetData> canvas;
FloatRect bounds;
};
using DeferredCollection = Variant<HashMap<Element*, String>
, HashSet<AXID>
, ListHashSet<Node*>
, ListHashSet<Ref<AccessibilityObject>>
, Vector<AttributeChange>
, Vector<CanvasFocusPathBoundsChange>
, Vector<std::pair<Node*, Node*>>
, WeakHashSet<Element, WeakPtrImplWithEventTargetData>
, WeakHashSet<HTMLTableElement, WeakPtrImplWithEventTargetData>
, WeakHashSet<AccessibilityObject>
, WeakHashSet<AccessibilityNodeObject>
, WeakListHashSet<Node, WeakPtrImplWithEventTargetData>
, WeakListHashSet<Element, WeakPtrImplWithEventTargetData>
, WeakHashMap<Element, String, WeakPtrImplWithEventTargetData>>;
void deferFocusedUIElementChangeIfNeeded(Node* oldFocusedNode, Node* newFocusedNode);
void deferModalChange(Element&);
void deferMenuListValueChange(Element*);
void deferElementAddedOrRemoved(Element*);
void NODELETE handleScrolledToAnchor(const Node& anchorNode);
void onScrollbarUpdate(ScrollView&);
void onRemoteFrameInitialized(AXRemoteFrame&);
bool isRetrievingCurrentModalNode() { return m_isRetrievingCurrentModalNode; }
Node* modalNode();
void deferAttributeChangeIfNeeded(Element&, const QualifiedName&, const AtomString&, const AtomString&);
// True for the attributes in relationAttributes() (aria-labelledby, aria-owns, etc.).
static bool isRelationAttribute(const QualifiedName&);
// Records that an element carries a relation attribute so the next relations rebuild includes it.
void trackRelationAttributeElement(Element&);
void recomputeIsIgnored(RenderObject&);
void recomputeIsIgnored(Node*);
// What must hold before this process will reach AXThread mode.
enum class AXThreadModePreconditions : uint8_t {
// A current accessibility client that we support isolated trees for, and this process's
// isolated-tree setting.
RequireClientAndSetting,
// Only the setting. For transitions driven by IPC rather than by an accessibility request,
// where there is no current client to ask about.
RequireSettingOnly,
None
};
static void enableAccessibility(AXThreadModePreconditions = AXThreadModePreconditions::RequireClientAndSetting);
// Resets mode to Off without notifying the UI process, so the change
// stays local to this web process. Used by Internals::resetToConsistentState
// to avoid interfering with other web content processes running tests.
WEBCORE_EXPORT static void disableAccessibilityForTesting();
WEBCORE_EXPORT static std::optional<AccessibilityMode> attemptMainThreadModeTransition();
WEBCORE_EXPORT static std::atomic<AccessibilityMode> gAccessibilityMode;
static AccessibilityMode accessibilityMode() { return gAccessibilityMode.load(std::memory_order_relaxed); }
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
WEBCORE_EXPORT static std::optional<AccessibilityMode> transitionToAXThreadModeIfNeeded(AXThreadModePreconditions = AXThreadModePreconditions::RequireClientAndSetting);
WEBCORE_EXPORT static bool shouldForceAccessibilityEnabled();
#endif // ENABLE(ACCESSIBILITY_ISOLATED_TREE)
using SyncModeToOtherProcessesCallback = Function<void(AccessibilityMode)>;
WEBCORE_EXPORT static void setSyncModeToOtherProcessesCallback(SyncModeToOtherProcessesCallback&&);
#if PLATFORM(MAC)
WEBCORE_EXPORT static bool isAppleInternalInstall();
#endif
static bool forceDeferredSpellChecking();
static void setForceDeferredSpellChecking(bool);
#if PLATFORM(MAC)
static bool shouldSpellCheck();
#else
static bool shouldSpellCheck() { return true; }
#endif
WEBCORE_EXPORT AccessibilityObject* focusedObjectForPage(const Page*);
WEBCORE_EXPORT AccessibilityObject* focusedObjectForLocalFrame();
// Enhanced user interface accessibility can be toggled by the assistive technology.
WEBCORE_EXPORT static void setEnhancedUserInterfaceAccessibility(bool flag);
static bool accessibilityEnabled();
WEBCORE_EXPORT static bool NODELETE accessibilityEnhancedUserInterfaceEnabled();
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
static bool useAXThreadTextApis() { return !isMainThread(); }
static bool shouldCreateAXThreadCompatibleMarkers() { return isIsolatedTreeEnabled(); }
#endif
static bool isAXTextStitchingEnabled() { return gAccessibilityTextStitchingEnabled; }
WEBCORE_EXPORT static bool NODELETE isAXThreadHitTestingEnabled();
#if PLATFORM(COCOA)
static bool shouldRepostNotificationsForTests() { return gShouldRepostNotificationsForTests; }
static void initializeUserDefaultValues();
static bool accessibilityDOMIdentifiersEnabled() { return gAccessibilityDOMIdentifiersEnabled; }
#endif
static bool forceInitialFrameCaching() { return gForceInitialFrameCaching; }
WEBCORE_EXPORT static void NODELETE setForceInitialFrameCaching(bool);
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
static bool shouldServeInitialCachedFrame();
#endif
const Element* rootAXEditableElement(const Node*);
bool elementIsTextControl(const Element&);
AccessibilityObject* objectForID(const AXID id) const { return m_objects.get(id); }
template<typename U> Vector<Ref<AXCoreObject>> objectsForIDs(const U&) const;
Node* nodeForID(std::optional<AXID>) const;
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
void onPaint(const RenderObject&, IntRect&&) const;
void onPaint(const Widget&, IntRect&&) const;
void onPaint(const RenderText&, size_t lineIndex);
#else
NO_RETURN_DUE_TO_ASSERT void onPaint(const RenderObject&, IntRect&&) const { ASSERT_NOT_REACHED(); }
NO_RETURN_DUE_TO_ASSERT void onPaint(const Widget&, IntRect&&) const { ASSERT_NOT_REACHED(); }
NO_RETURN_DUE_TO_ASSERT void onPaint(const RenderText&, size_t) { ASSERT_NOT_REACHED(); }
#endif
void deferCanvasFocusPathBoundsUpdate(Element& canvasFallbackElement, HTMLCanvasElement&, FloatRect bounds);
std::optional<IntRect> cachedBoundsForID(AXID) const;
// Text marker utilities.
std::optional<TextMarkerData> textMarkerDataForVisiblePosition(const VisiblePosition&, TextMarkerOrigin = static_cast<TextMarkerOrigin>(0));
TextMarkerData textMarkerDataForCharacterOffset(const CharacterOffset&, TextMarkerOrigin = static_cast<TextMarkerOrigin>(0));
TextMarkerData textMarkerDataForNextCharacterOffset(const CharacterOffset&);
AXTextMarker nextTextMarker(const AXTextMarker&);
TextMarkerData textMarkerDataForPreviousCharacterOffset(const CharacterOffset&);
AXTextMarker previousTextMarker(const AXTextMarker&);
VisiblePosition visiblePositionForTextMarkerData(const TextMarkerData&);
CharacterOffset characterOffsetForTextMarkerData(const TextMarkerData&);
// Use ignoreNextNodeStart/ignorePreviousNodeEnd to determine the behavior when we are at node boundary.
CharacterOffset nextCharacterOffset(const CharacterOffset&, bool ignoreNextNodeStart = true);
CharacterOffset previousCharacterOffset(const CharacterOffset&, bool ignorePreviousNodeEnd = true);
TextMarkerData startOrEndTextMarkerDataForRange(const SimpleRange&, bool);
CharacterOffset startOrEndCharacterOffsetForRange(const SimpleRange&, bool, bool enterTextControls = false);
AccessibilityObject* objectForTextMarkerData(const TextMarkerData&);
std::optional<SimpleRange> rangeForUnorderedCharacterOffsets(const CharacterOffset&, const CharacterOffset&);
static SimpleRange rangeForNodeContents(Node&);
static unsigned lengthForRange(const SimpleRange&);
// Word boundary
CharacterOffset nextWordEndCharacterOffset(const CharacterOffset&);
CharacterOffset previousWordStartCharacterOffset(const CharacterOffset&);
std::optional<SimpleRange> leftWordRange(const CharacterOffset&);
std::optional<SimpleRange> rightWordRange(const CharacterOffset&);
// Paragraph
std::optional<SimpleRange> paragraphForCharacterOffset(const CharacterOffset&);
CharacterOffset nextParagraphEndCharacterOffset(const CharacterOffset&);
CharacterOffset previousParagraphStartCharacterOffset(const CharacterOffset&);
// Sentence
std::optional<SimpleRange> sentenceForCharacterOffset(const CharacterOffset&);
CharacterOffset nextSentenceEndCharacterOffset(const CharacterOffset&);
CharacterOffset previousSentenceStartCharacterOffset(const CharacterOffset&);
// Bounds
CharacterOffset characterOffsetForPoint(const IntPoint&, AXCoreObject*);
IntRect absoluteCaretBoundsForCharacterOffset(const CharacterOffset&);
CharacterOffset characterOffsetForBounds(const IntRect&, bool);
// Lines
CharacterOffset endCharacterOffsetOfLine(const CharacterOffset&);
CharacterOffset startCharacterOffsetOfLine(const CharacterOffset&);
// Index
CharacterOffset characterOffsetForIndex(int, const AXCoreObject*);
void postNotification(RenderObject*, AXNotification, PostTarget = PostTarget::Element);
void postNotification(Node*, AXNotification, PostTarget = PostTarget::Element);
void postNotification(AccessibilityObject*, Document*, AXNotification, PostTarget = PostTarget::Element);
void postNotification(AccessibilityObject* object, AXNotification notification)
{
if (object)
postNotification(*object, notification);
}
void postNotification(AccessibilityObject&, AXNotification);
void postDeferredNotification(RenderObject&, AXNotification);
void postARIANotifyNotification(Node&, const String&, const AriaNotifyOptions&);
#if PLATFORM(COCOA)
void postLiveRegionNotification(AccessibilityObject&, LiveRegionStatus, const AttributedString&);
#endif
// Requests clients to announce to the user the given message in the way they deem appropriate.
WEBCORE_EXPORT void announce(const String&);
// Invokes `assemble` exactly once, either synchronously if the page is not translated and no
// other announcements are queued ahead, or when the translated text arrives (or times out).
// The `language` argument is the target language when the text was translated and empty otherwise.
void translateAnnouncementThenAssemble(Ref<AccessibilityObject>&&, Vector<String>&& segments, Function<void(Vector<String>&&, const String& language)>&& assemble);
// Records a node whose descendant text is about to be swapped out for a re-rendering of the same
// content, e.g. when page-level translation replaces text.
//
// The resulting children-changed is not a content change the user should hear about again, so the
// live region announcement for it is skipped. Forgotten once that change has been processed, so
// it never suppresses a later, genuine mutation of the same node.
WEBCORE_EXPORT void deferReRenderedContent(Node&);
WEBCORE_EXPORT static void setAnnouncementTranslationTimeoutForTesting(std::optional<Seconds>);
void NODELETE setTextSelectionIntent(const AXTextStateChangeIntent&);
void NODELETE setIsSynchronizingSelection(bool);
// While non-std::nullopt, a focus change onto the given element will not be surfaced to assistive
// technology. A null target suppresses a *clearing* of focus instead.
void beginSuppressingFocusChange(Element* target) { m_suppressedFocusChange = WeakPtr { target }; }
void endSuppressingFocusChange() { m_suppressedFocusChange = std::nullopt; }
void postTextStateChangeNotification(Node*, AXTextEditType, const String&, const VisiblePosition&);
void postTextReplacementNotification(Node*, AXTextEditType deletionType, const String& deletedText, AXTextEditType insertionType, const String& insertedText, const VisiblePosition&);
void postTextReplacementNotificationForTextControl(HTMLTextFormControlElement&, const String& deletedText, const String& insertedText);
void postTextStateChangeNotification(Node*, const AXTextStateChangeIntent&, const VisibleSelection&);
void postTextStateChangeNotification(const Position&, const AXTextStateChangeIntent&, const VisibleSelection&);
void postLiveRegionChangeNotification(AccessibilityObject&);
// Testing-only: number of times a live region snapshot has been (re)computed since the last reset.
WEBCORE_EXPORT unsigned liveRegionSnapshotBuildCount() const;
WEBCORE_EXPORT void resetLiveRegionSnapshotBuildCount();
void frameLoadingEventNotification(LocalFrame*, AXLoadingEvent);
void prepareForDocumentDestruction(const Document&);
void startCachingComputedObjectAttributesUntilTreeMutates();
void stopCachingComputedObjectAttributes();
AXComputedObjectAttributeCache* computedObjectAttributeCache() LIFETIME_BOUND { return m_computedObjectAttributeCache.get(); }
Document* document() const;
FrameIdentifier frameID() const { return m_frameID; }
RefPtr<Page> NODELETE page() const;
IntPoint mapScreenPointToPagePoint(const IntPoint&) const;
void objectBecameIgnored(const AccessibilityObject&);
void objectBecameUnignored(const AccessibilityObject&);
static bool isMockObjectOrWebAreaRole(AccessibilityRole role)
{
switch (role) {
case AccessibilityRole::WebArea:
case AccessibilityRole::ScrollArea:
case AccessibilityRole::ScrollBar:
case AccessibilityRole::LocalFrame:
case AccessibilityRole::FrameHost:
case AccessibilityRole::RemoteFrame:
case AccessibilityRole::SliderThumb:
case AccessibilityRole::SpinButton:
case AccessibilityRole::SpinButtonPart:
case AccessibilityRole::MenuListPopup:
case AccessibilityRole::Column:
case AccessibilityRole::TableHeaderContainer:
return true;
default:
return false;
}
}
void incrementUnignoredContentObjectCount(AccessibilityRole role)
{
if (!isMockObjectOrWebAreaRole(role))
++m_unignoredContentObjectCount;
}
void decrementUnignoredContentObjectCount(AccessibilityRole role)
{
if (isMockObjectOrWebAreaRole(role))
return;
AX_ASSERT(m_unignoredContentObjectCount);
if (m_unignoredContentObjectCount)
--m_unignoredContentObjectCount;
}
#if PLATFORM(COCOA)
static void NODELETE setShouldRepostNotificationsForTests(bool);
#endif
void deferRecomputeIsIgnoredIfNeeded(Element*);
void deferRecomputeIsIgnored(Element*);
void deferRecomputeTableIsExposed(Element*);
void deferRecomputeTableCellSlots(AccessibilityNodeObject&);
void deferTextChangedIfNeeded(Node*);
void deferSelectedChildrenChangedIfNeeded(Element&);
WEBCORE_EXPORT void performDeferredCacheUpdate(ForceLayout);
void deferTextReplacementNotificationForTextControl(HTMLTextFormControlElement&, const String& previousValue);
std::optional<SimpleRange> rangeMatchesTextNearRange(const SimpleRange&, const String&);
static ASCIILiteral notificationPlatformName(AXNotification);
WEBCORE_EXPORT AXTreeData treeData(std::optional<OptionSet<AXStreamOptions>> = std::nullopt);
enum class UpdateRelations : bool { No, Yes };
// Returns the IDs of the objects that relate to the given object with the specified relationship.
std::optional<ListHashSet<AXID>> relatedObjectIDsFor(const AXCoreObject&, AXRelation, UpdateRelations = UpdateRelations::Yes);
void updateRelations(Element&, const QualifiedName&);
bool hasAriaOwnsRelations() const { return m_relationsNeedUpdate || m_hasAriaOwnsRelations; }
#if PLATFORM(IOS_FAMILY)
void relayNotification(String&&, RetainPtr<NSData>&&);
void setWillPresentDatePopover(bool willPresent) { m_willPresentDatePopover = willPresent; }
bool willPresentDatePopover() const { return m_willPresentDatePopover; }
#endif
#if PLATFORM(MAC)
AXCoreObject::AccessibilityChildrenVector sortedLiveRegions();
AXCoreObject::AccessibilityChildrenVector sortedNonRootWebAreas();
void deferSortForNewLiveRegion(Ref<AccessibilityObject>&&);
void queueUnsortedObject(Ref<AccessibilityObject>&&, PreSortedObjectType);
void addSortedObjects(Vector<Ref<AccessibilityObject>>&&, PreSortedObjectType);
void removeLiveRegion(AccessibilityObject&);
void initializeSortedIDLists();
WEBCORE_EXPORT static bool clientIsInTestMode();
#endif
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
inline void scheduleObjectRegionsUpdate(bool scheduleImmediately = false);
inline void willUpdateObjectRegions();
static bool isIsolatedTreeEnabled() { return accessibilityMode() == AccessibilityMode::AXThread; }
WEBCORE_EXPORT static void initializeAXThreadIfNeeded();
WEBCORE_EXPORT static bool NODELETE isAXThreadInitialized();
WEBCORE_EXPORT RefPtr<AXIsolatedTree> getOrCreateIsolatedTree();
void initializeIsolatedTreeGeometry();
static bool isAccessibilityList(Element&);
private:
static bool clientSupportsIsolatedTree();
enum class PlatformAXThreadSupport : bool { NotSupported, Supported };
static PlatformAXThreadSupport platformAXThreadSupport(AXThreadModePreconditions);
enum class DidStartThread : bool { No, Yes };
static DidStartThread platformStartSecondaryThread();
// Propagates the root of the isolated tree back into the Core and WebKit.
void setIsolatedTree(Ref<AXIsolatedTree>);
void setIsolatedTreeFocusedObject(AccessibilityObject*);
#if ENABLE(ACCESSIBILITY_LOCAL_FRAME)
// Refreshes the isolated-tree focused object of each ancestor local frame, keeping an ancestor
// tree (e.g. the main frame's, which VoiceOver queries) pointed at the AXLocalFrame leading
// toward the focused subframe. Only the focused frame's own cache handles its focus change, so
// ancestor trees would otherwise never learn focus moved into a descendant local frame.
void updateAncestorFramesFocusedObject();
#endif
void buildIsolatedTree();
void updateIsolatedTree(AccessibilityObject&, AXNotification);
void updateIsolatedTree(AccessibilityObject*, AXNotification);
void updateIsolatedTree(const Vector<std::pair<Ref<AccessibilityObject>, AXNotificationWithData>>&);
void updateIsolatedTree(AccessibilityObject*, AXProperty) const;
void updateIsolatedTree(AccessibilityObject&, AXProperty) const;
void startUpdateTreeSnapshotTimer();
#endif
void updateCachedTextOfAssociatedObjects(AccessibilityObject&);
protected:
void postPlatformNotification(AccessibilityObject&, AXNotification);
void platformHandleFocusedUIElementChanged(AccessibilityObject* oldFocus, AccessibilityObject* newFocus);
void platformPerformDeferredCacheUpdate();
#if PLATFORM(COCOA) || USE(ATSPI)
void postTextSelectionChangePlatformNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&);
void postTextStateChangePlatformNotification(AccessibilityObject*, AXTextEditType, const String&, const VisiblePosition&);
void postTextReplacementPlatformNotification(AccessibilityObject*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&);
void postTextReplacementPlatformNotificationForTextControl(AccessibilityObject*, const String& deletedText, const String& insertedText);
#else // PLATFORM(COCOA) || USE(ATSPI)
static AXTextChange textChangeForEditType(AXTextEditType);
void nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&);
#endif
#if PLATFORM(MAC)
void postUserInfoForChanges(AccessibilityObject&, AccessibilityObject&, RetainPtr<NSMutableArray>);
#endif
#if PLATFORM(COCOA)
WEBCORE_EXPORT void postPlatformAnnouncementNotification(const String&);
WEBCORE_EXPORT void postPlatformARIANotifyNotification(AccessibilityObject&, const AriaNotifyData&);
WEBCORE_EXPORT void postPlatformLiveRegionNotification(AccessibilityObject&, const LiveRegionAnnouncementData&);
#else
void postPlatformAnnouncementNotification(const String&) { }
void postPlatformARIANotifyNotification(AccessibilityObject&, const AriaNotifyData&) { }
#endif
void frameLoadingEventPlatformNotification(RenderView*, AXLoadingEvent);
void handleLabelChanged(AccessibilityObject*);
// CharacterOffset functions.
enum TraverseOption { TraverseOptionDefault = 1 << 0, TraverseOptionToNodeEnd = 1 << 1, TraverseOptionIncludeStart = 1 << 2, TraverseOptionValidateOffset = 1 << 3, TraverseOptionDoNotEnterTextControls = 1 << 4 };
Node* NODELETE nextNode(Node*) const;
Node* NODELETE previousNode(Node*) const;
CharacterOffset traverseToOffsetInRange(const SimpleRange&, int, TraverseOption = TraverseOptionDefault, bool stayWithinRange = false);
public:
VisiblePosition visiblePositionFromCharacterOffset(const CharacterOffset&, AllowUserSelectNone = AllowUserSelectNone::No);
protected:
CharacterOffset characterOffsetFromVisiblePosition(const VisiblePosition&);
char32_t characterAfter(const CharacterOffset&);
char32_t characterBefore(const CharacterOffset&);
CharacterOffset characterOffsetForNodeAndOffset(Node&, int, TraverseOption = TraverseOptionDefault);
enum class NeedsContextAtParagraphStart : bool { No, Yes };
CharacterOffset previousBoundary(const CharacterOffset&, BoundarySearchFunction, NeedsContextAtParagraphStart = NeedsContextAtParagraphStart::No);
CharacterOffset nextBoundary(const CharacterOffset&, BoundarySearchFunction);
CharacterOffset startCharacterOffsetOfWord(const CharacterOffset&, WordSide = WordSide::RightWordIfOnBoundary);
CharacterOffset endCharacterOffsetOfWord(const CharacterOffset&, WordSide = WordSide::RightWordIfOnBoundary);
CharacterOffset startCharacterOffsetOfParagraph(const CharacterOffset&, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
CharacterOffset endCharacterOffsetOfParagraph(const CharacterOffset&, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
CharacterOffset startCharacterOffsetOfSentence(const CharacterOffset&);
CharacterOffset endCharacterOffsetOfSentence(const CharacterOffset&);
CharacterOffset characterOffsetForPoint(const IntPoint&);
LayoutRect localCaretRectForCharacterOffset(RenderObject*&, const CharacterOffset&);
bool shouldSkipBoundary(const CharacterOffset&, const CharacterOffset&);
private:
// Returns the object or nearest render-tree ancestor object that is already created (i.e.
// retrievable by |get|, not |getOrCreate|).
AccessibilityObject* getIncludingAncestors(RenderObject&) const;
// The AX focus is more finegrained than the notion of focused Node. This method handles those cases where the focused AX object is a descendant or a sub-part of the focused Node.
AccessibilityObject* focusedObjectForNode(Node*);
static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement&);
void notificationPostTimerFired();
void enqueueNotificationToPost(Ref<AccessibilityObject>&&, AXNotificationWithData&&);
void announcementTranslationDidComplete(uint64_t requestID, Vector<String>&& translatedSegments);
bool isReRenderedContent(const AccessibilityObject&) const;
void flushPendingAnnouncements();
void pendingAnnouncementTimeoutTimerFired();
void scheduleAnnouncementTimeoutTimerIfNeeded();
static Seconds announcementTranslationTimeout();
void liveRegionChangedNotificationPostTimerFired();
void processChangedLiveRegions();
void performCacheUpdateTimerFired() { performDeferredCacheUpdate(ForceLayout::No); }
void postTextStateChangeNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&);
#if PLATFORM(COCOA)
bool enqueuePasswordNotification(AccessibilityObject&, AXTextChangeContext&&);
void passwordNotificationTimerFired();
#endif
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
void selectedTextRangeTimerFired();
Seconds NODELETE platformSelectedTextRangeDebounceInterval() const;
void updateTreeSnapshotTimerFired() { processQueuedIsolatedNodeUpdates(); }
void processQueuedIsolatedNodeUpdates();
void deferAddUnconnectedNode(AccessibilityObject&);
#if PLATFORM(MAC)
void createIsolatedObjectIfNeeded(AccessibilityObject&);
#endif
#endif // ENABLE(ACCESSIBILITY_ISOLATED_TREE)
void deferRowspanChange(AccessibilityObject*);
void handleCanvasFocusPathBoundsChange(const CanvasFocusPathBoundsChange&);
void handleChildrenChanged(AccessibilityObject&);
void handleAllDeferredChildrenChanged();
void handleInputTypeChanged(Element&);
void handleRoleChanged(Element&, const AtomString&, const AtomString&);
void handleARIARoleDescriptionChanged(Element&);
void handleMenuOpened(Element&);
void handleLiveRegionCreated(Element&);
#if PLATFORM(COCOA)
void initializeLiveRegionManager();
#endif
void handleMenuItemSelected(Element*);
void handleTabPanelSelected(Element*, Element*);
void handleRowCountChanged(AccessibilityObject*, Document*);
void handleAttributeChange(Element*, const QualifiedName&, const AtomString&, const AtomString&);
void handleClickHandlerChanged(Node&, const AtomString& eventType);
bool shouldProcessAttributeChange(Element*, const QualifiedName&);
void selectedChildrenChanged(Node*);
void selectedChildrenChanged(RenderObject*);
void handleScrollbarUpdate(ScrollView&);
void handleActiveDescendantChange(Element&, const AtomString&, const AtomString&);
void handleAriaExpandedChange(Element&);
void handleAriaHiddenChange(Element&);
enum class UpdateModal : bool { No, Yes };
void handleFocusedUIElementChanged(Element* oldFocus, Element* newFocus, UpdateModal = UpdateModal::Yes);
void handleRemoteFrameGainedFocus(RemoteFrame&, Element* oldFocusedElement);
#if ENABLE(ACCESSIBILITY_LOCAL_FRAME)
// Returns the AXLocalFrame in this cache's tree that proxies the direct child frame leading toward
// the focused subframe, or nullptr if focus is not in a descendant local frame. Resolves the frame
// owner element via TreeScope::focusedElementInScope() (as Document::activeElement() does).
AccessibilityObject* localFrameLeadingToFocusedFrame();
#endif
void handleMenuListValueChanged(Element&);
void handleTextChanged(AccessibilityObject*);
void handleRecomputeCellSlots(AccessibilityNodeObject&);
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
void handleRowspanChanged(AccessibilityNodeObject&);
#endif
void handleDeferredNotification(const DeferredNotificationData&);
void handleDeferredPopoverToggle(AccessibilityObject&);
// aria-modal or modal <dialog> related
bool isModalElement(Element&) const;
void findModalNodes();
void updateCurrentModalNode();
bool modalElementHasAccessibleContent(Element&);
void setDirtyStitchGroups(const RenderBlock&);
// Relationships between objects.
static Vector<QualifiedName>& relationAttributes();
AXRelation NODELETE attributeToRelationType(const QualifiedName&);
enum class AddSymmetricRelation : bool { No, Yes };
static AXRelation NODELETE symmetricRelation(AXRelation);
bool addRelation(Element&, Element&, AXRelation);
bool addRelation(AccessibilityObject*, AccessibilityObject*, AXRelation, AddSymmetricRelation = AddSymmetricRelation::Yes);
bool addRelation(Element&, const QualifiedName&);
void addLabelForRelation(Element&);
bool removeRelation(Element&, AXRelation);
void removeAllRelations(AXID);
void removeRelationByID(AXID originID, AXID targetID, AXRelation);
bool updateLabelFor(HTMLLabelElement&);
void updateLabeledBy(Element*);
void updateRelationsIfNeeded();
void updateRelationsForTree(ContainerNode&);
bool idChangeCanAffectRelations(Element*, const AtomString& oldID, const AtomString& newID) const;
void relationsNeedUpdate(bool);
void dirtyIsolatedTreeRelations();
HashMap<AXID, AXRelations> relations();
HashMap<AXID, LineRange> mostRecentlyPaintedText();
const HashSet<AXID>& relationTargetIDs();
bool isDescendantOfRelatedNode(Node&);
#if PLATFORM(MAC)
AXTextStateChangeIntent inferDirectionFromIntent(AccessibilityObject&, const AXTextStateChangeIntent&, const VisibleSelection&);
#endif
// Object creation.
Ref<AccessibilityRenderObject> createObjectFromRenderer(RenderObject&);
Ref<AccessibilityNodeObject> createFromNode(Node&);
const WeakPtr<Document, WeakPtrImplWithEventTargetData> m_document;
const FrameIdentifier m_frameID; // constant for object's lifetime.
#if ENABLE(ACCESSIBILITY_LOCAL_FRAME)
std::optional<AXFrameGeometry> m_frameGeometry;
IntPoint m_frameViewOriginScrollPosition;
#endif
OptionSet<ActivityState> m_pageActivityState;
HashMap<AXID, Ref<AccessibilityObject>> m_objects;
// Should be used only for renderer-only (i.e. no DOM node) accessibility objects.
WeakHashMap<RenderObject, AXID, SingleThreadWeakPtrImpl> m_renderObjectIdMapping;
WeakHashMap<Widget, AXID, SingleThreadWeakPtrImpl> m_widgetIdMapping;
// FIXME: The type for m_nodeIdMapping really should be:
// HashMap<WeakRef<Node, WeakPtrImplWithEventTargetData>, AXID>
// As this guarantees that we've called AXObjectCache::remove(Node&) for every node we store.
// However, in rare circumstances, we can add a node to this map, then later the document associated
// with the node loses its m_frame via detachFromFrame(). Then the node gets destroyed, but we can't
// clean it up from this map, since existingAXObjectCache fails due to the nullptr m_frame.
// This scenario seems extremely rare, and may only happen when the webpage is about to be destroyed anyways,
// so, go with WeakHashMap now until we find a completely safe solution based on document / frame lifecycles.
WeakHashMap<Node, AXID, WeakPtrImplWithEventTargetData> m_nodeIdMapping;
// This map exists as an optimization, reducing the number of HashMap lookups that AXObjectCache::get
// has to do to 1 (vs. a m_nodeIdMapping lookup, plus a m_objects lookup). Since this is one of
// our hottest functions, the extra memory cost is worth it.
WeakHashMap<Node, Ref<AccessibilityObject>, WeakPtrImplWithEventTargetData> m_nodeObjectMapping;
WeakHashMap<RenderText, LineRange, SingleThreadWeakPtrImpl> m_mostRecentlyPaintedText;
std::unique_ptr<AXComputedObjectAttributeCache> m_computedObjectAttributeCache;
#if PLATFORM(COCOA)
std::unique_ptr<AXLiveRegionManager> m_liveRegionManager;
#endif
static bool gAccessibilityEnhancedUserInterfaceEnabled;
WEBCORE_EXPORT static std::atomic<bool> gForceDeferredSpellChecking;
// FIXME: since the following only affects the behavior of isolated objects, we should move it into AXIsolatedTree in order to keep this class main thread only.
static std::atomic<bool> gForceInitialFrameCaching;
// Accessed on and off the main thread.
static std::atomic<bool> gAccessibilityTextStitchingEnabled;
// Accessed on and off the main thread.
static std::atomic<bool> gAccessibilityThreadHitTestingEnabled;
#if PLATFORM(COCOA)
static std::atomic<bool> gAccessibilityDOMIdentifiersEnabled;
#endif
Timer m_notificationPostTimer;
Vector<std::pair<Ref<AccessibilityObject>, AXNotificationWithData>> m_notificationsToPost;
HashSet<AXID> m_pendingLayoutCompleteObjectIDs;
// Core-AAM requires announcements to be translated before they reach the platform when the page
// is presented as a machine translation.
struct PendingAnnouncement {
Ref<AccessibilityObject> object;
Vector<String> segments;
String targetLocale;
// Zero once this entry is no longer awaiting a reply, whether it succeeded, failed, or timed out.
uint64_t translationRequestID { 0 };
MonotonicTime deadline;
// Rebuilds the payload from the (possibly translated) segments and enqueues it to post. The
// language argument is empty when the segments were left untranslated.
Function<void(Vector<String>&&, const String& language)> assembleAndEnqueue;
};
Deque<PendingAnnouncement> m_pendingAnnouncements;
Timer m_pendingAnnouncementTimeoutTimer;
uint64_t m_nextAnnouncementTranslationRequestID { 0 };
// Nodes whose pending children-changed is a re-rendering of content already announced.
WeakListHashSet<Node, WeakPtrImplWithEventTargetData> m_deferredReRenderedContent;
// The nodes above plus all their ancestors, so isReRenderedContent() is a lookup instead of a
// walk. Built on first query and discarded whenever m_deferredReRenderedContent changes.
mutable std::optional<WeakHashSet<Node, WeakPtrImplWithEventTargetData>> m_reRenderedContentAndAncestors;
#if PLATFORM(COCOA)
Timer m_passwordNotificationTimer;
Deque<std::pair<Ref<AccessibilityObject>, AXTextChangeContext>> m_passwordNotifications;
#endif
Timer m_liveRegionChangedPostTimer;
ListHashSet<Ref<AccessibilityObject>> m_changedLiveRegions;
#if PLATFORM(MAC)
// This block is PLATFORM(MAC) because the remote search API is currently only used on macOS.
// AX tree-order-sorted list of a few types of objects. This is useful because some assistive
// technologies send us frequent remote search requests for all the live regions or non-root webareas
// on the page.
bool m_sortedIDListsInitialized { false };
Vector<AXID> m_sortedLiveRegionIDs;
Vector<AXID> m_sortedNonRootWebAreaIDs;
#endif // PLATFORM(MAC)
WeakPtr<Element, WeakPtrImplWithEventTargetData> m_currentModalElement;
// Multiple aria-modals behavior is undefined by spec. We keep them sorted based on DOM order here.
Vector<WeakPtr<Element, WeakPtrImplWithEventTargetData>> m_modalElements;
bool m_modalNodesInitialized { false };
bool m_isRetrievingCurrentModalNode { false };
bool m_needsAriaHiddenModalOverrideCheck { false };
#if PLATFORM(COCOA)
bool m_liveRegionManagerInitialized { false };
static std::atomic<bool> gShouldRepostNotificationsForTests;
#endif
// "Unignored content object count" and not "unignored object count"
// because we exclude certain roles from this count (e.g. web-areas, mock objects)
// on the basis of them not being meaningful content.
unsigned m_unignoredContentObjectCount { 0 };
Timer m_performCacheUpdateTimer;
AXTextStateChangeIntent m_textSelectionIntent;
WeakHashSet<AccessibilityObject> m_deferredRendererChangedList;
WeakHashSet<AccessibilityObject> m_deferredRecomputeActiveSummaryList;
WeakHashSet<Element, WeakPtrImplWithEventTargetData> m_deferredRecomputeIsIgnoredList;
WeakHashSet<Element, WeakPtrImplWithEventTargetData> m_deferredRecomputeTableIsExposedList;
WeakHashSet<AccessibilityNodeObject> m_deferredRecomputeTableCellSlotsList;
WeakHashSet<AccessibilityNodeObject> m_deferredRowspanChanges;
WeakListHashSet<Node, WeakPtrImplWithEventTargetData> m_deferredTextChangedList;
WeakHashSet<Element, WeakPtrImplWithEventTargetData> m_deferredSelectedChildredChangedList;
ListHashSet<Ref<AccessibilityObject>> m_deferredChildrenChangedList;
WeakListHashSet<Element, WeakPtrImplWithEventTargetData> m_deferredElementAddedOrRemovedList;
WeakHashSet<Element, WeakPtrImplWithEventTargetData> m_deferredModalChangedList;
WeakHashSet<Element, WeakPtrImplWithEventTargetData> m_deferredMenuListChange;
SingleThreadWeakHashSet<ScrollView> m_deferredScrollbarUpdateChangeList;
WeakHashMap<Element, String, WeakPtrImplWithEventTargetData> m_deferredTextFormControlValue;
Vector<AttributeChange> m_deferredAttributeChange;
Vector<CanvasFocusPathBoundsChange> m_deferredCanvasFocusPathBoundsChanges;
std::optional<std::pair<WeakPtr<Element, WeakPtrImplWithEventTargetData>, WeakPtr<Element, WeakPtrImplWithEventTargetData>>> m_deferredFocusedNodeChange;
std::optional<DeferredRemoteFrameFocus> m_deferredRemoteFrameFocus;
// A std::nullopt means no focus change is set up for suppression.
// A non-std::nullopt value of nullptr means to suppress the next focus clear.
// A non-std::nullopt value of an element means to suppress the focus change to that element.
//
// In all three cases, the term "suppression" refers to the act of not surfacing a focus
// change notification to assistive technologies.
std::optional<WeakPtr<Element, WeakPtrImplWithEventTargetData>> m_suppressedFocusChange;
WeakHashSet<AccessibilityObject> m_deferredUnconnectedObjects;
#if PLATFORM(MAC)
HashMap<PreSortedObjectType, Vector<Ref<AccessibilityObject>>, IntHash<PreSortedObjectType>, WTF::StrongEnumHashTraits<PreSortedObjectType>> m_deferredUnsortedObjects;
Vector<WeakPtr<Document, WeakPtrImplWithEventTargetData>> m_deferredDocumentsWithNewRenderTrees;
#endif
Vector<DeferredNotificationData> m_deferredNotifications;
Vector<Ref<AccessibilityObject>> m_deferredToggledPopovers;
const Ref<AXGeometryManager> m_geometryManager;
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
Timer m_buildIsolatedTreeTimer;
bool m_deferredRegenerateIsolatedTree { false };
DeferrableOneShotTimer m_selectedTextRangeTimer;
Markable<AXID> m_lastDebouncedTextRangeObject;
Timer m_updateTreeSnapshotTimer;
#endif
bool m_isSynchronizingSelection { false };
bool m_performingDeferredCacheUpdate { false };
// True while remove(AXID) is tearing down an object.
bool m_isRemovingNode { false };
double m_loadingProgress { 0 };
// Tracks focus landing inside an aria-hidden region. After one rendering update
// (giving web developers a chance to move focus in a rAF callback), if focus is
// still inside the aria-hidden region, the aria-hidden is permanently overridden.
WeakPtr<Element, WeakPtrImplWithEventTargetData> m_pendingAriaHiddenFocusTarget;
bool m_needsAriaHiddenFocusCheck { false };
unsigned m_cacheUpdateDeferredCount { 0 };
#if PLATFORM(IOS_FAMILY)
bool m_willPresentDatePopover;
#endif
// Relationships between objects.
HashMap<AXID, AXRelations> m_relations;
bool m_relationsNeedUpdate { true };
bool m_hasAriaOwnsRelations { false };
bool m_doneInitialRelationsBuild { false };
HashSet<AXID> m_relationTargets;
HashMap<AXID, AXRelations> m_recentlyRemovedRelations;
WeakHashSet<Element, WeakPtrImplWithEventTargetData> m_elementsWithRelationAttributes;
// Ids referenced by a relation attribute (e.g. aria-labelledby) whose target didn't exist when
// relations were last built. If an element with one of these ids is later inserted, we must
// re-resolve relations.
HashSet<AtomString> m_unresolvedRelationTargetIds;
// All ids referenced by a relation attribute (resolved or not) as of the last relations build.
// Used to decide whether an id-attribute change can affect any relation.
HashSet<AtomString> m_referencedRelationTargetIds;
#if USE(ATSPI)
ListHashSet<RefPtr<AccessibilityObject>> m_deferredParentChangedList;
#endif
#if PLATFORM(MAC)
Markable<AXID> m_lastTextFieldAXID;
VisibleSelection m_lastSelection;
#endif
WeakHashMap<RenderObject, Vector<AXStitchGroup>, SingleThreadWeakPtrImpl> m_stitchGroups;
};
inline bool AXObjectCache::accessibilityEnabled()
{
return !isAccessibilityModeOff(accessibilityMode());
}
inline void AXObjectCache::enableAccessibility([[maybe_unused]] AXThreadModePreconditions preconditions)
{
if (accessibilityMode() == AccessibilityMode::AXThread) {
// If we're in AXThread mode, there's nothing more to do.
#if !ENABLE(ACCESSIBILITY_ISOLATED_TREE)
AX_ASSERT_NOT_REACHED();
#endif
return;
}
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
if (transitionToAXThreadModeIfNeeded(preconditions))
return;
#endif
// We may not always be allowed to transition to AXThread-mode based on
// various factors (e.g. feature flags, system defaults), so make sure
// we move to MainThread mode at a minimum.
std::ignore = attemptMainThreadModeTransition();
}
inline bool AXObjectCache::forceDeferredSpellChecking()
{
return gForceDeferredSpellChecking;
}
inline void AXObjectCache::setForceDeferredSpellChecking(bool shouldForce)
{
gForceDeferredSpellChecking = shouldForce;
}
#if PLATFORM(COCOA)
WEBCORE_EXPORT RetainPtr<NSString> notifyPriorityToAXValueString(NotifyPriority);
WEBCORE_EXPORT RetainPtr<NSString> interruptBehaviorToAXValueString(InterruptBehavior);
#endif
} // namespace WebCore