5f969cdee6be0d56956dca84948a59bace216cef
[WebKit-https.git] / WebCore / accessibility / AXObjectCache.cpp
1 /*
2  * Copyright (C) 2008, 2009, 2010 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "AXObjectCache.h"
31
32 #include "AccessibilityARIAGrid.h"
33 #include "AccessibilityARIAGridCell.h"
34 #include "AccessibilityARIAGridRow.h"
35 #include "AccessibilityImageMapLink.h"
36 #include "AccessibilityList.h"
37 #include "AccessibilityListBox.h"
38 #include "AccessibilityListBoxOption.h"
39 #include "AccessibilityMediaControls.h"
40 #include "AccessibilityMenuList.h"
41 #include "AccessibilityMenuListOption.h"
42 #include "AccessibilityMenuListPopup.h"
43 #include "AccessibilityProgressIndicator.h"
44 #include "AccessibilityRenderObject.h"
45 #include "AccessibilityScrollbar.h"
46 #include "AccessibilitySlider.h"
47 #include "AccessibilityTable.h"
48 #include "AccessibilityTableCell.h"
49 #include "AccessibilityTableColumn.h"
50 #include "AccessibilityTableHeaderContainer.h"
51 #include "AccessibilityTableRow.h"
52 #include "FocusController.h"
53 #include "Frame.h"
54 #include "HTMLAreaElement.h"
55 #include "HTMLImageElement.h"
56 #include "HTMLNames.h"
57 #if ENABLE(VIDEO)
58 #include "MediaControlElements.h"
59 #endif
60 #include "InputElement.h"
61 #include "Page.h"
62 #include "RenderListBox.h"
63 #include "RenderMenuList.h"
64 #include "RenderTable.h"
65 #include "RenderTableCell.h"
66 #include "RenderTableRow.h"
67 #include "RenderProgress.h"
68 #include "RenderSlider.h"
69 #include "RenderView.h"
70
71 #include <wtf/PassRefPtr.h>
72
73 namespace WebCore {
74
75 using namespace HTMLNames;
76     
77 bool AXObjectCache::gAccessibilityEnabled = false;
78 bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false;
79
80 AXObjectCache::AXObjectCache()
81     : m_notificationPostTimer(this, &AXObjectCache::notificationPostTimerFired)
82 {
83 }
84
85 AXObjectCache::~AXObjectCache()
86 {
87     HashMap<AXID, RefPtr<AccessibilityObject> >::iterator end = m_objects.end();
88     for (HashMap<AXID, RefPtr<AccessibilityObject> >::iterator it = m_objects.begin(); it != end; ++it) {
89         AccessibilityObject* obj = (*it).second.get();
90         detachWrapper(obj);
91         obj->detach();
92         removeAXID(obj);
93     }
94 }
95
96 AccessibilityObject* AXObjectCache::focusedImageMapUIElement(HTMLAreaElement* areaElement)
97 {
98     // Find the corresponding accessibility object for the HTMLAreaElement. This should be
99     // in the list of children for its corresponding image.
100     if (!areaElement)
101         return 0;
102     
103     HTMLImageElement* imageElement = areaElement->imageElement();
104     if (!imageElement)
105         return 0;
106     
107     AccessibilityObject* axRenderImage = areaElement->document()->axObjectCache()->getOrCreate(imageElement->renderer());
108     if (!axRenderImage)
109         return 0;
110     
111     AccessibilityObject::AccessibilityChildrenVector imageChildren = axRenderImage->children();
112     unsigned count = imageChildren.size();
113     for (unsigned k = 0; k < count; ++k) {
114         AccessibilityObject* child = imageChildren[k].get();
115         if (!child->isImageMapLink())
116             continue;
117         
118         if (static_cast<AccessibilityImageMapLink*>(child)->areaElement() == areaElement)
119             return child;
120     }    
121     
122     return 0;
123 }
124     
125 AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page)
126 {
127     // get the focused node in the page
128     Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document();
129     Node* focusedNode = focusedDocument->focusedNode();
130     if (!focusedNode)
131         focusedNode = focusedDocument;
132
133     if (focusedNode->hasTagName(areaTag))
134         return focusedImageMapUIElement(static_cast<HTMLAreaElement*>(focusedNode));
135     
136     RenderObject* focusedNodeRenderer = focusedNode->renderer();
137     if (!focusedNodeRenderer)
138         return 0;
139
140     AccessibilityObject* obj = focusedNodeRenderer->document()->axObjectCache()->getOrCreate(focusedNodeRenderer);
141
142     if (obj->shouldFocusActiveDescendant()) {
143         if (AccessibilityObject* descendant = obj->activeDescendant())
144             obj = descendant;
145     }
146
147     // the HTML element, for example, is focusable but has an AX object that is ignored
148     if (obj->accessibilityIsIgnored())
149         obj = obj->parentObjectUnignored();
150
151     return obj;
152 }
153
154 AccessibilityObject* AXObjectCache::get(RenderObject* renderer)
155 {
156     if (!renderer)
157         return 0;
158     
159     AccessibilityObject* obj = 0;
160     AXID axID = m_renderObjectMapping.get(renderer);
161     ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
162
163     if (axID)
164         obj = m_objects.get(axID).get();
165     
166     return obj;
167 }
168
169 // FIXME: This probably belongs on Node.
170 // FIXME: This should take a const char*, but one caller passes nullAtom.
171 bool nodeHasRole(Node* node, const String& role)
172 {
173     if (!node || !node->isElementNode())
174         return false;
175
176     return equalIgnoringCase(static_cast<Element*>(node)->getAttribute(roleAttr), role);
177 }
178
179 static PassRefPtr<AccessibilityObject> createFromRenderer(RenderObject* renderer)
180 {
181     // FIXME: How could renderer->node() ever not be an Element?
182     Node* node = renderer->node();
183
184     // If the node is aria role="list" or the aria role is empty and its a
185     // ul/ol/dl type (it shouldn't be a list if aria says otherwise).
186     if (node && ((nodeHasRole(node, "list") || nodeHasRole(node, "directory"))
187                       || (nodeHasRole(node, nullAtom) && (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(dlTag)))))
188         return AccessibilityList::create(renderer);
189
190     // aria tables
191     if (nodeHasRole(node, "grid") || nodeHasRole(node, "treegrid"))
192         return AccessibilityARIAGrid::create(renderer);
193     if (nodeHasRole(node, "row"))
194         return AccessibilityARIAGridRow::create(renderer);
195     if (nodeHasRole(node, "gridcell") || nodeHasRole(node, "columnheader") || nodeHasRole(node, "rowheader"))
196         return AccessibilityARIAGridCell::create(renderer);
197
198 #if ENABLE(VIDEO)
199     // media controls
200     if (node && node->isMediaControlElement())
201         return AccessibilityMediaControl::create(renderer);
202 #endif
203
204     if (renderer->isBoxModelObject()) {
205         RenderBoxModelObject* cssBox = toRenderBoxModelObject(renderer);
206         if (cssBox->isListBox())
207             return AccessibilityListBox::create(toRenderListBox(cssBox));
208         if (cssBox->isMenuList())
209             return AccessibilityMenuList::create(toRenderMenuList(cssBox));
210
211         // standard tables
212         if (cssBox->isTable())
213             return AccessibilityTable::create(toRenderTable(cssBox));
214         if (cssBox->isTableRow())
215             return AccessibilityTableRow::create(toRenderTableRow(cssBox));
216         if (cssBox->isTableCell())
217             return AccessibilityTableCell::create(toRenderTableCell(cssBox));
218
219 #if ENABLE(PROGRESS_TAG)
220         // progress bar
221         if (cssBox->isProgress())
222             return AccessibilityProgressIndicator::create(toRenderProgress(cssBox));
223 #endif
224
225         // input type=range
226         if (cssBox->isSlider())
227             return AccessibilitySlider::create(toRenderSlider(cssBox));
228     }
229
230     return AccessibilityRenderObject::create(renderer);
231 }
232
233 AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
234 {
235     if (!renderer)
236         return 0;
237
238     if (AccessibilityObject* obj = get(renderer))
239         return obj;
240
241     RefPtr<AccessibilityObject> newObj = createFromRenderer(renderer);
242
243     getAXID(newObj.get());
244
245     m_renderObjectMapping.set(renderer, newObj->axObjectID());
246     m_objects.set(newObj->axObjectID(), newObj);
247     attachWrapper(newObj.get());
248     return newObj.get();
249 }
250
251 AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole role)
252 {
253     RefPtr<AccessibilityObject> obj = 0;
254     
255     // will be filled in...
256     switch (role) {
257     case ListBoxOptionRole:
258         obj = AccessibilityListBoxOption::create();
259         break;
260     case ImageMapLinkRole:
261         obj = AccessibilityImageMapLink::create();
262         break;
263     case ColumnRole:
264         obj = AccessibilityTableColumn::create();
265         break;            
266     case TableHeaderContainerRole:
267         obj = AccessibilityTableHeaderContainer::create();
268         break;   
269     case SliderThumbRole:
270         obj = AccessibilitySliderThumb::create();
271         break;
272     case MenuListPopupRole:
273         obj = AccessibilityMenuListPopup::create();
274         break;
275     case MenuListOptionRole:
276         obj = AccessibilityMenuListOption::create();
277         break;
278     case ScrollBarRole:
279         obj = AccessibilityScrollbar::create();
280         break;
281     default:
282         obj = 0;
283     }
284     
285     if (obj)
286         getAXID(obj.get());
287     else
288         return 0;
289
290     m_objects.set(obj->axObjectID(), obj);    
291     attachWrapper(obj.get());
292     return obj.get();
293 }
294
295 void AXObjectCache::remove(AXID axID)
296 {
297     if (!axID)
298         return;
299     
300     // first fetch object to operate some cleanup functions on it 
301     AccessibilityObject* obj = m_objects.get(axID).get();
302     if (!obj)
303         return;
304     
305     detachWrapper(obj);
306     obj->detach();
307     removeAXID(obj);
308     
309     // finally remove the object
310     if (!m_objects.take(axID))
311         return;
312     
313     ASSERT(m_objects.size() >= m_idsInUse.size());    
314 }
315     
316 void AXObjectCache::remove(RenderObject* renderer)
317 {
318     if (!renderer)
319         return;
320     
321     AXID axID = m_renderObjectMapping.get(renderer);
322     remove(axID);
323     m_renderObjectMapping.remove(renderer);
324 }
325
326 #if !PLATFORM(WIN) || OS(WINCE)
327 AXID AXObjectCache::platformGenerateAXID() const
328 {
329     static AXID lastUsedID = 0;
330
331     // Generate a new ID.
332     AXID objID = lastUsedID;
333     do {
334         ++objID;
335     } while (!objID || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
336
337     lastUsedID = objID;
338
339     return objID;
340 }
341 #endif
342
343 AXID AXObjectCache::getAXID(AccessibilityObject* obj)
344 {
345     // check for already-assigned ID
346     AXID objID = obj->axObjectID();
347     if (objID) {
348         ASSERT(m_idsInUse.contains(objID));
349         return objID;
350     }
351
352     objID = platformGenerateAXID();
353
354     m_idsInUse.add(objID);
355     obj->setAXObjectID(objID);
356     
357     return objID;
358 }
359
360 void AXObjectCache::removeAXID(AccessibilityObject* object)
361 {
362     if (!object)
363         return;
364     
365     AXID objID = object->axObjectID();
366     if (!objID)
367         return;
368     ASSERT(!HashTraits<AXID>::isDeletedValue(objID));
369     ASSERT(m_idsInUse.contains(objID));
370     object->setAXObjectID(0);
371     m_idsInUse.remove(objID);
372 }
373
374 #if HAVE(ACCESSIBILITY)
375 void AXObjectCache::contentChanged(RenderObject* renderer)
376 {
377     AccessibilityObject* object = getOrCreate(renderer);
378     if (object)
379         object->contentChanged(); 
380 }
381 #endif
382
383 void AXObjectCache::childrenChanged(RenderObject* renderer)
384 {
385     if (!renderer)
386         return;
387  
388     AXID axID = m_renderObjectMapping.get(renderer);
389     if (!axID)
390         return;
391     
392     AccessibilityObject* obj = m_objects.get(axID).get();
393     if (obj)
394         obj->childrenChanged();
395 }
396     
397 void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*)
398 {
399     m_notificationPostTimer.stop();
400
401     unsigned i = 0, count = m_notificationsToPost.size();
402     for (i = 0; i < count; ++i) {
403         AccessibilityObject* obj = m_notificationsToPost[i].first.get();
404 #ifndef NDEBUG
405         // Make sure none of the render views are in the process of being layed out.
406         // Notifications should only be sent after the renderer has finished
407         if (obj->isAccessibilityRenderObject()) {
408             AccessibilityRenderObject* renderObj = static_cast<AccessibilityRenderObject*>(obj);
409             RenderObject* renderer = renderObj->renderer();
410             if (renderer && renderer->view())
411                 ASSERT(!renderer->view()->layoutState());
412         }
413 #endif
414         
415         postPlatformNotification(obj, m_notificationsToPost[i].second);
416     }
417     
418     m_notificationsToPost.clear();
419 }
420     
421 #if HAVE(ACCESSIBILITY)
422 void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, bool postToElement, PostType postType)
423 {
424     // Notifications for text input objects are sent to that object.
425     // All others are sent to the top WebArea.
426     if (!renderer)
427         return;
428     
429     // Get an accessibility object that already exists. One should not be created here
430     // because a render update may be in progress and creating an AX object can re-trigger a layout
431     RefPtr<AccessibilityObject> object = get(renderer);
432     while (!object && renderer) {
433         renderer = renderer->parent();
434         object = get(renderer); 
435     }
436     
437     if (!renderer)
438         return;
439     
440     postNotification(object.get(), renderer->document(), notification, postToElement, postType);
441 }
442
443 void AXObjectCache::postNotification(AccessibilityObject* object, Document* document, AXNotification notification, bool postToElement, PostType postType)
444 {
445     if (object && !postToElement)
446         object = object->observableObject();
447
448     if (!object && document)
449         object = get(document->renderer());
450
451     if (!object)
452         return;
453
454     if (postType == PostAsynchronously) {
455         m_notificationsToPost.append(make_pair(object, notification));
456         if (!m_notificationPostTimer.isActive())
457             m_notificationPostTimer.startOneShot(0);
458     } else
459         postPlatformNotification(object, notification);
460 }
461
462 void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
463 {
464     // postToElement is false so that you can pass in any child of an element and it will go up the parent tree
465     // to find the container which should send out the notification.
466     postNotification(renderer, AXSelectedChildrenChanged, false);
467 }
468
469 void AXObjectCache::nodeTextChangeNotification(RenderObject* renderer, AXTextChange textChange, unsigned offset, unsigned count)
470 {
471     if (!renderer)
472         return;
473
474     // Delegate on the right platform
475     AccessibilityObject* obj = getOrCreate(renderer);
476     nodeTextChangePlatformNotification(obj, textChange, offset, count);
477 }
478 #endif
479
480 #if HAVE(ACCESSIBILITY)
481 void AXObjectCache::handleAriaExpandedChange(RenderObject *renderer)
482 {
483     if (!renderer)
484         return;
485     AccessibilityObject* obj = getOrCreate(renderer);
486     if (obj)
487         obj->handleAriaExpandedChanged();
488 }
489     
490 void AXObjectCache::handleActiveDescendantChanged(RenderObject* renderer)
491 {
492     if (!renderer)
493         return;
494     AccessibilityObject* obj = getOrCreate(renderer);
495     if (obj)
496         obj->handleActiveDescendantChanged();
497 }
498
499 void AXObjectCache::handleAriaRoleChanged(RenderObject* renderer)
500 {
501     if (!renderer)
502         return;
503     AccessibilityObject* obj = getOrCreate(renderer);
504     if (obj && obj->isAccessibilityRenderObject())
505         static_cast<AccessibilityRenderObject*>(obj)->updateAccessibilityRole();
506 }
507 #endif
508
509 VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData& textMarkerData)
510 {
511     if (!isNodeInUse(textMarkerData.node))
512         return VisiblePosition();
513     
514     VisiblePosition visiblePos = VisiblePosition(textMarkerData.node, textMarkerData.offset, textMarkerData.affinity);
515     Position deepPos = visiblePos.deepEquivalent();
516     if (deepPos.isNull())
517         return VisiblePosition();
518     
519     RenderObject* renderer = deepPos.node()->renderer();
520     if (!renderer)
521         return VisiblePosition();
522     
523     AXObjectCache* cache = renderer->document()->axObjectCache();
524     if (!cache->isIDinUse(textMarkerData.axID))
525         return VisiblePosition();
526     
527     if (deepPos.node() != textMarkerData.node || deepPos.deprecatedEditingOffset() != textMarkerData.offset)
528         return VisiblePosition();
529     
530     return visiblePos;
531 }
532
533 void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos)
534 {
535     // This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence.
536     // This also allows callers to check for failure by looking at textMarkerData upon return.
537     memset(&textMarkerData, 0, sizeof(TextMarkerData));
538     
539     if (visiblePos.isNull())
540         return;
541     
542     Position deepPos = visiblePos.deepEquivalent();
543     Node* domNode = deepPos.node();
544     ASSERT(domNode);
545     if (!domNode)
546         return;
547     
548     if (domNode->isHTMLElement()) {
549         InputElement* inputElement = toInputElement(static_cast<Element*>(domNode));
550         if (inputElement && inputElement->isPasswordField())
551             return;
552     }
553     
554     // locate the renderer, which must exist for a visible dom node
555     RenderObject* renderer = domNode->renderer();
556     ASSERT(renderer);
557     
558     // find or create an accessibility object for this renderer
559     AXObjectCache* cache = renderer->document()->axObjectCache();
560     RefPtr<AccessibilityObject> obj = cache->getOrCreate(renderer);
561     
562     textMarkerData.axID = obj.get()->axObjectID();
563     textMarkerData.node = domNode;
564     textMarkerData.offset = deepPos.deprecatedEditingOffset();
565     textMarkerData.affinity = visiblePos.affinity();   
566     
567     cache->setNodeInUse(domNode);
568 }
569     
570 } // namespace WebCore