AX: crash at WebCore::Range::selectNodeContents(WebCore::Node*, int&)
[WebKit-https.git] / Source / WebCore / accessibility / AXObjectCache.h
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 #ifndef AXObjectCache_h
27 #define AXObjectCache_h
28
29 #include "AXTextStateChangeIntent.h"
30 #include "AccessibilityObject.h"
31 #include "Range.h"
32 #include "Timer.h"
33 #include <limits.h>
34 #include <wtf/Forward.h>
35 #include <wtf/HashMap.h>
36 #include <wtf/HashSet.h>
37 #include <wtf/ListHashSet.h>
38 #include <wtf/RefPtr.h>
39
40 namespace WebCore {
41
42 class Document;
43 class HTMLAreaElement;
44 class Node;
45 class Page;
46 class RenderObject;
47 class ScrollView;
48 class VisiblePosition;
49 class Widget;
50
51 struct TextMarkerData {
52     AXID axID;
53     Node* node;
54     int offset;
55     int characterStartIndex;
56     int characterOffset;
57     bool ignored;
58     EAffinity affinity;
59 };
60
61 struct CharacterOffset {
62     Node* node;
63     int startIndex;
64     int offset;
65     int remainingOffset;
66     
67     CharacterOffset(Node* n = nullptr, int startIndex = 0, int offset = 0, int remaining = 0)
68         : node(n)
69         , startIndex(startIndex)
70         , offset(offset)
71         , remainingOffset(remaining)
72     { }
73     
74     int remaining() const { return remainingOffset; }
75     bool isNull() const { return !node; }
76 };
77
78 class AXComputedObjectAttributeCache {
79 public:
80     AccessibilityObjectInclusion getIgnored(AXID) const;
81     void setIgnored(AXID, AccessibilityObjectInclusion);
82
83 private:
84     struct CachedAXObjectAttributes {
85         CachedAXObjectAttributes() : ignored(DefaultBehavior) { }
86
87         AccessibilityObjectInclusion ignored;
88     };
89
90     HashMap<AXID, CachedAXObjectAttributes> m_idMapping;
91 };
92
93 #if !PLATFORM(COCOA)
94 enum AXTextChange { AXTextInserted, AXTextDeleted, AXTextAttributesChanged };
95 #endif
96
97 enum PostTarget { TargetElement, TargetObservableParent };
98
99 enum PostType { PostSynchronously, PostAsynchronously };
100
101 class AXObjectCache {
102     WTF_MAKE_NONCOPYABLE(AXObjectCache); WTF_MAKE_FAST_ALLOCATED;
103 public:
104     explicit AXObjectCache(Document&);
105     ~AXObjectCache();
106
107     WEBCORE_EXPORT static AccessibilityObject* focusedUIElementForPage(const Page*);
108
109     // Returns the root object for the entire document.
110     WEBCORE_EXPORT AccessibilityObject* rootObject();
111     // Returns the root object for a specific frame.
112     WEBCORE_EXPORT AccessibilityObject* rootObjectForFrame(Frame*);
113     
114     // For AX objects with elements that back them.
115     AccessibilityObject* getOrCreate(RenderObject*);
116     AccessibilityObject* getOrCreate(Widget*);
117     AccessibilityObject* getOrCreate(Node*);
118
119     // used for objects without backing elements
120     AccessibilityObject* getOrCreate(AccessibilityRole);
121     
122     // will only return the AccessibilityObject if it already exists
123     AccessibilityObject* get(RenderObject*);
124     AccessibilityObject* get(Widget*);
125     AccessibilityObject* get(Node*);
126     
127     void remove(RenderObject*);
128     void remove(Node*);
129     void remove(Widget*);
130     void remove(AXID);
131
132     void detachWrapper(AccessibilityObject*, AccessibilityDetachmentType);
133     void attachWrapper(AccessibilityObject*);
134     void childrenChanged(Node*, Node* newChild = nullptr);
135     void childrenChanged(RenderObject*, RenderObject* newChild = nullptr);
136     void childrenChanged(AccessibilityObject*);
137     void checkedStateChanged(Node*);
138     void selectedChildrenChanged(Node*);
139     void selectedChildrenChanged(RenderObject*);
140     // Called by a node when text or a text equivalent (e.g. alt) attribute is changed.
141     void textChanged(Node*);
142     void textChanged(RenderObject*);
143     // Called when a node has just been attached, so we can make sure we have the right subclass of AccessibilityObject.
144     void updateCacheAfterNodeIsAttached(Node*);
145
146     void handleActiveDescendantChanged(Node*);
147     void handleAriaRoleChanged(Node*);
148     void handleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode);
149     void handleScrolledToAnchor(const Node* anchorNode);
150     void handleAriaExpandedChange(Node*);
151     void handleScrollbarUpdate(ScrollView*);
152     
153     void handleAriaModalChange(Node*);
154     Node* ariaModalNode();
155
156     void handleAttributeChanged(const QualifiedName& attrName, Element*);
157     void recomputeIsIgnored(RenderObject* renderer);
158
159 #if HAVE(ACCESSIBILITY)
160     WEBCORE_EXPORT static void enableAccessibility();
161     WEBCORE_EXPORT static void disableAccessibility();
162
163     // Enhanced user interface accessibility can be toggled by the assistive technology.
164     WEBCORE_EXPORT static void setEnhancedUserInterfaceAccessibility(bool flag);
165     
166     static bool accessibilityEnabled() { return gAccessibilityEnabled; }
167     static bool accessibilityEnhancedUserInterfaceEnabled() { return gAccessibilityEnhancedUserInterfaceEnabled; }
168 #else
169     static void enableAccessibility() { }
170     static void disableAccessibility() { }
171     static void setEnhancedUserInterfaceAccessibility(bool) { }
172     static bool accessibilityEnabled() { return false; }
173     static bool accessibilityEnhancedUserInterfaceEnabled() { return false; }
174 #endif
175
176     void removeAXID(AccessibilityObject*);
177     bool isIDinUse(AXID id) const { return m_idsInUse.contains(id); }
178
179     const Element* rootAXEditableElement(const Node*);
180     bool nodeIsTextControl(const Node*);
181
182     AXID platformGenerateAXID() const;
183     AccessibilityObject* objectFromAXID(AXID id) const { return m_objects.get(id); }
184
185     // Text marker utilities.
186     void textMarkerDataForVisiblePosition(TextMarkerData&, const VisiblePosition&);
187     VisiblePosition visiblePositionForTextMarkerData(TextMarkerData&);
188     CharacterOffset characterOffsetForTextMarkerData(TextMarkerData&);
189     void textMarkerDataForCharacterOffset(TextMarkerData&, Node&, int, bool toNodeEnd = false);
190     void startOrEndTextMarkerDataForRange(TextMarkerData&, RefPtr<Range>, bool);
191     AccessibilityObject* accessibilityObjectForTextMarkerData(TextMarkerData&);
192     RefPtr<Range> rangeForUnorderedCharacterOffsets(const CharacterOffset&, const CharacterOffset&);
193     static RefPtr<Range> rangeForNodeContents(Node*);
194     static int lengthForRange(Range*);
195
196     enum AXNotification {
197         AXActiveDescendantChanged,
198         AXAutocorrectionOccured,
199         AXCheckedStateChanged,
200         AXChildrenChanged,
201         AXFocusedUIElementChanged,
202         AXLayoutComplete,
203         AXLoadComplete,
204         AXNewDocumentLoadComplete,
205         AXSelectedChildrenChanged,
206         AXSelectedTextChanged,
207         AXValueChanged,
208         AXScrolledToAnchor,
209         AXLiveRegionCreated,
210         AXLiveRegionChanged,
211         AXMenuListItemSelected,
212         AXMenuListValueChanged,
213         AXMenuClosed,
214         AXMenuOpened,
215         AXRowCountChanged,
216         AXRowCollapsed,
217         AXRowExpanded,
218         AXExpandedChanged,
219         AXInvalidStatusChanged,
220         AXTextChanged,
221         AXAriaAttributeChanged,
222         AXElementBusyChanged
223     };
224
225     void postNotification(RenderObject*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously);
226     void postNotification(Node*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously);
227     void postNotification(AccessibilityObject*, Document*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously);
228
229 #ifndef NDEBUG
230     void showIntent(const AXTextStateChangeIntent&);
231 #endif
232
233     void setTextSelectionIntent(const AXTextStateChangeIntent&);
234     void setIsSynchronizingSelection(bool);
235
236     void postTextStateChangeNotification(Node*, AXTextEditType, const String&, const VisiblePosition&);
237     void postTextReplacementNotification(Node*, AXTextEditType deletionType, const String& deletedText, AXTextEditType insertionType, const String& insertedText, const VisiblePosition&);
238     void postTextStateChangeNotification(Node*, const AXTextStateChangeIntent&, const VisibleSelection&);
239     void postTextStateChangeNotification(const Position&, const AXTextStateChangeIntent&, const VisibleSelection&);
240     void postLiveRegionChangeNotification(AccessibilityObject*);
241
242     enum AXLoadingEvent {
243         AXLoadingStarted,
244         AXLoadingReloaded,
245         AXLoadingFailed,
246         AXLoadingFinished
247     };
248
249     void frameLoadingEventNotification(Frame*, AXLoadingEvent);
250
251     void clearTextMarkerNodesInUse(Document*);
252
253     void startCachingComputedObjectAttributesUntilTreeMutates();
254     void stopCachingComputedObjectAttributes();
255
256     AXComputedObjectAttributeCache* computedObjectAttributeCache() { return m_computedObjectAttributeCache.get(); }
257
258     Document& document() const { return m_document; }
259
260 #if PLATFORM(MAC)
261     static void setShouldRepostNotificationsForTests(bool value);
262 #endif
263
264 protected:
265     void postPlatformNotification(AccessibilityObject*, AXNotification);
266     void platformHandleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode);
267
268 #if PLATFORM(COCOA)
269     void postTextStateChangePlatformNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&);
270     void postTextStateChangePlatformNotification(AccessibilityObject*, AXTextEditType, const String&, const VisiblePosition&);
271     void postTextReplacementPlatformNotification(AccessibilityObject*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&);
272 #else
273     static AXTextChange textChangeForEditType(AXTextEditType);
274     void nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&);
275 #endif
276
277     void frameLoadingEventPlatformNotification(AccessibilityObject*, AXLoadingEvent);
278     void textChanged(AccessibilityObject*);
279     void labelChanged(Element*);
280
281     // This is a weak reference cache for knowing if Nodes used by TextMarkers are valid.
282     void setNodeInUse(Node* n) { m_textMarkerNodes.add(n); }
283     void removeNodeForUse(Node* n) { m_textMarkerNodes.remove(n); }
284     bool isNodeInUse(Node* n) { return m_textMarkerNodes.contains(n); }
285     
286     Node* nextNode(Node*) const;
287     Node* previousNode(Node*) const;
288     CharacterOffset traverseToOffsetInRange(RefPtr<Range>, int, bool, bool stayWithinRange = false);
289     VisiblePosition visiblePositionFromCharacterOffset(AccessibilityObject*, const CharacterOffset&);
290     CharacterOffset characterOffsetFromVisiblePosition(AccessibilityObject*, const VisiblePosition&);
291     void setTextMarkerDataWithCharacterOffset(TextMarkerData&, const CharacterOffset&);
292
293 private:
294     AccessibilityObject* rootWebArea();
295
296     static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement*);
297
298     AXID getAXID(AccessibilityObject*);
299
300     void notificationPostTimerFired();
301
302     void liveRegionChangedNotificationPostTimerFired();
303
304     void postTextStateChangeNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&);
305
306     bool enqueuePasswordValueChangeNotification(AccessibilityObject*);
307     void passwordNotificationPostTimerFired();
308
309     void handleMenuOpened(Node*);
310     void handleLiveRegionCreated(Node*);
311     void handleMenuItemSelected(Node*);
312     
313     // aria-modal related
314     void findAriaModalNodes();
315     void updateCurrentAriaModalNode();
316     bool isNodeVisible(Node*) const;
317
318     Document& m_document;
319     HashMap<AXID, RefPtr<AccessibilityObject>> m_objects;
320     HashMap<RenderObject*, AXID> m_renderObjectMapping;
321     HashMap<Widget*, AXID> m_widgetObjectMapping;
322     HashMap<Node*, AXID> m_nodeObjectMapping;
323     HashSet<Node*> m_textMarkerNodes;
324     std::unique_ptr<AXComputedObjectAttributeCache> m_computedObjectAttributeCache;
325     WEBCORE_EXPORT static bool gAccessibilityEnabled;
326     WEBCORE_EXPORT static bool gAccessibilityEnhancedUserInterfaceEnabled;
327
328     HashSet<AXID> m_idsInUse;
329
330     Timer m_notificationPostTimer;
331     Vector<std::pair<RefPtr<AccessibilityObject>, AXNotification>> m_notificationsToPost;
332
333     Timer m_passwordNotificationPostTimer;
334
335     ListHashSet<RefPtr<AccessibilityObject>> m_passwordNotificationsToPost;
336     
337     Timer m_liveRegionChangedPostTimer;
338     ListHashSet<RefPtr<AccessibilityObject>> m_liveRegionObjectsSet;
339     
340     Node* m_currentAriaModalNode;
341     ListHashSet<Node*> m_ariaModalNodesSet;
342
343     AXTextStateChangeIntent m_textSelectionIntent;
344     bool m_isSynchronizingSelection { false };
345 };
346
347 class AXAttributeCacheEnabler
348 {
349 public:
350     explicit AXAttributeCacheEnabler(AXObjectCache *cache);
351     ~AXAttributeCacheEnabler();
352
353 #if HAVE(ACCESSIBILITY)
354 private:
355     AXObjectCache* m_cache;
356 #endif
357 };
358
359 bool nodeHasRole(Node*, const String& role);
360 // This will let you know if aria-hidden was explicitly set to false.
361 bool isNodeAriaVisible(Node*);
362     
363 #if !HAVE(ACCESSIBILITY)
364 inline AccessibilityObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID) const { return DefaultBehavior; }
365 inline void AXComputedObjectAttributeCache::setIgnored(AXID, AccessibilityObjectInclusion) { }
366 inline AXObjectCache::AXObjectCache(Document& document) : m_document(document), m_notificationPostTimer(nullptr), m_passwordNotificationPostTimer(nullptr), m_liveRegionChangedPostTimer(nullptr) { }
367 inline AXObjectCache::~AXObjectCache() { }
368 inline AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page*) { return nullptr; }
369 inline AccessibilityObject* AXObjectCache::get(RenderObject*) { return nullptr; }
370 inline AccessibilityObject* AXObjectCache::get(Node*) { return nullptr; }
371 inline AccessibilityObject* AXObjectCache::get(Widget*) { return nullptr; }
372 inline AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole) { return nullptr; }
373 inline AccessibilityObject* AXObjectCache::getOrCreate(RenderObject*) { return nullptr; }
374 inline AccessibilityObject* AXObjectCache::getOrCreate(Node*) { return nullptr; }
375 inline AccessibilityObject* AXObjectCache::getOrCreate(Widget*) { return nullptr; }
376 inline AccessibilityObject* AXObjectCache::rootObject() { return nullptr; }
377 inline AccessibilityObject* AXObjectCache::rootObjectForFrame(Frame*) { return nullptr; }
378 inline bool nodeHasRole(Node*, const String&) { return false; }
379 inline void AXObjectCache::startCachingComputedObjectAttributesUntilTreeMutates() { }
380 inline void AXObjectCache::stopCachingComputedObjectAttributes() { }
381 inline bool isNodeAriaVisible(Node*) { return true; }
382 inline const Element* AXObjectCache::rootAXEditableElement(const Node*) { return nullptr; }
383 inline Node* AXObjectCache::ariaModalNode() { return nullptr; }
384 inline void AXObjectCache::attachWrapper(AccessibilityObject*) { }
385 inline void AXObjectCache::checkedStateChanged(Node*) { }
386 inline void AXObjectCache::childrenChanged(RenderObject*, RenderObject*) { }
387 inline void AXObjectCache::childrenChanged(Node*, Node*) { }
388 inline void AXObjectCache::childrenChanged(AccessibilityObject*) { }
389 inline void AXObjectCache::textChanged(RenderObject*) { }
390 inline void AXObjectCache::textChanged(Node*) { }
391 inline void AXObjectCache::textChanged(AccessibilityObject*) { }
392 inline void AXObjectCache::updateCacheAfterNodeIsAttached(Node*) { }
393 inline void AXObjectCache::detachWrapper(AccessibilityObject*, AccessibilityDetachmentType) { }
394 inline void AXObjectCache::frameLoadingEventNotification(Frame*, AXLoadingEvent) { }
395 inline void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject*, AXLoadingEvent) { }
396 inline void AXObjectCache::handleActiveDescendantChanged(Node*) { }
397 inline void AXObjectCache::handleAriaExpandedChange(Node*) { }
398 inline void AXObjectCache::handleAriaRoleChanged(Node*) { }
399 inline void AXObjectCache::handleAriaModalChange(Node*) { }
400 inline void AXObjectCache::handleFocusedUIElementChanged(Node*, Node*) { }
401 inline void AXObjectCache::handleScrollbarUpdate(ScrollView*) { }
402 inline void AXObjectCache::handleAttributeChanged(const QualifiedName&, Element*) { }
403 inline void AXObjectCache::recomputeIsIgnored(RenderObject*) { }
404 inline void AXObjectCache::handleScrolledToAnchor(const Node*) { }
405 inline void AXObjectCache::postTextStateChangeNotification(Node*, const AXTextStateChangeIntent&, const VisibleSelection&) { }
406 inline void AXObjectCache::postTextStateChangeNotification(Node*, AXTextEditType, const String&, const VisiblePosition&) { }
407 inline void AXObjectCache::postTextReplacementNotification(Node*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&) { }
408 inline void AXObjectCache::postNotification(AccessibilityObject*, Document*, AXNotification, PostTarget, PostType) { }
409 inline void AXObjectCache::postNotification(RenderObject*, AXNotification, PostTarget, PostType) { }
410 inline void AXObjectCache::postNotification(Node*, AXNotification, PostTarget, PostType) { }
411 inline void AXObjectCache::postPlatformNotification(AccessibilityObject*, AXNotification) { }
412 inline void AXObjectCache::postLiveRegionChangeNotification(AccessibilityObject*) { }
413 inline RefPtr<Range> AXObjectCache::rangeForNodeContents(Node*) { return nullptr; }
414 inline void AXObjectCache::remove(AXID) { }
415 inline void AXObjectCache::remove(RenderObject*) { }
416 inline void AXObjectCache::remove(Node*) { }
417 inline void AXObjectCache::remove(Widget*) { }
418 inline void AXObjectCache::selectedChildrenChanged(RenderObject*) { }
419 inline void AXObjectCache::selectedChildrenChanged(Node*) { }
420 inline void AXObjectCache::setIsSynchronizingSelection(bool) { }
421 inline void AXObjectCache::setTextSelectionIntent(const AXTextStateChangeIntent&) { }
422 #if PLATFORM(COCOA)
423 inline void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject*, const AXTextStateChangeIntent&, const VisibleSelection&) { }
424 inline void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject*, AXTextEditType, const String&, const VisiblePosition&) { }
425 inline void AXObjectCache::postTextReplacementPlatformNotification(AccessibilityObject*, AXTextEditType, const String&, AXTextEditType, const String&, const VisiblePosition&) { }
426 #else
427 inline AXTextChange AXObjectCache::textChangeForEditType(AXTextEditType) { return AXTextInserted; }
428 inline void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&) { }
429 #endif
430
431 inline AXAttributeCacheEnabler::AXAttributeCacheEnabler(AXObjectCache*) { }
432 inline AXAttributeCacheEnabler::~AXAttributeCacheEnabler() { }
433
434 #endif
435
436 }
437
438 #endif