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