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