AX: aria-hidden=false does not work as expected
[WebKit-https.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
31 #if HAVE(ACCESSIBILITY)
32
33 #include "AXObjectCache.h"
34
35 #include "AccessibilityARIAGrid.h"
36 #include "AccessibilityARIAGridCell.h"
37 #include "AccessibilityARIAGridRow.h"
38 #include "AccessibilityImageMapLink.h"
39 #include "AccessibilityList.h"
40 #include "AccessibilityListBox.h"
41 #include "AccessibilityListBoxOption.h"
42 #include "AccessibilityMediaControls.h"
43 #include "AccessibilityMenuList.h"
44 #include "AccessibilityMenuListOption.h"
45 #include "AccessibilityMenuListPopup.h"
46 #include "AccessibilityProgressIndicator.h"
47 #include "AccessibilityRenderObject.h"
48 #include "AccessibilityScrollView.h"
49 #include "AccessibilityScrollbar.h"
50 #include "AccessibilitySlider.h"
51 #include "AccessibilitySpinButton.h"
52 #include "AccessibilityTable.h"
53 #include "AccessibilityTableCell.h"
54 #include "AccessibilityTableColumn.h"
55 #include "AccessibilityTableHeaderContainer.h"
56 #include "AccessibilityTableRow.h"
57 #include "Document.h"
58 #include "FocusController.h"
59 #include "Frame.h"
60 #include "HTMLAreaElement.h"
61 #include "HTMLImageElement.h"
62 #include "HTMLInputElement.h"
63 #include "HTMLNames.h"
64 #if ENABLE(VIDEO)
65 #include "MediaControlElements.h"
66 #endif
67 #include "Page.h"
68 #include "RenderListBox.h"
69 #include "RenderMenuList.h"
70 #include "RenderProgress.h"
71 #include "RenderSlider.h"
72 #include "RenderTable.h"
73 #include "RenderTableCell.h"
74 #include "RenderTableRow.h"
75 #include "RenderView.h"
76 #include "ScrollView.h"
77
78 #include <wtf/PassRefPtr.h>
79
80 namespace WebCore {
81
82 using namespace HTMLNames;
83     
84 bool AXObjectCache::gAccessibilityEnabled = false;
85 bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false;
86
87 AXObjectCache::AXObjectCache(const Document* doc)
88     : m_notificationPostTimer(this, &AXObjectCache::notificationPostTimerFired)
89 {
90     m_document = const_cast<Document*>(doc);
91 }
92
93 AXObjectCache::~AXObjectCache()
94 {
95     HashMap<AXID, RefPtr<AccessibilityObject> >::iterator end = m_objects.end();
96     for (HashMap<AXID, RefPtr<AccessibilityObject> >::iterator it = m_objects.begin(); it != end; ++it) {
97         AccessibilityObject* obj = (*it).value.get();
98         detachWrapper(obj);
99         obj->detach();
100         removeAXID(obj);
101     }
102 }
103
104 AccessibilityObject* AXObjectCache::focusedImageMapUIElement(HTMLAreaElement* areaElement)
105 {
106     // Find the corresponding accessibility object for the HTMLAreaElement. This should be
107     // in the list of children for its corresponding image.
108     if (!areaElement)
109         return 0;
110     
111     HTMLImageElement* imageElement = areaElement->imageElement();
112     if (!imageElement)
113         return 0;
114     
115     AccessibilityObject* axRenderImage = areaElement->document()->axObjectCache()->getOrCreate(imageElement);
116     if (!axRenderImage)
117         return 0;
118     
119     AccessibilityObject::AccessibilityChildrenVector imageChildren = axRenderImage->children();
120     unsigned count = imageChildren.size();
121     for (unsigned k = 0; k < count; ++k) {
122         AccessibilityObject* child = imageChildren[k].get();
123         if (!child->isImageMapLink())
124             continue;
125         
126         if (static_cast<AccessibilityImageMapLink*>(child)->areaElement() == areaElement)
127             return child;
128     }    
129     
130     return 0;
131 }
132     
133 AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page)
134 {
135     if (!gAccessibilityEnabled)
136         return 0;
137
138     // get the focused node in the page
139     Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document();
140     Node* focusedNode = focusedDocument->focusedNode();
141     if (!focusedNode)
142         focusedNode = focusedDocument;
143
144     if (focusedNode->hasTagName(areaTag))
145         return focusedImageMapUIElement(static_cast<HTMLAreaElement*>(focusedNode));
146     
147     AccessibilityObject* obj = focusedNode->document()->axObjectCache()->getOrCreate(focusedNode);
148     if (!obj)
149         return 0;
150
151     if (obj->shouldFocusActiveDescendant()) {
152         if (AccessibilityObject* descendant = obj->activeDescendant())
153             obj = descendant;
154     }
155
156     // the HTML element, for example, is focusable but has an AX object that is ignored
157     if (obj->accessibilityIsIgnored())
158         obj = obj->parentObjectUnignored();
159
160     return obj;
161 }
162
163 AccessibilityObject* AXObjectCache::get(Widget* widget)
164 {
165     if (!widget)
166         return 0;
167         
168     AXID axID = m_widgetObjectMapping.get(widget);
169     ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
170     if (!axID)
171         return 0;
172     
173     return m_objects.get(axID).get();    
174 }
175     
176 AccessibilityObject* AXObjectCache::get(RenderObject* renderer)
177 {
178     if (!renderer)
179         return 0;
180     
181     AXID axID = m_renderObjectMapping.get(renderer);
182     ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
183     if (!axID)
184         return 0;
185
186     return m_objects.get(axID).get();    
187 }
188
189 AccessibilityObject* AXObjectCache::get(Node* node)
190 {
191     if (!node)
192         return 0;
193
194     AXID renderID = node->renderer() ? m_renderObjectMapping.get(node->renderer()) : 0;
195     ASSERT(!HashTraits<AXID>::isDeletedValue(renderID));
196
197     AXID nodeID = m_nodeObjectMapping.get(node);
198     ASSERT(!HashTraits<AXID>::isDeletedValue(nodeID));
199
200     if (node->renderer() && nodeID && !renderID) {
201         // This can happen if an AccessibilityNodeObject is created for a node that's not
202         // rendered, but later something changes and it gets a renderer (like if it's
203         // reparented).
204         remove(nodeID);
205         return 0;
206     }
207
208     if (renderID)
209         return m_objects.get(renderID).get();
210
211     if (!nodeID)
212         return 0;
213
214     return m_objects.get(nodeID).get();
215 }
216
217 // FIXME: This probably belongs on Node.
218 // FIXME: This should take a const char*, but one caller passes nullAtom.
219 bool nodeHasRole(Node* node, const String& role)
220 {
221     if (!node || !node->isElementNode())
222         return false;
223
224     return equalIgnoringCase(static_cast<Element*>(node)->getAttribute(roleAttr), role);
225 }
226
227 static PassRefPtr<AccessibilityObject> createFromRenderer(RenderObject* renderer)
228 {
229     // FIXME: How could renderer->node() ever not be an Element?
230     Node* node = renderer->node();
231
232     // If the node is aria role="list" or the aria role is empty and its a
233     // ul/ol/dl type (it shouldn't be a list if aria says otherwise).
234     if (node && ((nodeHasRole(node, "list") || nodeHasRole(node, "directory"))
235                       || (nodeHasRole(node, nullAtom) && (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(dlTag)))))
236         return AccessibilityList::create(renderer);
237
238     // aria tables
239     if (nodeHasRole(node, "grid") || nodeHasRole(node, "treegrid"))
240         return AccessibilityARIAGrid::create(renderer);
241     if (nodeHasRole(node, "row"))
242         return AccessibilityARIAGridRow::create(renderer);
243     if (nodeHasRole(node, "gridcell") || nodeHasRole(node, "columnheader") || nodeHasRole(node, "rowheader"))
244         return AccessibilityARIAGridCell::create(renderer);
245
246 #if ENABLE(VIDEO)
247     // media controls
248     if (node && node->isMediaControlElement())
249         return AccessibilityMediaControl::create(renderer);
250 #endif
251
252     if (renderer->isBoxModelObject()) {
253         RenderBoxModelObject* cssBox = toRenderBoxModelObject(renderer);
254         if (cssBox->isListBox())
255             return AccessibilityListBox::create(toRenderListBox(cssBox));
256         if (cssBox->isMenuList())
257             return AccessibilityMenuList::create(toRenderMenuList(cssBox));
258
259         // standard tables
260         if (cssBox->isTable())
261             return AccessibilityTable::create(toRenderTable(cssBox));
262         if (cssBox->isTableRow())
263             return AccessibilityTableRow::create(toRenderTableRow(cssBox));
264         if (cssBox->isTableCell())
265             return AccessibilityTableCell::create(toRenderTableCell(cssBox));
266
267 #if ENABLE(PROGRESS_ELEMENT)
268         // progress bar
269         if (cssBox->isProgress())
270             return AccessibilityProgressIndicator::create(toRenderProgress(cssBox));
271 #endif
272
273         // input type=range
274         if (cssBox->isSlider())
275             return AccessibilitySlider::create(toRenderSlider(cssBox));
276     }
277
278     return AccessibilityRenderObject::create(renderer);
279 }
280
281 static PassRefPtr<AccessibilityObject> createFromNode(Node* node)
282 {
283     return AccessibilityNodeObject::create(node);
284 }
285
286 AccessibilityObject* AXObjectCache::getOrCreate(Widget* widget)
287 {
288     if (!widget)
289         return 0;
290
291     if (AccessibilityObject* obj = get(widget))
292         return obj;
293     
294     RefPtr<AccessibilityObject> newObj = 0;
295     if (widget->isFrameView())
296         newObj = AccessibilityScrollView::create(static_cast<ScrollView*>(widget));
297     else if (widget->isScrollbar())
298         newObj = AccessibilityScrollbar::create(static_cast<Scrollbar*>(widget));
299         
300     getAXID(newObj.get());
301     
302     m_widgetObjectMapping.set(widget, newObj->axObjectID());
303     m_objects.set(newObj->axObjectID(), newObj);    
304     attachWrapper(newObj.get());
305     return newObj.get();
306 }
307
308 AccessibilityObject* AXObjectCache::getOrCreate(Node* node)
309 {
310     if (!node)
311         return 0;
312
313     if (AccessibilityObject* obj = get(node))
314         return obj;
315
316     if (node->renderer())
317         return getOrCreate(node->renderer());
318
319     if (!node->parentElement())
320         return 0;
321     
322     // It's only allowed to create an AccessibilityObject from a Node if it's in a canvas subtree.
323     // Or if it's a hidden element, but we still want to expose it because of other ARIA attributes.
324     bool inCanvasSubtree = node->parentElement()->isInCanvasSubtree();
325     bool isHidden = !node->renderer() && isNodeAriaVisible(node);
326     if (!inCanvasSubtree && !isHidden)
327         return 0;
328
329     RefPtr<AccessibilityObject> newObj = createFromNode(node);
330
331     getAXID(newObj.get());
332
333     m_nodeObjectMapping.set(node, newObj->axObjectID());
334     m_objects.set(newObj->axObjectID(), newObj);
335     attachWrapper(newObj.get());
336     return newObj.get();
337 }
338
339 AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
340 {
341     if (!renderer)
342         return 0;
343
344     if (AccessibilityObject* obj = get(renderer))
345         return obj;
346
347     RefPtr<AccessibilityObject> newObj = createFromRenderer(renderer);
348
349     getAXID(newObj.get());
350
351     m_renderObjectMapping.set(renderer, newObj->axObjectID());
352     m_objects.set(newObj->axObjectID(), newObj);
353     attachWrapper(newObj.get());
354     return newObj.get();
355 }
356     
357 AccessibilityObject* AXObjectCache::rootObject()
358 {
359     if (!gAccessibilityEnabled)
360         return 0;
361     
362     return getOrCreate(m_document->view());
363 }
364
365 AccessibilityObject* AXObjectCache::rootObjectForFrame(Frame* frame)
366 {
367     if (!gAccessibilityEnabled)
368         return 0;
369
370     if (!frame)
371         return 0;
372     return getOrCreate(frame->view());
373 }    
374     
375 AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole role)
376 {
377     RefPtr<AccessibilityObject> obj = 0;
378     
379     // will be filled in...
380     switch (role) {
381     case ListBoxOptionRole:
382         obj = AccessibilityListBoxOption::create();
383         break;
384     case ImageMapLinkRole:
385         obj = AccessibilityImageMapLink::create();
386         break;
387     case ColumnRole:
388         obj = AccessibilityTableColumn::create();
389         break;            
390     case TableHeaderContainerRole:
391         obj = AccessibilityTableHeaderContainer::create();
392         break;   
393     case SliderThumbRole:
394         obj = AccessibilitySliderThumb::create();
395         break;
396     case MenuListPopupRole:
397         obj = AccessibilityMenuListPopup::create();
398         break;
399     case MenuListOptionRole:
400         obj = AccessibilityMenuListOption::create();
401         break;
402     case SpinButtonRole:
403         obj = AccessibilitySpinButton::create();
404         break;
405     case SpinButtonPartRole:
406         obj = AccessibilitySpinButtonPart::create();
407         break;
408     default:
409         obj = 0;
410     }
411     
412     if (obj)
413         getAXID(obj.get());
414     else
415         return 0;
416
417     m_objects.set(obj->axObjectID(), obj);    
418     attachWrapper(obj.get());
419     return obj.get();
420 }
421
422 void AXObjectCache::remove(AXID axID)
423 {
424     if (!axID)
425         return;
426     
427     // first fetch object to operate some cleanup functions on it 
428     AccessibilityObject* obj = m_objects.get(axID).get();
429     if (!obj)
430         return;
431     
432     detachWrapper(obj);
433     obj->detach();
434     removeAXID(obj);
435     
436     // finally remove the object
437     if (!m_objects.take(axID))
438         return;
439     
440     ASSERT(m_objects.size() >= m_idsInUse.size());    
441 }
442     
443 void AXObjectCache::remove(RenderObject* renderer)
444 {
445     if (!renderer)
446         return;
447     
448     AXID axID = m_renderObjectMapping.get(renderer);
449     remove(axID);
450     m_renderObjectMapping.remove(renderer);
451 }
452
453 void AXObjectCache::remove(Node* node)
454 {
455     if (!node)
456         return;
457
458     removeNodeForUse(node);
459
460     // This is all safe even if we didn't have a mapping.
461     AXID axID = m_nodeObjectMapping.get(node);
462     remove(axID);
463     m_nodeObjectMapping.remove(node);
464
465     if (node->renderer()) {
466         remove(node->renderer());
467         return;
468     }
469 }
470
471 void AXObjectCache::remove(Widget* view)
472 {
473     if (!view)
474         return;
475         
476     AXID axID = m_widgetObjectMapping.get(view);
477     remove(axID);
478     m_widgetObjectMapping.remove(view);
479 }
480     
481     
482 #if !PLATFORM(WIN) || OS(WINCE)
483 AXID AXObjectCache::platformGenerateAXID() const
484 {
485     static AXID lastUsedID = 0;
486
487     // Generate a new ID.
488     AXID objID = lastUsedID;
489     do {
490         ++objID;
491     } while (!objID || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
492
493     lastUsedID = objID;
494
495     return objID;
496 }
497 #endif
498
499 AXID AXObjectCache::getAXID(AccessibilityObject* obj)
500 {
501     // check for already-assigned ID
502     AXID objID = obj->axObjectID();
503     if (objID) {
504         ASSERT(m_idsInUse.contains(objID));
505         return objID;
506     }
507
508     objID = platformGenerateAXID();
509
510     m_idsInUse.add(objID);
511     obj->setAXObjectID(objID);
512     
513     return objID;
514 }
515
516 void AXObjectCache::removeAXID(AccessibilityObject* object)
517 {
518     if (!object)
519         return;
520     
521     AXID objID = object->axObjectID();
522     if (!objID)
523         return;
524     ASSERT(!HashTraits<AXID>::isDeletedValue(objID));
525     ASSERT(m_idsInUse.contains(objID));
526     object->setAXObjectID(0);
527     m_idsInUse.remove(objID);
528 }
529
530 void AXObjectCache::contentChanged(Node* node)
531 {
532     if (AccessibilityObject* object = getOrCreate(node))
533         object->contentChanged(); 
534 }
535
536 void AXObjectCache::contentChanged(RenderObject* renderer)
537 {
538     if (AccessibilityObject* object = getOrCreate(renderer))
539         object->contentChanged(); 
540 }
541
542 void AXObjectCache::updateCacheAfterNodeIsAttached(Node* node)
543 {
544     // Calling get() will update the AX object if we had an AccessibilityNodeObject but now we need
545     // an AccessibilityRenderObject, because it was reparented to a location outside of a canvas.
546     get(node);
547 }
548
549 void AXObjectCache::childrenChanged(Node* node)
550 {
551     if (AccessibilityObject* obj = get(node))
552         obj->childrenChanged();
553 }
554
555 void AXObjectCache::childrenChanged(RenderObject* renderer)
556 {
557     if (AccessibilityObject* obj = get(renderer))
558         obj->childrenChanged();
559 }
560     
561 void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*)
562 {
563     m_notificationPostTimer.stop();
564
565     unsigned i = 0, count = m_notificationsToPost.size();
566     for (i = 0; i < count; ++i) {
567         AccessibilityObject* obj = m_notificationsToPost[i].first.get();
568 #ifndef NDEBUG
569         // Make sure none of the render views are in the process of being layed out.
570         // Notifications should only be sent after the renderer has finished
571         if (obj->isAccessibilityRenderObject()) {
572             AccessibilityRenderObject* renderObj = static_cast<AccessibilityRenderObject*>(obj);
573             RenderObject* renderer = renderObj->renderer();
574             if (renderer && renderer->view())
575                 ASSERT(!renderer->view()->layoutState());
576         }
577 #endif
578         
579         postPlatformNotification(obj, m_notificationsToPost[i].second);
580     }
581     
582     m_notificationsToPost.clear();
583 }
584     
585 void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, bool postToElement, PostType postType)
586 {
587     if (!renderer)
588         return;
589     
590     // Get an accessibility object that already exists. One should not be created here
591     // because a render update may be in progress and creating an AX object can re-trigger a layout
592     RefPtr<AccessibilityObject> object = get(renderer);
593     while (!object && renderer) {
594         renderer = renderer->parent();
595         object = get(renderer); 
596     }
597     
598     if (!renderer)
599         return;
600     
601     postNotification(object.get(), renderer->document(), notification, postToElement, postType);
602 }
603
604 void AXObjectCache::postNotification(Node* node, AXNotification notification, bool postToElement, PostType postType)
605 {
606     if (!node)
607         return;
608     
609     // Get an accessibility object that already exists. One should not be created here
610     // because a render update may be in progress and creating an AX object can re-trigger a layout
611     RefPtr<AccessibilityObject> object = get(node);
612     while (!object && node) {
613         node = node->parentNode();
614         object = get(node);
615     }
616     
617     if (!node)
618         return;
619     
620     postNotification(object.get(), node->document(), notification, postToElement, postType);
621 }
622
623 void AXObjectCache::postNotification(AccessibilityObject* object, Document* document, AXNotification notification, bool postToElement, PostType postType)
624 {
625     if (object && !postToElement)
626         object = object->observableObject();
627
628     if (!object && document)
629         object = get(document->renderer());
630
631     if (!object)
632         return;
633
634     if (postType == PostAsynchronously) {
635         m_notificationsToPost.append(std::make_pair(object, notification));
636         if (!m_notificationPostTimer.isActive())
637             m_notificationPostTimer.startOneShot(0);
638     } else
639         postPlatformNotification(object, notification);
640 }
641
642 void AXObjectCache::checkedStateChanged(Node* node)
643 {
644     postNotification(node, AXObjectCache::AXCheckedStateChanged, true);
645 }
646
647 void AXObjectCache::selectedChildrenChanged(Node* node)
648 {
649     // postToElement is false so that you can pass in any child of an element and it will go up the parent tree
650     // to find the container which should send out the notification.
651     postNotification(node, AXSelectedChildrenChanged, false);
652 }
653
654 void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
655 {
656     // postToElement is false so that you can pass in any child of an element and it will go up the parent tree
657     // to find the container which should send out the notification.
658     postNotification(renderer, AXSelectedChildrenChanged, false);
659 }
660
661 void AXObjectCache::nodeTextChangeNotification(Node* node, AXTextChange textChange, unsigned offset, const String& text)
662 {
663     if (!node)
664         return;
665
666     // Delegate on the right platform
667     AccessibilityObject* obj = getOrCreate(node);
668     nodeTextChangePlatformNotification(obj, textChange, offset, text);
669 }
670
671 void AXObjectCache::frameLoadingEventNotification(Frame* frame, AXLoadingEvent loadingEvent)
672 {
673     if (!frame)
674         return;
675
676     // Delegate on the right platform
677     RenderView* contentRenderer = frame->contentRenderer();
678     if (!contentRenderer)
679         return;
680
681     AccessibilityObject* obj = getOrCreate(contentRenderer);
682     frameLoadingEventPlatformNotification(obj, loadingEvent);
683 }
684
685 void AXObjectCache::handleScrollbarUpdate(ScrollView* view)
686 {
687     if (!view)
688         return;
689     
690     // We don't want to create a scroll view from this method, only update an existing one.
691     if (AccessibilityObject* scrollViewObject = get(view))
692         scrollViewObject->updateChildrenIfNecessary();
693 }
694     
695 void AXObjectCache::handleAriaExpandedChange(Node* node)
696 {
697     if (AccessibilityObject* obj = getOrCreate(node))
698         obj->handleAriaExpandedChanged();
699 }
700     
701 void AXObjectCache::handleActiveDescendantChanged(Node* node)
702 {
703     if (AccessibilityObject* obj = getOrCreate(node))
704         obj->handleActiveDescendantChanged();
705 }
706
707 void AXObjectCache::handleAriaRoleChanged(Node* node)
708 {
709     if (AccessibilityObject* obj = getOrCreate(node))
710         obj->updateAccessibilityRole();
711 }
712
713 VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData& textMarkerData)
714 {
715     if (!isNodeInUse(textMarkerData.node))
716         return VisiblePosition();
717     
718     // FIXME: Accessability should make it clear these are DOM-compliant offsets or store Position objects.
719     VisiblePosition visiblePos = VisiblePosition(createLegacyEditingPosition(textMarkerData.node, textMarkerData.offset), textMarkerData.affinity);
720     Position deepPos = visiblePos.deepEquivalent();
721     if (deepPos.isNull())
722         return VisiblePosition();
723     
724     RenderObject* renderer = deepPos.deprecatedNode()->renderer();
725     if (!renderer)
726         return VisiblePosition();
727     
728     AXObjectCache* cache = renderer->document()->axObjectCache();
729     if (!cache->isIDinUse(textMarkerData.axID))
730         return VisiblePosition();
731     
732     if (deepPos.deprecatedNode() != textMarkerData.node || deepPos.deprecatedEditingOffset() != textMarkerData.offset)
733         return VisiblePosition();
734     
735     return visiblePos;
736 }
737
738 void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos)
739 {
740     // This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence.
741     // This also allows callers to check for failure by looking at textMarkerData upon return.
742     memset(&textMarkerData, 0, sizeof(TextMarkerData));
743     
744     if (visiblePos.isNull())
745         return;
746     
747     Position deepPos = visiblePos.deepEquivalent();
748     Node* domNode = deepPos.deprecatedNode();
749     ASSERT(domNode);
750     if (!domNode)
751         return;
752     
753     if (domNode->isHTMLElement()) {
754         HTMLInputElement* inputElement = domNode->toInputElement();
755         if (inputElement && inputElement->isPasswordField())
756             return;
757     }
758     
759     // find or create an accessibility object for this node
760     AXObjectCache* cache = domNode->document()->axObjectCache();
761     RefPtr<AccessibilityObject> obj = cache->getOrCreate(domNode);
762     
763     textMarkerData.axID = obj.get()->axObjectID();
764     textMarkerData.node = domNode;
765     textMarkerData.offset = deepPos.deprecatedEditingOffset();
766     textMarkerData.affinity = visiblePos.affinity();   
767     
768     cache->setNodeInUse(domNode);
769 }
770
771 const Element* AXObjectCache::rootAXEditableElement(const Node* node)
772 {
773     const Element* result = node->rootEditableElement();
774     const Element* element = node->isElementNode() ? toElement(node) : node->parentElement();
775
776     for (; element; element = element->parentElement()) {
777         if (nodeIsTextControl(element))
778             result = element;
779     }
780
781     return result;
782 }
783
784 bool AXObjectCache::nodeIsTextControl(const Node* node)
785 {
786     if (!node)
787         return false;
788
789     const AccessibilityObject* axObject = getOrCreate(const_cast<Node*>(node));
790     return axObject && axObject->isTextControl();
791 }
792     
793 bool isNodeAriaVisible(Node* node)
794 {
795     if (!node)
796         return false;
797     
798     if (!node->isElementNode())
799         return false;
800     
801     return equalIgnoringCase(toElement(node)->getAttribute(aria_hiddenAttr), "false");
802 }
803
804 } // namespace WebCore
805
806 #endif // HAVE(ACCESSIBILITY)