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