1/*
2 * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2011 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 "AccessibilityObject.h"
30#include "Timer.h"
31#include <limits.h>
32#include <wtf/Forward.h>
33#include <wtf/HashMap.h>
34#include <wtf/HashSet.h>
35#include <wtf/RefPtr.h>
36
37namespace WebCore {
38
39class Document;
40class HTMLAreaElement;
41class Node;
42class Page;
43class RenderObject;
44class ScrollView;
45class VisiblePosition;
46class Widget;
47
48struct TextMarkerData {
49    AXID axID;
50    Node* node;
51    int offset;
52    EAffinity affinity;
53};
54
55class AXComputedObjectAttributeCache {
56public:
57    AccessibilityObjectInclusion getIgnored(AXID) const;
58    void setIgnored(AXID, AccessibilityObjectInclusion);
59
60private:
61    struct CachedAXObjectAttributes {
62        CachedAXObjectAttributes() : ignored(DefaultBehavior) { }
63
64        AccessibilityObjectInclusion ignored;
65    };
66
67    HashMap<AXID, CachedAXObjectAttributes> m_idMapping;
68};
69
70enum PostTarget { TargetElement, TargetObservableParent };
71
72enum PostType { PostSynchronously, PostAsynchronously };
73
74class AXObjectCache {
75    WTF_MAKE_NONCOPYABLE(AXObjectCache); WTF_MAKE_FAST_ALLOCATED;
76public:
77    explicit AXObjectCache(Document&);
78    ~AXObjectCache();
79
80    static AccessibilityObject* focusedUIElementForPage(const Page*);
81
82    // Returns the root object for the entire document.
83    AccessibilityObject* rootObject();
84    // Returns the root object for a specific frame.
85    AccessibilityObject* rootObjectForFrame(Frame*);
86
87    // For AX objects with elements that back them.
88    AccessibilityObject* getOrCreate(RenderObject*);
89    AccessibilityObject* getOrCreate(Widget*);
90    AccessibilityObject* getOrCreate(Node*);
91
92    // used for objects without backing elements
93    AccessibilityObject* getOrCreate(AccessibilityRole);
94
95    // will only return the AccessibilityObject if it already exists
96    AccessibilityObject* get(RenderObject*);
97    AccessibilityObject* get(Widget*);
98    AccessibilityObject* get(Node*);
99
100    void remove(RenderObject*);
101    void remove(Node*);
102    void remove(Widget*);
103    void remove(AXID);
104
105    void detachWrapper(AccessibilityObject*, AccessibilityDetachmentType);
106    void attachWrapper(AccessibilityObject*);
107    void childrenChanged(Node*, Node* newChild = nullptr);
108    void childrenChanged(RenderObject*, RenderObject* newChild = nullptr);
109    void childrenChanged(AccessibilityObject*);
110    void checkedStateChanged(Node*);
111    void selectedChildrenChanged(Node*);
112    void selectedChildrenChanged(RenderObject*);
113    // Called by a node when text or a text equivalent (e.g. alt) attribute is changed.
114    void textChanged(Node*);
115    void textChanged(RenderObject*);
116    // Called when a node has just been attached, so we can make sure we have the right subclass of AccessibilityObject.
117    void updateCacheAfterNodeIsAttached(Node*);
118
119    void handleActiveDescendantChanged(Node*);
120    void handleAriaRoleChanged(Node*);
121    void handleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode);
122    void handleScrolledToAnchor(const Node* anchorNode);
123    void handleAriaExpandedChange(Node*);
124    void handleScrollbarUpdate(ScrollView*);
125
126    void handleAttributeChanged(const QualifiedName& attrName, Element*);
127    void recomputeIsIgnored(RenderObject* renderer);
128
129#if HAVE(ACCESSIBILITY)
130    static void enableAccessibility();
131    static void disableAccessibility();
132
133    // Enhanced user interface accessibility can be toggled by the assistive technology.
134    static void setEnhancedUserInterfaceAccessibility(bool flag);
135
136    static bool accessibilityEnabled() { return gAccessibilityEnabled; }
137    static bool accessibilityEnhancedUserInterfaceEnabled() { return gAccessibilityEnhancedUserInterfaceEnabled; }
138#else
139    static void enableAccessibility() { }
140    static void disableAccessibility() { }
141    static void setEnhancedUserInterfaceAccessibility(bool) { }
142    static bool accessibilityEnabled() { return false; }
143    static bool accessibilityEnhancedUserInterfaceEnabled() { return false; }
144#endif
145
146    void removeAXID(AccessibilityObject*);
147    bool isIDinUse(AXID id) const { return m_idsInUse.contains(id); }
148
149    Element* rootAXEditableElement(Node*);
150    const Element* rootAXEditableElement(const Node*);
151    bool nodeIsTextControl(const Node*);
152
153    AXID platformGenerateAXID() const;
154    AccessibilityObject* objectFromAXID(AXID id) const { return m_objects.get(id); }
155
156    // Text marker utilities.
157    void textMarkerDataForVisiblePosition(TextMarkerData&, const VisiblePosition&);
158    VisiblePosition visiblePositionForTextMarkerData(TextMarkerData&);
159
160    enum AXNotification {
161        AXActiveDescendantChanged,
162        AXAutocorrectionOccured,
163        AXCheckedStateChanged,
164        AXChildrenChanged,
165        AXFocusedUIElementChanged,
166        AXLayoutComplete,
167        AXLoadComplete,
168        AXSelectedChildrenChanged,
169        AXSelectedTextChanged,
170        AXValueChanged,
171        AXScrolledToAnchor,
172        AXLiveRegionCreated,
173        AXLiveRegionChanged,
174        AXMenuListItemSelected,
175        AXMenuListValueChanged,
176        AXMenuClosed,
177        AXMenuOpened,
178        AXRowCountChanged,
179        AXRowCollapsed,
180        AXRowExpanded,
181        AXExpandedChanged,
182        AXInvalidStatusChanged,
183        AXTextChanged,
184        AXAriaAttributeChanged,
185        AXElementBusyChanged
186    };
187
188    void postNotification(RenderObject*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously);
189    void postNotification(Node*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously);
190    void postNotification(AccessibilityObject*, Document*, AXNotification, PostTarget = TargetElement, PostType = PostAsynchronously);
191
192    enum AXTextChange {
193        AXTextInserted,
194        AXTextDeleted,
195    };
196
197    void nodeTextChangeNotification(Node*, AXTextChange, unsigned offset, const String&);
198
199    enum AXLoadingEvent {
200        AXLoadingStarted,
201        AXLoadingReloaded,
202        AXLoadingFailed,
203        AXLoadingFinished
204    };
205
206    void frameLoadingEventNotification(Frame*, AXLoadingEvent);
207
208    void clearTextMarkerNodesInUse(Document*);
209
210    void startCachingComputedObjectAttributesUntilTreeMutates();
211    void stopCachingComputedObjectAttributes();
212
213    AXComputedObjectAttributeCache* computedObjectAttributeCache() { return m_computedObjectAttributeCache.get(); }
214
215    Document& document() const { return m_document; }
216
217protected:
218    void postPlatformNotification(AccessibilityObject*, AXNotification);
219    void platformHandleFocusedUIElementChanged(Node* oldFocusedNode, Node* newFocusedNode);
220
221    void nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned offset, const String&);
222    void frameLoadingEventPlatformNotification(AccessibilityObject*, AXLoadingEvent);
223    void textChanged(AccessibilityObject*);
224    void labelChanged(Element*);
225
226    // This is a weak reference cache for knowing if Nodes used by TextMarkers are valid.
227    void setNodeInUse(Node* n) { m_textMarkerNodes.add(n); }
228    void removeNodeForUse(Node* n) { m_textMarkerNodes.remove(n); }
229    bool isNodeInUse(Node* n) { return m_textMarkerNodes.contains(n); }
230
231private:
232    Document& m_document;
233    HashMap<AXID, RefPtr<AccessibilityObject>> m_objects;
234    HashMap<RenderObject*, AXID> m_renderObjectMapping;
235    HashMap<Widget*, AXID> m_widgetObjectMapping;
236    HashMap<Node*, AXID> m_nodeObjectMapping;
237    HashSet<Node*> m_textMarkerNodes;
238    std::unique_ptr<AXComputedObjectAttributeCache> m_computedObjectAttributeCache;
239    static bool gAccessibilityEnabled;
240    static bool gAccessibilityEnhancedUserInterfaceEnabled;
241
242    HashSet<AXID> m_idsInUse;
243
244    Timer<AXObjectCache> m_notificationPostTimer;
245    Vector<std::pair<RefPtr<AccessibilityObject>, AXNotification>> m_notificationsToPost;
246    void notificationPostTimerFired(Timer<AXObjectCache>&);
247    void handleMenuOpened(Node*);
248    void handleLiveRegionCreated(Node*);
249    void handleMenuItemSelected(Node*);
250
251    static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement*);
252
253    AXID getAXID(AccessibilityObject*);
254};
255
256class AXAttributeCacheEnabler
257{
258public:
259    explicit AXAttributeCacheEnabler(AXObjectCache *cache);
260    ~AXAttributeCacheEnabler();
261
262private:
263    AXObjectCache* m_cache;
264};
265
266bool nodeHasRole(Node*, const String& role);
267// This will let you know if aria-hidden was explicitly set to false.
268bool isNodeAriaVisible(Node*);
269
270#if !HAVE(ACCESSIBILITY)
271inline AccessibilityObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID) const { return DefaultBehavior; }
272inline void AXComputedObjectAttributeCache::setIgnored(AXID, AccessibilityObjectInclusion) { }
273inline AXObjectCache::AXObjectCache(Document& document) : m_document(document), m_notificationPostTimer(this, (Timer<AXObjectCache>::TimerFiredFunction) nullptr) { }
274inline AXObjectCache::~AXObjectCache() { }
275inline AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page*) { return 0; }
276inline AccessibilityObject* AXObjectCache::get(RenderObject*) { return 0; }
277inline AccessibilityObject* AXObjectCache::get(Node*) { return 0; }
278inline AccessibilityObject* AXObjectCache::get(Widget*) { return 0; }
279inline AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole) { return 0; }
280inline AccessibilityObject* AXObjectCache::getOrCreate(RenderObject*) { return 0; }
281inline AccessibilityObject* AXObjectCache::getOrCreate(Node*) { return 0; }
282inline AccessibilityObject* AXObjectCache::getOrCreate(Widget*) { return 0; }
283inline AccessibilityObject* AXObjectCache::rootObject() { return 0; }
284inline AccessibilityObject* AXObjectCache::rootObjectForFrame(Frame*) { return 0; }
285inline Element* AXObjectCache::rootAXEditableElement(Node*) { return 0; }
286inline bool nodeHasRole(Node*, const String&) { return false; }
287inline void AXObjectCache::startCachingComputedObjectAttributesUntilTreeMutates() { }
288inline void AXObjectCache::stopCachingComputedObjectAttributes() { }
289inline bool isNodeAriaVisible(Node*) { return true; }
290inline const Element* AXObjectCache::rootAXEditableElement(const Node*) { return 0; }
291inline void AXObjectCache::attachWrapper(AccessibilityObject*) { }
292inline void AXObjectCache::checkedStateChanged(Node*) { }
293inline void AXObjectCache::childrenChanged(RenderObject*, RenderObject*) { }
294inline void AXObjectCache::childrenChanged(Node*, Node*) { }
295inline void AXObjectCache::childrenChanged(AccessibilityObject*) { }
296inline void AXObjectCache::textChanged(RenderObject*) { }
297inline void AXObjectCache::textChanged(Node*) { }
298inline void AXObjectCache::textChanged(AccessibilityObject*) { }
299inline void AXObjectCache::updateCacheAfterNodeIsAttached(Node*) { }
300inline void AXObjectCache::detachWrapper(AccessibilityObject*, AccessibilityDetachmentType) { }
301inline void AXObjectCache::frameLoadingEventNotification(Frame*, AXLoadingEvent) { }
302inline void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject*, AXLoadingEvent) { }
303inline void AXObjectCache::handleActiveDescendantChanged(Node*) { }
304inline void AXObjectCache::handleAriaExpandedChange(Node*) { }
305inline void AXObjectCache::handleAriaRoleChanged(Node*) { }
306inline void AXObjectCache::handleFocusedUIElementChanged(Node*, Node*) { }
307inline void AXObjectCache::handleScrollbarUpdate(ScrollView*) { }
308inline void AXObjectCache::handleAttributeChanged(const QualifiedName&, Element*) { }
309inline void AXObjectCache::recomputeIsIgnored(RenderObject*) { }
310inline void AXObjectCache::handleScrolledToAnchor(const Node*) { }
311inline void AXObjectCache::nodeTextChangeNotification(Node*, AXTextChange, unsigned, const String&) { }
312inline void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&) { }
313inline void AXObjectCache::postNotification(AccessibilityObject*, Document*, AXNotification, PostTarget, PostType) { }
314inline void AXObjectCache::postNotification(RenderObject*, AXNotification, PostTarget, PostType) { }
315inline void AXObjectCache::postNotification(Node*, AXNotification, PostTarget, PostType) { }
316inline void AXObjectCache::postPlatformNotification(AccessibilityObject*, AXNotification) { }
317inline void AXObjectCache::remove(AXID) { }
318inline void AXObjectCache::remove(RenderObject*) { }
319inline void AXObjectCache::remove(Node*) { }
320inline void AXObjectCache::remove(Widget*) { }
321inline void AXObjectCache::selectedChildrenChanged(RenderObject*) { }
322inline void AXObjectCache::selectedChildrenChanged(Node*) { }
323#endif
324
325}
326
327#endif
328