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