| 1 | /* |
| 2 | * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2011, 2015 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * 1. Redistributions of source code must retain the above copyright |
| 8 | * notice, this list of conditions and the following disclaimer. |
| 9 | * 2. Redistributions in binary form must reproduce the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer in the |
| 11 | * documentation and/or other materials provided with the distribution. |
| 12 | * |
| 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| 14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| 17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 24 | */ |
| 25 | |
| 26 | #pragma once |
| 27 | |
| 28 | #if ENABLE(ACCESSIBILITY_ISOLATED_TREE) |
| 29 | #include "AXIsolatedTree.h" |
| 30 | #endif |
| 31 | #include "AXTextStateChangeIntent.h" |
| 32 | #include "AccessibilityObject.h" |
| 33 | #include "Range.h" |
| 34 | #include "Timer.h" |
| 35 | #include "VisibleUnits.h" |
| 36 | #include <limits.h> |
| 37 | #include <wtf/Forward.h> |
| 38 | #include <wtf/HashMap.h> |
| 39 | #include <wtf/HashSet.h> |
| 40 | #include <wtf/ListHashSet.h> |
| 41 | #include <wtf/RefPtr.h> |
| 42 | |
| 43 | #if PLATFORM(GTK) |
| 44 | #include <wtf/glib/GRefPtr.h> |
| 45 | #endif |
| 46 | |
| 47 | namespace WebCore { |
| 48 | |
| 49 | #if ENABLE(ACCESSIBILITY_ISOLATED_TREE) |
| 50 | class AXIsolatedTreeNode; |
| 51 | #endif |
| 52 | class Document; |
| 53 | class HTMLAreaElement; |
| 54 | class HTMLTextFormControlElement; |
| 55 | class Node; |
| 56 | class Page; |
| 57 | class RenderBlock; |
| 58 | class RenderObject; |
| 59 | class RenderText; |
| 60 | class ScrollView; |
| 61 | class VisiblePosition; |
| 62 | class Widget; |
| 63 | |
| 64 | struct TextMarkerData { |
| 65 | AXID axID { 0 }; |
| 66 | Node* node { nullptr }; |
| 67 | int offset { 0 }; |
| 68 | int characterStartIndex { 0 }; |
| 69 | int characterOffset { 0 }; |
| 70 | bool ignored { false }; |
| 71 | EAffinity affinity { DOWNSTREAM }; |
| 72 | }; |
| 73 | |
| 74 | struct CharacterOffset { |
| 75 | Node* node; |
| 76 | int startIndex; |
| 77 | int offset; |
| 78 | int remainingOffset; |
| 79 | |
| 80 | CharacterOffset(Node* n = nullptr, int startIndex = 0, int offset = 0, int remaining = 0) |
| 81 | : node(n) |
| 82 | , startIndex(startIndex) |
| 83 | , offset(offset) |
| 84 | , remainingOffset(remaining) |
| 85 | { } |
| 86 | |
| 87 | int remaining() const { return remainingOffset; } |
| 88 | bool isNull() const { return !node; } |
| 89 | bool isEqual(const CharacterOffset& other) const |
| 90 | { |
| 91 | if (isNull() || other.isNull()) |
| 92 | return false; |
| 93 | return node == other.node && startIndex == other.startIndex && offset == other.offset; |
| 94 | } |
| 95 | }; |
| 96 | |
| 97 | class AXComputedObjectAttributeCache { |
| 98 | WTF_MAKE_FAST_ALLOCATED; |
| 99 | public: |
| 100 | AccessibilityObjectInclusion getIgnored(AXID) const; |
| 101 | void setIgnored(AXID, AccessibilityObjectInclusion); |
| 102 | |
| 103 | private: |
| 104 | struct CachedAXObjectAttributes { |
| 105 | CachedAXObjectAttributes() |
| 106 | : ignored(AccessibilityObjectInclusion::DefaultBehavior) |
| 107 | { } |
| 108 | |
| 109 | AccessibilityObjectInclusion ignored; |
| 110 | }; |
| 111 | |
| 112 | HashMap<AXID, CachedAXObjectAttributes> m_idMapping; |
| 113 | }; |
| 114 | |
| 115 | struct VisiblePositionIndex { |
| 116 | int value = -1; |
| 117 | RefPtr<ContainerNode> scope; |
| 118 | }; |
| 119 | |
| 120 | struct VisiblePositionIndexRange { |
| 121 | VisiblePositionIndex startIndex; |
| 122 | VisiblePositionIndex endIndex; |
| 123 | bool isNull() const { return startIndex.value == -1 || endIndex.value == -1; } |
| 124 | }; |
| 125 | |
| 126 | class AccessibilityReplacedText { |
| 127 | public: |
| 128 | AccessibilityReplacedText() = default; |
| 129 | AccessibilityReplacedText(const VisibleSelection&); |
| 130 | void postTextStateChangeNotification(AXObjectCache*, AXTextEditType, const String&, const VisibleSelection&); |
| 131 | const VisiblePositionIndexRange& replacedRange() { return m_replacedRange; } |
| 132 | protected: |
| 133 | String m_replacedText; |
| 134 | VisiblePositionIndexRange m_replacedRange; |
| 135 | }; |
| 136 | |
| 137 | #if !PLATFORM(COCOA) |
| 138 | enum AXTextChange { AXTextInserted, AXTextDeleted, AXTextAttributesChanged }; |
| 139 | #endif |
| 140 | |
| 141 | enum PostTarget { TargetElement, TargetObservableParent }; |
| 142 | |
| 143 | enum PostType { PostSynchronously, PostAsynchronously }; |
| 144 | |
| 145 | class AXObjectCache { |
| 146 | WTF_MAKE_NONCOPYABLE(AXObjectCache); WTF_MAKE_FAST_ALLOCATED; |
| 147 | public: |
| 148 | explicit AXObjectCache(Document&); |
| 149 | ~AXObjectCache(); |
| 150 | |
| 151 | WEBCORE_EXPORT static AccessibilityObject* focusedUIElementForPage(const Page*); |
| 152 | |
| 153 | // Returns the root object for the entire document. |
| 154 | WEBCORE_EXPORT AccessibilityObject* rootObject(); |
| 155 | // Returns the root object for a specific frame. |
| 156 | WEBCORE_EXPORT AccessibilityObject* rootObjectForFrame(Frame*); |
| 157 | |
| 158 | // For AX objects with elements that back them. |
| 159 | AccessibilityObject* getOrCreate(RenderObject*); |
| 160 | AccessibilityObject* getOrCreate(Widget*); |
| 161 | WEBCORE_EXPORT AccessibilityObject* getOrCreate(Node*); |
| 162 | |
| 163 | // used for objects without backing elements |
| 164 | AccessibilityObject* getOrCreate(AccessibilityRole); |
| 165 | |
| 166 | // will only return the AccessibilityObject if it already exists |
| 167 | AccessibilityObject* get(RenderObject*); |
| 168 | AccessibilityObject* get(Widget*); |
| 169 | AccessibilityObject* get(Node*); |
| 170 | |
| 171 | void remove(RenderObject*); |
| 172 | void remove(Node&); |
| 173 | void remove(Widget*); |
| 174 | void remove(AXID); |
| 175 | |
| 176 | void detachWrapper(AccessibilityObject*, AccessibilityDetachmentType); |
| 177 | void attachWrapper(AccessibilityObject*); |
| 178 | void childrenChanged(Node*, Node* newChild = nullptr); |
| 179 | void childrenChanged(RenderObject*, RenderObject* newChild = nullptr); |
| 180 | void childrenChanged(AccessibilityObject*); |
| 181 | void checkedStateChanged(Node*); |
| 182 | // Called when a node has just been attached, so we can make sure we have the right subclass of AccessibilityObject. |
| 183 | void updateCacheAfterNodeIsAttached(Node*); |
| 184 | |
| 185 | void deferFocusedUIElementChangeIfNeeded(Node* oldFocusedNode, Node* newFocusedNode); |
| 186 | void handleScrolledToAnchor(const Node* anchorNode); |
| 187 | void handleScrollbarUpdate(ScrollView*); |
| 188 | |
| 189 | Node* modalNode(); |
| 190 | |
| 191 | void deferAttributeChangeIfNeeded(const QualifiedName&, Element*); |
| 192 | void recomputeIsIgnored(RenderObject* renderer); |
| 193 | |
| 194 | #if ENABLE(ACCESSIBILITY_ISOLATED_TREE) |
| 195 | WEBCORE_EXPORT Ref<AXIsolatedTree> generateIsolatedAccessibilityTree(); |
| 196 | |
| 197 | void associateIsolatedTreeNode(AccessibilityObject&, AXIsolatedTreeNode&, AXIsolatedTreeID); |
| 198 | Ref<AXIsolatedTreeNode> createIsolatedAccessibilityTreeHierarchy(AccessibilityObject&, AXID, AXIsolatedTree&, Vector<Ref<AXIsolatedTreeNode>>&); |
| 199 | #endif |
| 200 | |
| 201 | #if HAVE(ACCESSIBILITY) |
| 202 | WEBCORE_EXPORT static void enableAccessibility(); |
| 203 | WEBCORE_EXPORT static void disableAccessibility(); |
| 204 | |
| 205 | // Enhanced user interface accessibility can be toggled by the assistive technology. |
| 206 | WEBCORE_EXPORT static void setEnhancedUserInterfaceAccessibility(bool flag); |
| 207 | |
| 208 | // Note: these may be called from a non-main thread concurrently as other readers. |
| 209 | static bool accessibilityEnabled() { return gAccessibilityEnabled; } |
| 210 | static bool accessibilityEnhancedUserInterfaceEnabled() { return gAccessibilityEnhancedUserInterfaceEnabled; } |
| 211 | #else |
| 212 | static void enableAccessibility() { } |
| 213 | static void disableAccessibility() { } |
| 214 | static void setEnhancedUserInterfaceAccessibility(bool) { } |
| 215 | static bool accessibilityEnabled() { return false; } |
| 216 | static bool accessibilityEnhancedUserInterfaceEnabled() { return false; } |
| 217 | #endif |
| 218 | |
| 219 | const Element* rootAXEditableElement(const Node*); |
| 220 | bool nodeIsTextControl(const Node*); |
| 221 | |
| 222 | AXID platformGenerateAXID() const; |
| 223 | AccessibilityObject* objectFromAXID(AXID id) const { return m_objects.get(id); } |
| 224 | |
| 225 | // Text marker utilities. |
| 226 | Optional<TextMarkerData> textMarkerDataForVisiblePosition(const VisiblePosition&); |
| 227 | Optional<TextMarkerData> textMarkerDataForFirstPositionInTextControl(HTMLTextFormControlElement&); |
| 228 | void textMarkerDataForCharacterOffset(TextMarkerData&, const CharacterOffset&); |
| 229 | void textMarkerDataForNextCharacterOffset(TextMarkerData&, const CharacterOffset&); |
| 230 | void textMarkerDataForPreviousCharacterOffset(TextMarkerData&, const CharacterOffset&); |
| 231 | VisiblePosition visiblePositionForTextMarkerData(TextMarkerData&); |
| 232 | CharacterOffset characterOffsetForTextMarkerData(TextMarkerData&); |
| 233 | // Use ignoreNextNodeStart/ignorePreviousNodeEnd to determine the behavior when we are at node boundary. |
| 234 | CharacterOffset nextCharacterOffset(const CharacterOffset&, bool ignoreNextNodeStart = true); |
| 235 | CharacterOffset previousCharacterOffset(const CharacterOffset&, bool ignorePreviousNodeEnd = true); |
| 236 | void startOrEndTextMarkerDataForRange(TextMarkerData&, RefPtr<Range>, bool); |
| 237 | CharacterOffset startOrEndCharacterOffsetForRange(RefPtr<Range>, bool, bool enterTextControls = false); |
| 238 | AccessibilityObject* accessibilityObjectForTextMarkerData(TextMarkerData&); |
| 239 | RefPtr<Range> rangeForUnorderedCharacterOffsets(const CharacterOffset&, const CharacterOffset&); |
| 240 | static RefPtr<Range> rangeForNodeContents(Node*); |
| 241 | static int lengthForRange(Range*); |
| 242 | |
| 243 | // Word boundary |
| 244 | CharacterOffset nextWordEndCharacterOffset(const CharacterOffset&); |
| 245 | CharacterOffset previousWordStartCharacterOffset(const CharacterOffset&); |
| 246 | RefPtr<Range> leftWordRange(const CharacterOffset&); |
| 247 | RefPtr<Range> rightWordRange(const CharacterOffset&); |
| 248 | |
| 249 | // Paragraph |
| 250 | RefPtr<Range> paragraphForCharacterOffset(const CharacterOffset&); |
| 251 | CharacterOffset nextParagraphEndCharacterOffset(const CharacterOffset&); |
| 252 | CharacterOffset previousParagraphStartCharacterOffset(const CharacterOffset&); |
| 253 | |
| 254 | // Sentence |
| 255 | RefPtr<Range> sentenceForCharacterOffset(const CharacterOffset&); |
| 256 | CharacterOffset nextSentenceEndCharacterOffset(const CharacterOffset&); |
| 257 | CharacterOffset previousSentenceStartCharacterOffset(const CharacterOffset&); |
| 258 | |
| 259 | // Bounds |
| 260 | CharacterOffset characterOffsetForPoint(const IntPoint&, AccessibilityObject*); |
| 261 | IntRect absoluteCaretBoundsForCharacterOffset(const CharacterOffset&); |
| 262 | CharacterOffset characterOffsetForBounds(const IntRect&, bool); |
| 263 | |
| 264 | // Lines |
| 265 | CharacterOffset endCharacterOffsetOfLine(const CharacterOffset&); |
| 266 | CharacterOffset startCharacterOffsetOfLine(const CharacterOffset&); |
| 267 | |
| 268 | // Index |
| 269 | CharacterOffset characterOffsetForIndex(int, const AccessibilityObject*); |
| 270 | int indexForCharacterOffset(const CharacterOffset&, AccessibilityObject*); |
| 271 | |
| 272 | enum AXNotification { |
| 273 | AXActiveDescendantChanged, |
| 274 | AXAutocorrectionOccured, |
| 275 | AXCheckedStateChanged, |
| 276 | AXChildrenChanged, |
| 277 | AXCurrentChanged, |
| 278 | AXDisabledStateChanged, |
| 279 | AXFocusedUIElementChanged, |
| 280 | AXLayoutComplete, |
| 281 | AXLoadComplete, |
| 282 | AXNewDocumentLoadComplete, |
| 283 | AXSelectedChildrenChanged, |
| 284 | AXSelectedTextChanged, |
| 285 | AXValueChanged, |
| 286 | AXScrolledToAnchor, |
| 287 | AXLiveRegionCreated, |
| 288 | AXLiveRegionChanged, |
| 289 | , |
| 290 | , |
| 291 | , |
| 292 | , |
| 293 | AXRowCountChanged, |
| 294 | AXRowCollapsed, |
| 295 | AXRowExpanded, |
| 296 | AXExpandedChanged, |
| 297 | AXInvalidStatusChanged, |
| 298 | AXPressDidSucceed, |
| 299 | AXPressDidFail, |
| 300 | AXPressedStateChanged, |
| 301 | AXReadOnlyStatusChanged, |
| 302 | AXRequiredStatusChanged, |
| 303 | AXTextChanged, |
| 304 | AXAriaAttributeChanged, |
| 305 | AXElementBusyChanged |
| 306 | }; |
| 307 | |
| 308 | void postNotification(RenderObject*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously); |
| 309 | void postNotification(Node*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously); |
| 310 | void postNotification(AccessibilityObject*, Document*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously); |
| 311 | |
| 312 | #ifndef NDEBUG |
| 313 | void showIntent(const AXTextStateChangeIntent&); |
| 314 | #endif |
| 315 | |
| 316 | void setTextSelectionIntent(const AXTextStateChangeIntent&); |
| 317 | void setIsSynchronizingSelection(bool); |
| 318 | |
| 319 | void postTextStateChangeNotification(Node*, AXTextEditType, const String&, const VisiblePosition&); |
| 320 | void postTextReplacementNotification(Node*, AXTextEditType deletionType, const String& deletedText, AXTextEditType insertionType, const String& insertedText, const VisiblePosition&); |
| 321 | void postTextReplacementNotificationForTextControl(HTMLTextFormControlElement&, const String& deletedText, const String& insertedText); |
| 322 | void postTextStateChangeNotification(Node*, const AXTextStateChangeIntent&, const VisibleSelection&); |
| 323 | void postTextStateChangeNotification(const Position&, const AXTextStateChangeIntent&, const VisibleSelection&); |
| 324 | void postLiveRegionChangeNotification(AccessibilityObject*); |
| 325 | void focusModalNode(); |
| 326 | |
| 327 | enum AXLoadingEvent { |
| 328 | AXLoadingStarted, |
| 329 | AXLoadingReloaded, |
| 330 | AXLoadingFailed, |
| 331 | AXLoadingFinished |
| 332 | }; |
| 333 | |
| 334 | void frameLoadingEventNotification(Frame*, AXLoadingEvent); |
| 335 | |
| 336 | void prepareForDocumentDestruction(const Document&); |
| 337 | |
| 338 | void startCachingComputedObjectAttributesUntilTreeMutates(); |
| 339 | void stopCachingComputedObjectAttributes(); |
| 340 | |
| 341 | AXComputedObjectAttributeCache* computedObjectAttributeCache() { return m_computedObjectAttributeCache.get(); } |
| 342 | |
| 343 | Document& document() const { return m_document; } |
| 344 | |
| 345 | #if PLATFORM(MAC) |
| 346 | static void setShouldRepostNotificationsForTests(bool value); |
| 347 | #endif |
| 348 | void deferRecomputeIsIgnoredIfNeeded(Element*); |
| 349 | void deferRecomputeIsIgnored(Element*); |
| 350 | void deferTextChangedIfNeeded(Node*); |
| 351 | void deferSelectedChildrenChangedIfNeeded(Element&); |
| 352 | void performDeferredCacheUpdate(); |
| 353 | void deferTextReplacementNotificationForTextControl(HTMLTextFormControlElement&, const String& previousValue); |
| 354 | |
| 355 | RefPtr<Range> rangeMatchesTextNearRange(RefPtr<Range>, const String&); |
| 356 | |
| 357 | |
| 358 | protected: |
| 359 | void postPlatformNotification(AccessibilityObject*, AXNotification); |
| 360 | void platformHandleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode); |
| 361 | |
| 362 | void platformPerformDeferredCacheUpdate(); |
| 363 | |
| 364 | #if PLATFORM(COCOA) |
| 365 | void postTextStateChangePlatformNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&); |
| 366 | void postTextStateChangePlatformNotification(AccessibilityObject*, AXTextEditType, const String&, const VisiblePosition&); |
| 367 | void postTextReplacementPlatformNotificationForTextControl(AccessibilityObject*, const String& deletedText, const String& insertedText, HTMLTextFormControlElement&); |
| 368 | void postTextReplacementPlatformNotification(AccessibilityObject*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&); |
| 369 | #else |
| 370 | static AXTextChange textChangeForEditType(AXTextEditType); |
| 371 | void nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&); |
| 372 | #endif |
| 373 | |
| 374 | void frameLoadingEventPlatformNotification(AccessibilityObject*, AXLoadingEvent); |
| 375 | void textChanged(AccessibilityObject*); |
| 376 | void labelChanged(Element*); |
| 377 | |
| 378 | // This is a weak reference cache for knowing if Nodes used by TextMarkers are valid. |
| 379 | void setNodeInUse(Node* n) { m_textMarkerNodes.add(n); } |
| 380 | void removeNodeForUse(Node& n) { m_textMarkerNodes.remove(&n); } |
| 381 | bool isNodeInUse(Node* n) { return m_textMarkerNodes.contains(n); } |
| 382 | |
| 383 | // CharacterOffset functions. |
| 384 | enum TraverseOption { TraverseOptionDefault = 1 << 0, TraverseOptionToNodeEnd = 1 << 1, TraverseOptionIncludeStart = 1 << 2, TraverseOptionValidateOffset = 1 << 3, TraverseOptionDoNotEnterTextControls = 1 << 4 }; |
| 385 | Node* nextNode(Node*) const; |
| 386 | Node* previousNode(Node*) const; |
| 387 | CharacterOffset traverseToOffsetInRange(RefPtr<Range>, int, TraverseOption = TraverseOptionDefault, bool stayWithinRange = false); |
| 388 | VisiblePosition visiblePositionFromCharacterOffset(const CharacterOffset&); |
| 389 | CharacterOffset characterOffsetFromVisiblePosition(const VisiblePosition&); |
| 390 | void setTextMarkerDataWithCharacterOffset(TextMarkerData&, const CharacterOffset&); |
| 391 | UChar32 characterAfter(const CharacterOffset&); |
| 392 | UChar32 characterBefore(const CharacterOffset&); |
| 393 | CharacterOffset characterOffsetForNodeAndOffset(Node&, int, TraverseOption = TraverseOptionDefault); |
| 394 | |
| 395 | enum class NeedsContextAtParagraphStart { Yes, No }; |
| 396 | CharacterOffset previousBoundary(const CharacterOffset&, BoundarySearchFunction, NeedsContextAtParagraphStart = NeedsContextAtParagraphStart::No); |
| 397 | CharacterOffset nextBoundary(const CharacterOffset&, BoundarySearchFunction); |
| 398 | CharacterOffset startCharacterOffsetOfWord(const CharacterOffset&, EWordSide = RightWordIfOnBoundary); |
| 399 | CharacterOffset endCharacterOffsetOfWord(const CharacterOffset&, EWordSide = RightWordIfOnBoundary); |
| 400 | CharacterOffset startCharacterOffsetOfParagraph(const CharacterOffset&, EditingBoundaryCrossingRule = CannotCrossEditingBoundary); |
| 401 | CharacterOffset endCharacterOffsetOfParagraph(const CharacterOffset&, EditingBoundaryCrossingRule = CannotCrossEditingBoundary); |
| 402 | CharacterOffset startCharacterOffsetOfSentence(const CharacterOffset&); |
| 403 | CharacterOffset endCharacterOffsetOfSentence(const CharacterOffset&); |
| 404 | CharacterOffset characterOffsetForPoint(const IntPoint&); |
| 405 | LayoutRect localCaretRectForCharacterOffset(RenderObject*&, const CharacterOffset&); |
| 406 | bool shouldSkipBoundary(const CharacterOffset&, const CharacterOffset&); |
| 407 | |
| 408 | private: |
| 409 | AccessibilityObject* rootWebArea(); |
| 410 | |
| 411 | static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement*); |
| 412 | |
| 413 | AXID getAXID(AccessibilityObject*); |
| 414 | |
| 415 | void notificationPostTimerFired(); |
| 416 | |
| 417 | void liveRegionChangedNotificationPostTimerFired(); |
| 418 | |
| 419 | void focusModalNodeTimerFired(); |
| 420 | |
| 421 | void performCacheUpdateTimerFired(); |
| 422 | |
| 423 | void postTextStateChangeNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&); |
| 424 | |
| 425 | bool enqueuePasswordValueChangeNotification(AccessibilityObject*); |
| 426 | void passwordNotificationPostTimerFired(); |
| 427 | |
| 428 | void handleMenuOpened(Node*); |
| 429 | void handleLiveRegionCreated(Node*); |
| 430 | void handleMenuItemSelected(Node*); |
| 431 | void handleAttributeChange(const QualifiedName&, Element*); |
| 432 | bool shouldProcessAttributeChange(const QualifiedName&, Element*); |
| 433 | void selectedChildrenChanged(Node*); |
| 434 | void selectedChildrenChanged(RenderObject*); |
| 435 | // Called by a node when text or a text equivalent (e.g. alt) attribute is changed. |
| 436 | void textChanged(Node*); |
| 437 | void handleActiveDescendantChanged(Node*); |
| 438 | void handleAriaRoleChanged(Node*); |
| 439 | void handleAriaExpandedChange(Node*); |
| 440 | void handleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode); |
| 441 | |
| 442 | // aria-modal related |
| 443 | void findModalNodes(); |
| 444 | void updateCurrentModalNode(); |
| 445 | bool isNodeVisible(Node*) const; |
| 446 | void handleModalChange(Node*); |
| 447 | |
| 448 | Document& m_document; |
| 449 | HashMap<AXID, RefPtr<AccessibilityObject>> m_objects; |
| 450 | HashMap<RenderObject*, AXID> m_renderObjectMapping; |
| 451 | HashMap<Widget*, AXID> m_widgetObjectMapping; |
| 452 | HashMap<Node*, AXID> m_nodeObjectMapping; |
| 453 | ListHashSet<Node*> m_textMarkerNodes; |
| 454 | std::unique_ptr<AXComputedObjectAttributeCache> m_computedObjectAttributeCache; |
| 455 | WEBCORE_EXPORT static bool gAccessibilityEnabled; |
| 456 | WEBCORE_EXPORT static bool gAccessibilityEnhancedUserInterfaceEnabled; |
| 457 | |
| 458 | HashSet<AXID> m_idsInUse; |
| 459 | |
| 460 | Timer m_notificationPostTimer; |
| 461 | Vector<std::pair<RefPtr<AccessibilityObject>, AXNotification>> m_notificationsToPost; |
| 462 | |
| 463 | Timer m_passwordNotificationPostTimer; |
| 464 | |
| 465 | ListHashSet<RefPtr<AccessibilityObject>> m_passwordNotificationsToPost; |
| 466 | |
| 467 | Timer m_liveRegionChangedPostTimer; |
| 468 | ListHashSet<RefPtr<AccessibilityObject>> m_liveRegionObjectsSet; |
| 469 | |
| 470 | Timer m_focusModalNodeTimer; |
| 471 | Node* m_currentModalNode; |
| 472 | ListHashSet<Node*> m_modalNodesSet; |
| 473 | |
| 474 | Timer m_performCacheUpdateTimer; |
| 475 | |
| 476 | AXTextStateChangeIntent m_textSelectionIntent; |
| 477 | ListHashSet<Element*> m_deferredRecomputeIsIgnoredList; |
| 478 | ListHashSet<Node*> m_deferredTextChangedList; |
| 479 | ListHashSet<Element*> m_deferredSelectedChildredChangedList; |
| 480 | ListHashSet<RefPtr<AccessibilityObject>> m_deferredChildredChangedList; |
| 481 | ListHashSet<Node*> m_deferredChildrenChangedNodeList; |
| 482 | HashMap<Element*, String> m_deferredTextFormControlValue; |
| 483 | HashMap<Element*, QualifiedName> m_deferredAttributeChange; |
| 484 | Vector<std::pair<Node*, Node*>> m_deferredFocusedNodeChange; |
| 485 | bool m_isSynchronizingSelection { false }; |
| 486 | bool m_performingDeferredCacheUpdate { false }; |
| 487 | |
| 488 | #if PLATFORM(GTK) |
| 489 | ListHashSet<RefPtr<AccessibilityObject>> m_deferredAttachedWrapperObjectList; |
| 490 | ListHashSet<GRefPtr<AccessibilityObjectWrapper>> m_deferredDetachedWrapperList; |
| 491 | #endif |
| 492 | }; |
| 493 | |
| 494 | class AXAttributeCacheEnabler |
| 495 | { |
| 496 | public: |
| 497 | explicit AXAttributeCacheEnabler(AXObjectCache *cache); |
| 498 | ~AXAttributeCacheEnabler(); |
| 499 | |
| 500 | #if HAVE(ACCESSIBILITY) |
| 501 | private: |
| 502 | AXObjectCache* m_cache; |
| 503 | #endif |
| 504 | }; |
| 505 | |
| 506 | bool nodeHasRole(Node*, const String& role); |
| 507 | // This will let you know if aria-hidden was explicitly set to false. |
| 508 | bool isNodeAriaVisible(Node*); |
| 509 | |
| 510 | #if !HAVE(ACCESSIBILITY) |
| 511 | inline AccessibilityObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID) const { return AccessibilityObjectInclusion::DefaultBehavior; } |
| 512 | inline AccessibilityReplacedText::AccessibilityReplacedText(const VisibleSelection&) { } |
| 513 | inline void AccessibilityReplacedText::postTextStateChangeNotification(AXObjectCache*, AXTextEditType, const String&, const VisibleSelection&) { } |
| 514 | inline void AXComputedObjectAttributeCache::setIgnored(AXID, AccessibilityObjectInclusion) { } |
| 515 | inline AXObjectCache::AXObjectCache(Document& document) : m_document(document), m_notificationPostTimer(*this, &AXObjectCache::notificationPostTimerFired), m_passwordNotificationPostTimer(*this, &AXObjectCache::passwordNotificationPostTimerFired), m_liveRegionChangedPostTimer(*this, &AXObjectCache::liveRegionChangedNotificationPostTimerFired), m_focusModalNodeTimer(*this, &AXObjectCache::focusModalNodeTimerFired), m_performCacheUpdateTimer(*this, &AXObjectCache::performCacheUpdateTimerFired) { } |
| 516 | inline AXObjectCache::~AXObjectCache() { } |
| 517 | inline AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page*) { return nullptr; } |
| 518 | inline AccessibilityObject* AXObjectCache::get(RenderObject*) { return nullptr; } |
| 519 | inline AccessibilityObject* AXObjectCache::get(Node*) { return nullptr; } |
| 520 | inline AccessibilityObject* AXObjectCache::get(Widget*) { return nullptr; } |
| 521 | inline AccessibilityObject* AXObjectCache::getOrCreate(RenderObject*) { return nullptr; } |
| 522 | inline AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole) { return nullptr; } |
| 523 | inline AccessibilityObject* AXObjectCache::getOrCreate(Node*) { return nullptr; } |
| 524 | inline AccessibilityObject* AXObjectCache::getOrCreate(Widget*) { return nullptr; } |
| 525 | inline AccessibilityObject* AXObjectCache::rootObject() { return nullptr; } |
| 526 | inline AccessibilityObject* AXObjectCache::rootObjectForFrame(Frame*) { return nullptr; } |
| 527 | inline bool nodeHasRole(Node*, const String&) { return false; } |
| 528 | inline void AXObjectCache::startCachingComputedObjectAttributesUntilTreeMutates() { } |
| 529 | inline void AXObjectCache::stopCachingComputedObjectAttributes() { } |
| 530 | inline bool isNodeAriaVisible(Node*) { return true; } |
| 531 | inline const Element* AXObjectCache::rootAXEditableElement(const Node*) { return nullptr; } |
| 532 | inline Node* AXObjectCache::modalNode() { return nullptr; } |
| 533 | inline void AXObjectCache::attachWrapper(AccessibilityObject*) { } |
| 534 | inline void AXObjectCache::checkedStateChanged(Node*) { } |
| 535 | inline void AXObjectCache::childrenChanged(AccessibilityObject*) { } |
| 536 | inline void AXObjectCache::childrenChanged(Node*, Node*) { } |
| 537 | inline void AXObjectCache::childrenChanged(RenderObject*, RenderObject*) { } |
| 538 | inline void AXObjectCache::deferFocusedUIElementChangeIfNeeded(Node*, Node*) { } |
| 539 | inline void AXObjectCache::deferRecomputeIsIgnoredIfNeeded(Element*) { } |
| 540 | inline void AXObjectCache::deferRecomputeIsIgnored(Element*) { } |
| 541 | inline void AXObjectCache::deferTextChangedIfNeeded(Node*) { } |
| 542 | inline void AXObjectCache::deferSelectedChildrenChangedIfNeeded(Element&) { } |
| 543 | inline void AXObjectCache::deferTextReplacementNotificationForTextControl(HTMLTextFormControlElement&, const String&) { } |
| 544 | inline void AXObjectCache::detachWrapper(AccessibilityObject*, AccessibilityDetachmentType) { } |
| 545 | inline void AXObjectCache::focusModalNodeTimerFired() { } |
| 546 | inline void AXObjectCache::performCacheUpdateTimerFired() { } |
| 547 | inline void AXObjectCache::frameLoadingEventNotification(Frame*, AXLoadingEvent) { } |
| 548 | inline void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject*, AXLoadingEvent) { } |
| 549 | inline void AXObjectCache::handleActiveDescendantChanged(Node*) { } |
| 550 | inline void AXObjectCache::handleAriaExpandedChange(Node*) { } |
| 551 | inline void AXObjectCache::handleModalChange(Node*) { } |
| 552 | inline void AXObjectCache::handleAriaRoleChanged(Node*) { } |
| 553 | inline void AXObjectCache::deferAttributeChangeIfNeeded(const QualifiedName&, Element*) { } |
| 554 | inline void AXObjectCache::handleAttributeChange(const QualifiedName&, Element*) { } |
| 555 | inline bool AXObjectCache::shouldProcessAttributeChange(const QualifiedName&, Element*) { return false; } |
| 556 | inline void AXObjectCache::handleFocusedUIElementChanged(Node*, Node*) { } |
| 557 | inline void AXObjectCache::handleScrollbarUpdate(ScrollView*) { } |
| 558 | inline void AXObjectCache::handleScrolledToAnchor(const Node*) { } |
| 559 | inline void AXObjectCache::liveRegionChangedNotificationPostTimerFired() { } |
| 560 | inline void AXObjectCache::notificationPostTimerFired() { } |
| 561 | inline void AXObjectCache::passwordNotificationPostTimerFired() { } |
| 562 | inline void AXObjectCache::performDeferredCacheUpdate() { } |
| 563 | inline void AXObjectCache::postLiveRegionChangeNotification(AccessibilityObject*) { } |
| 564 | inline void AXObjectCache::postNotification(AccessibilityObject*, Document*, AXNotification, PostTarget, PostType) { } |
| 565 | inline void AXObjectCache::postNotification(Node*, AXNotification, PostTarget, PostType) { } |
| 566 | inline void AXObjectCache::postNotification(RenderObject*, AXNotification, PostTarget, PostType) { } |
| 567 | inline void AXObjectCache::postPlatformNotification(AccessibilityObject*, AXNotification) { } |
| 568 | inline void AXObjectCache::postTextReplacementNotification(Node*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&) { } |
| 569 | inline void AXObjectCache::postTextReplacementNotificationForTextControl(HTMLTextFormControlElement&, const String&, const String&) { } |
| 570 | inline void AXObjectCache::postTextStateChangeNotification(Node*, AXTextEditType, const String&, const VisiblePosition&) { } |
| 571 | inline void AXObjectCache::postTextStateChangeNotification(Node*, const AXTextStateChangeIntent&, const VisibleSelection&) { } |
| 572 | inline void AXObjectCache::recomputeIsIgnored(RenderObject*) { } |
| 573 | inline void AXObjectCache::textChanged(AccessibilityObject*) { } |
| 574 | inline void AXObjectCache::textChanged(Node*) { } |
| 575 | inline void AXObjectCache::updateCacheAfterNodeIsAttached(Node*) { } |
| 576 | inline RefPtr<Range> AXObjectCache::rangeForNodeContents(Node*) { return nullptr; } |
| 577 | inline void AXObjectCache::remove(AXID) { } |
| 578 | inline void AXObjectCache::remove(RenderObject*) { } |
| 579 | inline void AXObjectCache::remove(Node&) { } |
| 580 | inline void AXObjectCache::remove(Widget*) { } |
| 581 | inline void AXObjectCache::selectedChildrenChanged(RenderObject*) { } |
| 582 | inline void AXObjectCache::selectedChildrenChanged(Node*) { } |
| 583 | inline void AXObjectCache::setIsSynchronizingSelection(bool) { } |
| 584 | inline void AXObjectCache::setTextSelectionIntent(const AXTextStateChangeIntent&) { } |
| 585 | inline RefPtr<Range> AXObjectCache::rangeForUnorderedCharacterOffsets(const CharacterOffset&, const CharacterOffset&) { return nullptr; } |
| 586 | inline IntRect AXObjectCache::absoluteCaretBoundsForCharacterOffset(const CharacterOffset&) { return IntRect(); } |
| 587 | inline CharacterOffset AXObjectCache::characterOffsetForIndex(int, const AccessibilityObject*) { return CharacterOffset(); } |
| 588 | inline CharacterOffset AXObjectCache::startOrEndCharacterOffsetForRange(RefPtr<Range>, bool, bool) { return CharacterOffset(); } |
| 589 | inline CharacterOffset AXObjectCache::endCharacterOffsetOfLine(const CharacterOffset&) { return CharacterOffset(); } |
| 590 | inline CharacterOffset AXObjectCache::nextCharacterOffset(const CharacterOffset&, bool) { return CharacterOffset(); } |
| 591 | inline CharacterOffset AXObjectCache::previousCharacterOffset(const CharacterOffset&, bool) { return CharacterOffset(); } |
| 592 | #if PLATFORM(COCOA) |
| 593 | inline void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&) { } |
| 594 | inline void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject*, AXTextEditType, const String&, const VisiblePosition&) { } |
| 595 | inline void AXObjectCache::postTextReplacementPlatformNotification(AccessibilityObject*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&) { } |
| 596 | #else |
| 597 | inline AXTextChange AXObjectCache::textChangeForEditType(AXTextEditType) { return AXTextInserted; } |
| 598 | inline void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&) { } |
| 599 | #endif |
| 600 | |
| 601 | inline AXAttributeCacheEnabler::AXAttributeCacheEnabler(AXObjectCache*) { } |
| 602 | inline AXAttributeCacheEnabler::~AXAttributeCacheEnabler() { } |
| 603 | |
| 604 | #endif |
| 605 | |
| 606 | } // namespace WebCore |
| 607 | |