tizen beta release
[profile/ivi/webkit-efl.git] / Source / 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 "AccessibilityScrollView.h"
46 #include "AccessibilityScrollbar.h"
47 #include "AccessibilitySlider.h"
48 #include "AccessibilitySpinButton.h"
49 #include "AccessibilityTable.h"
50 #include "AccessibilityTableCell.h"
51 #include "AccessibilityTableColumn.h"
52 #include "AccessibilityTableHeaderContainer.h"
53 #include "AccessibilityTableRow.h"
54 #include "Document.h"
55 #include "FocusController.h"
56 #include "Frame.h"
57 #include "HTMLAreaElement.h"
58 #include "HTMLImageElement.h"
59 #include "HTMLInputElement.h"
60 #include "HTMLNames.h"
61 #if ENABLE(VIDEO)
62 #include "MediaControlElements.h"
63 #endif
64 #include "Page.h"
65 #include "RenderListBox.h"
66 #include "RenderMenuList.h"
67 #include "RenderProgress.h"
68 #include "RenderSlider.h"
69 #include "RenderTable.h"
70 #include "RenderTableCell.h"
71 #include "RenderTableRow.h"
72 #include "RenderView.h"
73 #include "ScrollView.h"
74
75 #include <wtf/PassRefPtr.h>
76
77 namespace WebCore {
78
79 using namespace HTMLNames;
80     
81 bool AXObjectCache::gAccessibilityEnabled = false;
82 bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false;
83
84 AXObjectCache::AXObjectCache(const Document* doc)
85     : m_notificationPostTimer(this, &AXObjectCache::notificationPostTimerFired)
86 {
87     m_document = const_cast<Document*>(doc);
88 }
89
90 AXObjectCache::~AXObjectCache()
91 {
92     HashMap<AXID, RefPtr<AccessibilityObject> >::iterator end = m_objects.end();
93     for (HashMap<AXID, RefPtr<AccessibilityObject> >::iterator it = m_objects.begin(); it != end; ++it) {
94         AccessibilityObject* obj = (*it).second.get();
95         detachWrapper(obj);
96         obj->detach();
97         removeAXID(obj);
98     }
99 }
100
101 AccessibilityObject* AXObjectCache::focusedImageMapUIElement(HTMLAreaElement* areaElement)
102 {
103     // Find the corresponding accessibility object for the HTMLAreaElement. This should be
104     // in the list of children for its corresponding image.
105     if (!areaElement)
106         return 0;
107     
108     HTMLImageElement* imageElement = areaElement->imageElement();
109     if (!imageElement)
110         return 0;
111     
112     AccessibilityObject* axRenderImage = areaElement->document()->axObjectCache()->getOrCreate(imageElement->renderer());
113     if (!axRenderImage)
114         return 0;
115     
116     AccessibilityObject::AccessibilityChildrenVector imageChildren = axRenderImage->children();
117     unsigned count = imageChildren.size();
118     for (unsigned k = 0; k < count; ++k) {
119         AccessibilityObject* child = imageChildren[k].get();
120         if (!child->isImageMapLink())
121             continue;
122         
123         if (static_cast<AccessibilityImageMapLink*>(child)->areaElement() == areaElement)
124             return child;
125     }    
126     
127     return 0;
128 }
129     
130 AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page)
131 {
132     // get the focused node in the page
133     Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document();
134     Node* focusedNode = focusedDocument->focusedNode();
135     if (!focusedNode)
136         focusedNode = focusedDocument;
137
138     if (focusedNode->hasTagName(areaTag))
139         return focusedImageMapUIElement(static_cast<HTMLAreaElement*>(focusedNode));
140     
141     RenderObject* focusedNodeRenderer = focusedNode->renderer();
142     if (!focusedNodeRenderer)
143         return 0;
144
145     AccessibilityObject* obj = focusedNodeRenderer->document()->axObjectCache()->getOrCreate(focusedNodeRenderer);
146
147     if (obj->shouldFocusActiveDescendant()) {
148         if (AccessibilityObject* descendant = obj->activeDescendant())
149             obj = descendant;
150     }
151
152     // the HTML element, for example, is focusable but has an AX object that is ignored
153     if (obj->accessibilityIsIgnored())
154         obj = obj->parentObjectUnignored();
155
156     return obj;
157 }
158
159 AccessibilityObject* AXObjectCache::get(Widget* widget)
160 {
161     if (!widget)
162         return 0;
163         
164     AXID axID = m_widgetObjectMapping.get(widget);
165     ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
166     if (!axID)
167         return 0;
168     
169     return m_objects.get(axID).get();    
170 }
171     
172 AccessibilityObject* AXObjectCache::get(RenderObject* renderer)
173 {
174     if (!renderer)
175         return 0;
176     
177     AXID axID = m_renderObjectMapping.get(renderer);
178     ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
179     if (!axID)
180         return 0;
181     
182     return m_objects.get(axID).get();    
183 }
184
185 // FIXME: This probably belongs on Node.
186 // FIXME: This should take a const char*, but one caller passes nullAtom.
187 bool nodeHasRole(Node* node, const String& role)
188 {
189     if (!node || !node->isElementNode())
190         return false;
191
192     return equalIgnoringCase(static_cast<Element*>(node)->getAttribute(roleAttr), role);
193 }
194
195 static PassRefPtr<AccessibilityObject> createFromRenderer(RenderObject* renderer)
196 {
197     // FIXME: How could renderer->node() ever not be an Element?
198     Node* node = renderer->node();
199
200     // If the node is aria role="list" or the aria role is empty and its a
201     // ul/ol/dl type (it shouldn't be a list if aria says otherwise).
202     if (node && ((nodeHasRole(node, "list") || nodeHasRole(node, "directory"))
203                       || (nodeHasRole(node, nullAtom) && (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(dlTag)))))
204         return AccessibilityList::create(renderer);
205
206     // aria tables
207     if (nodeHasRole(node, "grid") || nodeHasRole(node, "treegrid"))
208         return AccessibilityARIAGrid::create(renderer);
209     if (nodeHasRole(node, "row"))
210         return AccessibilityARIAGridRow::create(renderer);
211     if (nodeHasRole(node, "gridcell") || nodeHasRole(node, "columnheader") || nodeHasRole(node, "rowheader"))
212         return AccessibilityARIAGridCell::create(renderer);
213
214 #if ENABLE(VIDEO)
215     // media controls
216     if (node && node->isMediaControlElement())
217         return AccessibilityMediaControl::create(renderer);
218 #endif
219
220     if (renderer->isBoxModelObject()) {
221         RenderBoxModelObject* cssBox = toRenderBoxModelObject(renderer);
222         if (cssBox->isListBox())
223             return AccessibilityListBox::create(toRenderListBox(cssBox));
224         if (cssBox->isMenuList())
225             return AccessibilityMenuList::create(toRenderMenuList(cssBox));
226
227         // standard tables
228         if (cssBox->isTable())
229             return AccessibilityTable::create(toRenderTable(cssBox));
230         if (cssBox->isTableRow())
231             return AccessibilityTableRow::create(toRenderTableRow(cssBox));
232         if (cssBox->isTableCell())
233             return AccessibilityTableCell::create(toRenderTableCell(cssBox));
234
235 #if ENABLE(PROGRESS_TAG)
236         // progress bar
237         if (cssBox->isProgress())
238             return AccessibilityProgressIndicator::create(toRenderProgress(cssBox));
239 #endif
240
241         // input type=range
242         if (cssBox->isSlider())
243             return AccessibilitySlider::create(toRenderSlider(cssBox));
244     }
245
246     return AccessibilityRenderObject::create(renderer);
247 }
248
249 AccessibilityObject* AXObjectCache::getOrCreate(Widget* widget)
250 {
251     if (!widget)
252         return 0;
253
254     if (AccessibilityObject* obj = get(widget))
255         return obj;
256     
257     RefPtr<AccessibilityObject> newObj = 0;
258     if (widget->isFrameView())
259         newObj = AccessibilityScrollView::create(static_cast<ScrollView*>(widget));
260     else if (widget->isScrollbar())
261         newObj = AccessibilityScrollbar::create(static_cast<Scrollbar*>(widget));
262         
263     getAXID(newObj.get());
264     
265     m_widgetObjectMapping.set(widget, newObj->axObjectID());
266     m_objects.set(newObj->axObjectID(), newObj);    
267     attachWrapper(newObj.get());
268     return newObj.get();
269 }
270     
271 AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
272 {
273     if (!renderer)
274         return 0;
275
276     if (AccessibilityObject* obj = get(renderer))
277         return obj;
278
279     RefPtr<AccessibilityObject> newObj = createFromRenderer(renderer);
280
281     getAXID(newObj.get());
282
283     m_renderObjectMapping.set(renderer, newObj->axObjectID());
284     m_objects.set(newObj->axObjectID(), newObj);
285     attachWrapper(newObj.get());
286     return newObj.get();
287 }
288     
289 AccessibilityObject* AXObjectCache::rootObject()
290 {
291     return getOrCreate(m_document->view());
292 }
293
294 AccessibilityObject* AXObjectCache::rootObjectForFrame(Frame* frame)
295 {
296     if (!frame)
297         return 0;
298     return getOrCreate(frame->view());
299 }    
300     
301 AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole role)
302 {
303     RefPtr<AccessibilityObject> obj = 0;
304     
305     // will be filled in...
306     switch (role) {
307     case ListBoxOptionRole:
308         obj = AccessibilityListBoxOption::create();
309         break;
310     case ImageMapLinkRole:
311         obj = AccessibilityImageMapLink::create();
312         break;
313     case ColumnRole:
314         obj = AccessibilityTableColumn::create();
315         break;            
316     case TableHeaderContainerRole:
317         obj = AccessibilityTableHeaderContainer::create();
318         break;   
319     case SliderThumbRole:
320         obj = AccessibilitySliderThumb::create();
321         break;
322     case MenuListPopupRole:
323         obj = AccessibilityMenuListPopup::create();
324         break;
325     case MenuListOptionRole:
326         obj = AccessibilityMenuListOption::create();
327         break;
328     case SpinButtonRole:
329         obj = AccessibilitySpinButton::create();
330         break;
331     case SpinButtonPartRole:
332         obj = AccessibilitySpinButtonPart::create();
333         break;
334     default:
335         obj = 0;
336     }
337     
338     if (obj)
339         getAXID(obj.get());
340     else
341         return 0;
342
343     m_objects.set(obj->axObjectID(), obj);    
344     attachWrapper(obj.get());
345     return obj.get();
346 }
347
348 void AXObjectCache::remove(AXID axID)
349 {
350     if (!axID)
351         return;
352     
353     // first fetch object to operate some cleanup functions on it 
354     AccessibilityObject* obj = m_objects.get(axID).get();
355     if (!obj)
356         return;
357     
358     detachWrapper(obj);
359     obj->detach();
360     removeAXID(obj);
361     
362     // finally remove the object
363     if (!m_objects.take(axID))
364         return;
365     
366     ASSERT(m_objects.size() >= m_idsInUse.size());    
367 }
368     
369 void AXObjectCache::remove(RenderObject* renderer)
370 {
371     if (!renderer)
372         return;
373     
374     AXID axID = m_renderObjectMapping.get(renderer);
375     remove(axID);
376     m_renderObjectMapping.remove(renderer);
377 }
378
379 void AXObjectCache::remove(Widget* view)
380 {
381     if (!view)
382         return;
383         
384     AXID axID = m_widgetObjectMapping.get(view);
385     remove(axID);
386     m_widgetObjectMapping.remove(view);
387 }
388     
389     
390 #if !PLATFORM(WIN) || OS(WINCE)
391 AXID AXObjectCache::platformGenerateAXID() const
392 {
393     static AXID lastUsedID = 0;
394
395     // Generate a new ID.
396     AXID objID = lastUsedID;
397     do {
398         ++objID;
399     } while (!objID || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
400
401     lastUsedID = objID;
402
403     return objID;
404 }
405 #endif
406
407 AXID AXObjectCache::getAXID(AccessibilityObject* obj)
408 {
409     // check for already-assigned ID
410     AXID objID = obj->axObjectID();
411     if (objID) {
412         ASSERT(m_idsInUse.contains(objID));
413         return objID;
414     }
415
416     objID = platformGenerateAXID();
417
418     m_idsInUse.add(objID);
419     obj->setAXObjectID(objID);
420     
421     return objID;
422 }
423
424 void AXObjectCache::removeAXID(AccessibilityObject* object)
425 {
426     if (!object)
427         return;
428     
429     AXID objID = object->axObjectID();
430     if (!objID)
431         return;
432     ASSERT(!HashTraits<AXID>::isDeletedValue(objID));
433     ASSERT(m_idsInUse.contains(objID));
434     object->setAXObjectID(0);
435     m_idsInUse.remove(objID);
436 }
437
438 #if HAVE(ACCESSIBILITY)
439 void AXObjectCache::contentChanged(RenderObject* renderer)
440 {
441     AccessibilityObject* object = getOrCreate(renderer);
442     if (object)
443         object->contentChanged(); 
444 }
445 #endif
446
447 void AXObjectCache::childrenChanged(RenderObject* renderer)
448 {
449     if (!renderer)
450         return;
451  
452     AXID axID = m_renderObjectMapping.get(renderer);
453     if (!axID)
454         return;
455     
456     AccessibilityObject* obj = m_objects.get(axID).get();
457     if (obj)
458         obj->childrenChanged();
459 }
460     
461 void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*)
462 {
463     m_notificationPostTimer.stop();
464
465     unsigned i = 0, count = m_notificationsToPost.size();
466     for (i = 0; i < count; ++i) {
467         AccessibilityObject* obj = m_notificationsToPost[i].first.get();
468 #ifndef NDEBUG
469         // Make sure none of the render views are in the process of being layed out.
470         // Notifications should only be sent after the renderer has finished
471         if (obj->isAccessibilityRenderObject()) {
472             AccessibilityRenderObject* renderObj = static_cast<AccessibilityRenderObject*>(obj);
473             RenderObject* renderer = renderObj->renderer();
474             if (renderer && renderer->view())
475                 ASSERT(!renderer->view()->layoutState());
476         }
477 #endif
478         
479         postPlatformNotification(obj, m_notificationsToPost[i].second);
480     }
481     
482     m_notificationsToPost.clear();
483 }
484     
485 #if HAVE(ACCESSIBILITY)
486 void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, bool postToElement, PostType postType)
487 {
488     // Notifications for text input objects are sent to that object.
489     // All others are sent to the top WebArea.
490     if (!renderer)
491         return;
492     
493     // Get an accessibility object that already exists. One should not be created here
494     // because a render update may be in progress and creating an AX object can re-trigger a layout
495     RefPtr<AccessibilityObject> object = get(renderer);
496     while (!object && renderer) {
497         renderer = renderer->parent();
498         object = get(renderer); 
499     }
500     
501     if (!renderer)
502         return;
503     
504     postNotification(object.get(), renderer->document(), notification, postToElement, postType);
505 }
506
507 void AXObjectCache::postNotification(AccessibilityObject* object, Document* document, AXNotification notification, bool postToElement, PostType postType)
508 {
509     if (object && !postToElement)
510         object = object->observableObject();
511
512     if (!object && document)
513         object = get(document->renderer());
514
515     if (!object)
516         return;
517
518     if (postType == PostAsynchronously) {
519         m_notificationsToPost.append(make_pair(object, notification));
520         if (!m_notificationPostTimer.isActive())
521             m_notificationPostTimer.startOneShot(0);
522     } else
523         postPlatformNotification(object, notification);
524 }
525
526 void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
527 {
528     // postToElement is false so that you can pass in any child of an element and it will go up the parent tree
529     // to find the container which should send out the notification.
530     postNotification(renderer, AXSelectedChildrenChanged, false);
531 }
532
533 void AXObjectCache::nodeTextChangeNotification(RenderObject* renderer, AXTextChange textChange, unsigned offset, const String& text)
534 {
535     if (!renderer)
536         return;
537
538     // Delegate on the right platform
539     AccessibilityObject* obj = getOrCreate(renderer);
540     nodeTextChangePlatformNotification(obj, textChange, offset, text);
541 }
542 #endif
543
544 #if HAVE(ACCESSIBILITY)
545
546 void AXObjectCache::handleScrollbarUpdate(ScrollView* view)
547 {
548     if (!view)
549         return;
550     
551     // We don't want to create a scroll view from this method, only update an existing one.
552     AccessibilityObject* scrollViewObject = get(view);
553     if (scrollViewObject)
554         scrollViewObject->updateChildrenIfNecessary();
555 }
556     
557 void AXObjectCache::handleAriaExpandedChange(RenderObject *renderer)
558 {
559     if (!renderer)
560         return;
561     AccessibilityObject* obj = getOrCreate(renderer);
562     if (obj)
563         obj->handleAriaExpandedChanged();
564 }
565     
566 void AXObjectCache::handleActiveDescendantChanged(RenderObject* renderer)
567 {
568     if (!renderer)
569         return;
570     AccessibilityObject* obj = getOrCreate(renderer);
571     if (obj)
572         obj->handleActiveDescendantChanged();
573 }
574
575 void AXObjectCache::handleAriaRoleChanged(RenderObject* renderer)
576 {
577     if (!renderer)
578         return;
579     AccessibilityObject* obj = getOrCreate(renderer);
580     if (obj && obj->isAccessibilityRenderObject())
581         static_cast<AccessibilityRenderObject*>(obj)->updateAccessibilityRole();
582 }
583 #endif
584
585 VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData& textMarkerData)
586 {
587     if (!isNodeInUse(textMarkerData.node))
588         return VisiblePosition();
589     
590     // FIXME: Accessability should make it clear these are DOM-compliant offsets or store Position objects.
591     VisiblePosition visiblePos = VisiblePosition(createLegacyEditingPosition(textMarkerData.node, textMarkerData.offset), textMarkerData.affinity);
592     Position deepPos = visiblePos.deepEquivalent();
593     if (deepPos.isNull())
594         return VisiblePosition();
595     
596     RenderObject* renderer = deepPos.deprecatedNode()->renderer();
597     if (!renderer)
598         return VisiblePosition();
599     
600     AXObjectCache* cache = renderer->document()->axObjectCache();
601     if (!cache->isIDinUse(textMarkerData.axID))
602         return VisiblePosition();
603     
604     if (deepPos.deprecatedNode() != textMarkerData.node || deepPos.deprecatedEditingOffset() != textMarkerData.offset)
605         return VisiblePosition();
606     
607     return visiblePos;
608 }
609
610 void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos)
611 {
612     // This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence.
613     // This also allows callers to check for failure by looking at textMarkerData upon return.
614     memset(&textMarkerData, 0, sizeof(TextMarkerData));
615     
616     if (visiblePos.isNull())
617         return;
618     
619     Position deepPos = visiblePos.deepEquivalent();
620     Node* domNode = deepPos.deprecatedNode();
621     ASSERT(domNode);
622     if (!domNode)
623         return;
624     
625     if (domNode->isHTMLElement()) {
626         HTMLInputElement* inputElement = domNode->toInputElement();
627         if (inputElement && inputElement->isPasswordField())
628             return;
629     }
630     
631     // locate the renderer, which must exist for a visible dom node
632     RenderObject* renderer = domNode->renderer();
633     ASSERT(renderer);
634     
635     // find or create an accessibility object for this renderer
636     AXObjectCache* cache = renderer->document()->axObjectCache();
637     RefPtr<AccessibilityObject> obj = cache->getOrCreate(renderer);
638     
639     textMarkerData.axID = obj.get()->axObjectID();
640     textMarkerData.node = domNode;
641     textMarkerData.offset = deepPos.deprecatedEditingOffset();
642     textMarkerData.affinity = visiblePos.affinity();   
643     
644     cache->setNodeInUse(domNode);
645 }
646     
647 } // namespace WebCore