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