fbc5b9b799622fe18160ba902f3caf687e31ebe0
[WebKit-https.git] / WebCore / page / mac / AXObjectCacheMac.mm
1 /*
2  * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Computer, 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 COMPUTER, 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 COMPUTER, 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 #import "config.h"
27 #import "AXObjectCache.h"
28
29 #import "AccessibilityObject.h"
30 #import "AccessibilityObjectWrapper.h"
31 #import "RenderObject.h"
32 #import "WebCoreViewFactory.h"
33
34 #import <wtf/PassRefPtr.h>
35
36 // The simple Cocoa calls in this file don't throw exceptions.
37
38 namespace WebCore {
39
40 struct TextMarkerData  {
41     AXID axID;
42     Node* node;
43     int offset;
44     EAffinity affinity;
45 };
46
47 bool AXObjectCache::gAccessibilityEnabled = false;
48
49 AXObjectCache::~AXObjectCache()
50 {
51     HashMap<RenderObject*, RefPtr<AccessibilityObject> >::iterator end = m_objects.end();
52     for (HashMap<RenderObject*, RefPtr<AccessibilityObject> >::iterator it = m_objects.begin(); it != end; ++it) {
53         AccessibilityObject* obj = (*it).second.get();
54         [obj->wrapper() detach];
55         obj->detach();
56     }
57 }
58
59 AccessibilityObject* AXObjectCache::get(RenderObject* renderer)
60 {
61     RefPtr<AccessibilityObject> obj = m_objects.get(renderer).get();
62     if (obj)
63         return obj.get();
64     obj = AccessibilityObject::create(renderer);
65     m_objects.set(renderer, obj);    
66     obj->setWrapper([[AccessibilityObjectWrapper alloc] initWithAccessibilityObject:obj.get()]);
67     return obj.get();
68 }
69
70 void AXObjectCache::remove(RenderObject* renderer)
71 {
72     // first fetch object to operate some cleanup functions on it 
73     AccessibilityObject* obj = m_objects.get(renderer).get();
74     if (obj) {
75         [obj->wrapper() detach];
76         obj->detach();
77         
78         // finally remove the object
79         if (!m_objects.take(renderer)) {
80             ASSERT(!renderer->hasAXObject());
81             return;
82         }
83     }
84     ASSERT(m_objects.size() >= m_idsInUse.size());
85 }
86
87 AXID AXObjectCache::getAXID(AccessibilityObject* obj)
88 {
89     // check for already-assigned ID
90     AXID objID = obj->axObjectID();
91     if (objID) {
92         ASSERT(m_idsInUse.contains(objID));
93         return objID;
94     }
95
96     // generate a new ID
97     static AXID lastUsedID = 0;
98     objID = lastUsedID;
99     do
100         ++objID;
101     while (objID == 0 || objID == AXIDHashTraits::deletedValue() || m_idsInUse.contains(objID));
102     m_idsInUse.add(objID);
103     lastUsedID = objID;
104     obj->setAXObjectID(objID);
105
106     return objID;
107 }
108
109 void AXObjectCache::removeAXID(AccessibilityObject* obj)
110 {
111     AXID objID = obj->axObjectID();
112     if (objID == 0)
113         return;
114     ASSERT(objID != AXIDHashTraits::deletedValue());
115     ASSERT(m_idsInUse.contains(objID));
116     obj->setAXObjectID(0);
117     m_idsInUse.remove(objID);
118 }
119
120 WebCoreTextMarker* AXObjectCache::textMarkerForVisiblePosition(const VisiblePosition& visiblePos)
121 {
122     Position deepPos = visiblePos.deepEquivalent();
123     Node* domNode = deepPos.node();
124     ASSERT(domNode);
125     if (!domNode)
126         return nil;
127     
128     // locate the renderer, which must exist for a visible dom node
129     RenderObject* renderer = domNode->renderer();
130     ASSERT(renderer);
131     
132     // find or create an accessibility object for this renderer
133     RefPtr<AccessibilityObject> obj = get(renderer);
134     
135     // create a text marker, adding an ID for the AccessibilityObject if needed
136     TextMarkerData textMarkerData;
137     textMarkerData.axID = getAXID(obj.get());
138     textMarkerData.node = domNode;
139     textMarkerData.offset = deepPos.offset();
140     textMarkerData.affinity = visiblePos.affinity();
141     return [[WebCoreViewFactory sharedFactory] textMarkerWithBytes:&textMarkerData length:sizeof(textMarkerData)]; 
142 }
143
144 VisiblePosition AXObjectCache::visiblePositionForTextMarker(WebCoreTextMarker* textMarker)
145 {
146     TextMarkerData textMarkerData;
147     
148     if (![[WebCoreViewFactory sharedFactory] getBytes:&textMarkerData fromTextMarker:textMarker length:sizeof(textMarkerData)])
149         return VisiblePosition();
150
151     // return empty position if the text marker is no longer valid
152     if (!m_idsInUse.contains(textMarkerData.axID))
153         return VisiblePosition();
154
155     // generate a VisiblePosition from the data we stored earlier
156     VisiblePosition visiblePos = VisiblePosition(textMarkerData.node, textMarkerData.offset, textMarkerData.affinity);
157
158     // make sure the node and offset still match (catches stale markers). affinity is not critical for this.
159     Position deepPos = visiblePos.deepEquivalent();
160     if (deepPos.node() != textMarkerData.node || deepPos.offset() != textMarkerData.offset)
161         return VisiblePosition();
162     
163     return visiblePos;
164 }
165
166 void AXObjectCache::childrenChanged(RenderObject* renderer)
167 {
168     AccessibilityObject* obj = m_objects.get(renderer).get();
169     if (obj)
170         obj->childrenChanged();
171 }
172
173 void AXObjectCache::postNotification(RenderObject* renderer, const String& message)
174 {
175     if (!renderer)
176         return;
177     
178     // notifications for text input objects are sent to that object
179     // all others are sent to the top WebArea
180     RefPtr<AccessibilityObject> obj = get(renderer)->observableObject();
181     if (!obj)
182         obj = get(renderer->document()->renderer());
183         
184     if (!obj)
185         return;
186
187     NSAccessibilityPostNotification(obj->wrapper(), message);
188 }
189
190 void AXObjectCache::postNotificationToElement(RenderObject* renderer, const String& message)
191 {
192     // send the notification to the specified element itself, not one of its ancestors
193     if (!renderer)
194         return;
195
196     RefPtr<AccessibilityObject> obj = get(renderer);
197     if (!obj)
198         return;
199
200     NSAccessibilityPostNotification(obj->wrapper(), message);
201 }
202
203 void AXObjectCache::handleFocusedUIElementChanged()
204 {
205     [[WebCoreViewFactory sharedFactory] accessibilityHandleFocusChanged];
206 }
207
208 }