Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / accessibility / AXObjectCache.cpp
1 /*
2  * Copyright (C) 2008, 2009, 2010, 2015 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 "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 "AccessibilityTree.h"
59 #include "AccessibilityTreeItem.h"
60 #include "Document.h"
61 #include "Editor.h"
62 #include "ElementIterator.h"
63 #include "FocusController.h"
64 #include "Frame.h"
65 #include "HTMLAreaElement.h"
66 #include "HTMLCanvasElement.h"
67 #include "HTMLImageElement.h"
68 #include "HTMLInputElement.h"
69 #include "HTMLLabelElement.h"
70 #include "HTMLMeterElement.h"
71 #include "HTMLNames.h"
72 #include "Page.h"
73 #include "RenderListBox.h"
74 #include "RenderMenuList.h"
75 #include "RenderMeter.h"
76 #include "RenderProgress.h"
77 #include "RenderSVGRoot.h"
78 #include "RenderSlider.h"
79 #include "RenderTable.h"
80 #include "RenderTableCell.h"
81 #include "RenderTableRow.h"
82 #include "RenderView.h"
83 #include "ScrollView.h"
84 #include <wtf/DataLog.h>
85
86 #if ENABLE(VIDEO)
87 #include "MediaControlElements.h"
88 #endif
89
90 namespace WebCore {
91
92 using namespace HTMLNames;
93
94 // Post value change notifications for password fields or elements contained in password fields at a 40hz interval to thwart analysis of typing cadence
95 static double AccessibilityPasswordValueChangeNotificationInterval = 0.025;
96 static double AccessibilityLiveRegionChangedNotificationInterval = 0.020;
97
98 AccessibilityObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID id) const
99 {
100     HashMap<AXID, CachedAXObjectAttributes>::const_iterator it = m_idMapping.find(id);
101     return it != m_idMapping.end() ? it->value.ignored : DefaultBehavior;
102 }
103
104 void AXComputedObjectAttributeCache::setIgnored(AXID id, AccessibilityObjectInclusion inclusion)
105 {
106     HashMap<AXID, CachedAXObjectAttributes>::iterator it = m_idMapping.find(id);
107     if (it != m_idMapping.end())
108         it->value.ignored = inclusion;
109     else {
110         CachedAXObjectAttributes attributes;
111         attributes.ignored = inclusion;
112         m_idMapping.set(id, attributes);
113     }
114 }
115     
116 bool AXObjectCache::gAccessibilityEnabled = false;
117 bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false;
118
119 void AXObjectCache::enableAccessibility()
120 {
121     gAccessibilityEnabled = true;
122 }
123
124 void AXObjectCache::disableAccessibility()
125 {
126     gAccessibilityEnabled = false;
127 }
128
129 void AXObjectCache::setEnhancedUserInterfaceAccessibility(bool flag)
130 {
131     gAccessibilityEnhancedUserInterfaceEnabled = flag;
132 #if PLATFORM(MAC)
133 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100
134     if (flag)
135         enableAccessibility();
136 #endif
137 #endif
138 }
139
140 AXObjectCache::AXObjectCache(Document& document)
141     : m_document(document)
142     , m_notificationPostTimer(*this, &AXObjectCache::notificationPostTimerFired)
143     , m_passwordNotificationPostTimer(*this, &AXObjectCache::passwordNotificationPostTimerFired)
144     , m_liveRegionChangedPostTimer(*this, &AXObjectCache::liveRegionChangedNotificationPostTimerFired)
145     , m_currentAriaModalNode(nullptr)
146 {
147     findAriaModalNodes();
148 }
149
150 AXObjectCache::~AXObjectCache()
151 {
152     m_notificationPostTimer.stop();
153     m_liveRegionChangedPostTimer.stop();
154
155     for (const auto& object : m_objects.values()) {
156         detachWrapper(object.get(), CacheDestroyed);
157         object->detach(CacheDestroyed);
158         removeAXID(object.get());
159     }
160 }
161
162 void AXObjectCache::findAriaModalNodes()
163 {
164     // Traverse the DOM tree to look for the aria-modal=true nodes.
165     for (Element* element = ElementTraversal::firstWithin(document().rootNode()); element; element = ElementTraversal::nextIncludingPseudo(*element)) {
166         
167         // Must have dialog or alertdialog role
168         if (!nodeHasRole(element, "dialog") && !nodeHasRole(element, "alertdialog"))
169             continue;
170         if (!equalIgnoringCase(element->fastGetAttribute(aria_modalAttr), "true"))
171             continue;
172         
173         m_ariaModalNodesSet.add(element);
174     }
175     
176     // Set the current valid aria-modal node if possible.
177     updateCurrentAriaModalNode();
178 }
179
180 void AXObjectCache::updateCurrentAriaModalNode()
181 {
182     // There might be multiple nodes with aria-modal=true set.
183     // We use this function to pick the one we want.
184     m_currentAriaModalNode = nullptr;
185     if (m_ariaModalNodesSet.isEmpty())
186         return;
187     
188     // We only care about the nodes which are visible.
189     ListHashSet<RefPtr<Node>> visibleNodes;
190     for (auto& object : m_ariaModalNodesSet) {
191         if (isNodeVisible(object))
192             visibleNodes.add(object);
193     }
194     
195     if (visibleNodes.isEmpty())
196         return;
197     
198     // If any of the node are keyboard focused, we want to pick that.
199     Node* focusedNode = document().focusedElement();
200     for (auto& object : visibleNodes) {
201         if (focusedNode != nullptr && focusedNode->isDescendantOf(object.get())) {
202             m_currentAriaModalNode = object.get();
203             break;
204         }
205     }
206     
207     // If none of the nodes are focused, we want to pick the last dialog in the DOM.
208     if (!m_currentAriaModalNode)
209         m_currentAriaModalNode = visibleNodes.last().get();
210 }
211
212 bool AXObjectCache::isNodeVisible(Node* node) const
213 {
214     if (!is<Element>(node))
215         return false;
216     
217     RenderObject* renderer = node->renderer();
218     if (!renderer)
219         return false;
220     const RenderStyle& style = renderer->style();
221     if (style.display() == NONE || style.visibility() != VISIBLE)
222         return false;
223     
224     // We also need to consider aria hidden status.
225     if (!isNodeAriaVisible(node))
226         return false;
227     
228     return true;
229 }
230
231 Node* AXObjectCache::ariaModalNode()
232 {
233     // This function returns the valid aria modal node.
234     if (m_ariaModalNodesSet.isEmpty())
235         return nullptr;
236     
237     // Check the current valid aria modal node first.
238     // Usually when one dialog sets aria-modal=true, that dialog is the one we want.
239     if (isNodeVisible(m_currentAriaModalNode))
240         return m_currentAriaModalNode;
241     
242     // Recompute the valid aria modal node when m_currentAriaModalNode is null or hidden.
243     updateCurrentAriaModalNode();
244     return isNodeVisible(m_currentAriaModalNode) ? m_currentAriaModalNode : nullptr;
245 }
246
247 AccessibilityObject* AXObjectCache::focusedImageMapUIElement(HTMLAreaElement* areaElement)
248 {
249     // Find the corresponding accessibility object for the HTMLAreaElement. This should be
250     // in the list of children for its corresponding image.
251     if (!areaElement)
252         return nullptr;
253     
254     HTMLImageElement* imageElement = areaElement->imageElement();
255     if (!imageElement)
256         return nullptr;
257     
258     AccessibilityObject* axRenderImage = areaElement->document().axObjectCache()->getOrCreate(imageElement);
259     if (!axRenderImage)
260         return nullptr;
261     
262     for (const auto& child : axRenderImage->children()) {
263         if (!is<AccessibilityImageMapLink>(*child))
264             continue;
265         
266         if (downcast<AccessibilityImageMapLink>(*child).areaElement() == areaElement)
267             return child.get();
268     }    
269     
270     return nullptr;
271 }
272     
273 AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page)
274 {
275     if (!gAccessibilityEnabled)
276         return nullptr;
277
278     // get the focused node in the page
279     Document* focusedDocument = page->focusController().focusedOrMainFrame().document();
280     Element* focusedElement = focusedDocument->focusedElement();
281     if (is<HTMLAreaElement>(focusedElement))
282         return focusedImageMapUIElement(downcast<HTMLAreaElement>(focusedElement));
283
284     AccessibilityObject* obj = focusedDocument->axObjectCache()->getOrCreate(focusedElement ? static_cast<Node*>(focusedElement) : focusedDocument);
285     if (!obj)
286         return nullptr;
287
288     if (obj->shouldFocusActiveDescendant()) {
289         if (AccessibilityObject* descendant = obj->activeDescendant())
290             obj = descendant;
291     }
292
293     // the HTML element, for example, is focusable but has an AX object that is ignored
294     if (obj->accessibilityIsIgnored())
295         obj = obj->parentObjectUnignored();
296
297     return obj;
298 }
299
300 AccessibilityObject* AXObjectCache::get(Widget* widget)
301 {
302     if (!widget)
303         return nullptr;
304         
305     AXID axID = m_widgetObjectMapping.get(widget);
306     ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
307     if (!axID)
308         return nullptr;
309     
310     return m_objects.get(axID);    
311 }
312     
313 AccessibilityObject* AXObjectCache::get(RenderObject* renderer)
314 {
315     if (!renderer)
316         return nullptr;
317     
318     AXID axID = m_renderObjectMapping.get(renderer);
319     ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
320     if (!axID)
321         return nullptr;
322
323     return m_objects.get(axID);    
324 }
325
326 AccessibilityObject* AXObjectCache::get(Node* node)
327 {
328     if (!node)
329         return nullptr;
330
331     AXID renderID = node->renderer() ? m_renderObjectMapping.get(node->renderer()) : 0;
332     ASSERT(!HashTraits<AXID>::isDeletedValue(renderID));
333
334     AXID nodeID = m_nodeObjectMapping.get(node);
335     ASSERT(!HashTraits<AXID>::isDeletedValue(nodeID));
336
337     if (node->renderer() && nodeID && !renderID) {
338         // This can happen if an AccessibilityNodeObject is created for a node that's not
339         // rendered, but later something changes and it gets a renderer (like if it's
340         // reparented).
341         remove(nodeID);
342         return nullptr;
343     }
344
345     if (renderID)
346         return m_objects.get(renderID);
347
348     if (!nodeID)
349         return nullptr;
350
351     return m_objects.get(nodeID);
352 }
353
354 // FIXME: This probably belongs on Node.
355 // FIXME: This should take a const char*, but one caller passes nullAtom.
356 bool nodeHasRole(Node* node, const String& role)
357 {
358     if (!node || !is<Element>(node))
359         return false;
360
361     auto& roleValue = downcast<Element>(*node).fastGetAttribute(roleAttr);
362     if (role.isNull())
363         return roleValue.isEmpty();
364     if (roleValue.isEmpty())
365         return false;
366
367     return SpaceSplitString(roleValue, true).contains(role);
368 }
369
370 static Ref<AccessibilityObject> createFromRenderer(RenderObject* renderer)
371 {
372     // FIXME: How could renderer->node() ever not be an Element?
373     Node* node = renderer->node();
374
375     // If the node is aria role="list" or the aria role is empty and its a
376     // ul/ol/dl type (it shouldn't be a list if aria says otherwise).
377     if (node && ((nodeHasRole(node, "list") || nodeHasRole(node, "directory"))
378                       || (nodeHasRole(node, nullAtom) && (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(dlTag)))))
379         return AccessibilityList::create(renderer);
380
381     // aria tables
382     if (nodeHasRole(node, "grid") || nodeHasRole(node, "treegrid") || nodeHasRole(node, "table"))
383         return AccessibilityARIAGrid::create(renderer);
384     if (nodeHasRole(node, "row"))
385         return AccessibilityARIAGridRow::create(renderer);
386     if (nodeHasRole(node, "gridcell") || nodeHasRole(node, "cell") || nodeHasRole(node, "columnheader") || nodeHasRole(node, "rowheader"))
387         return AccessibilityARIAGridCell::create(renderer);
388
389     // aria tree
390     if (nodeHasRole(node, "tree"))
391         return AccessibilityTree::create(renderer);
392     if (nodeHasRole(node, "treeitem"))
393         return AccessibilityTreeItem::create(renderer);
394
395 #if ENABLE(VIDEO)
396     // media controls
397     if (node && node->isMediaControlElement())
398         return AccessibilityMediaControl::create(renderer);
399 #endif
400
401     if (is<RenderSVGRoot>(*renderer))
402         return AccessibilitySVGRoot::create(renderer);
403     
404     if (is<RenderBoxModelObject>(*renderer)) {
405         RenderBoxModelObject& cssBox = downcast<RenderBoxModelObject>(*renderer);
406         if (is<RenderListBox>(cssBox))
407             return AccessibilityListBox::create(&downcast<RenderListBox>(cssBox));
408         if (is<RenderMenuList>(cssBox))
409             return AccessibilityMenuList::create(&downcast<RenderMenuList>(cssBox));
410
411         // standard tables
412         if (is<RenderTable>(cssBox))
413             return AccessibilityTable::create(&downcast<RenderTable>(cssBox));
414         if (is<RenderTableRow>(cssBox))
415             return AccessibilityTableRow::create(&downcast<RenderTableRow>(cssBox));
416         if (is<RenderTableCell>(cssBox))
417             return AccessibilityTableCell::create(&downcast<RenderTableCell>(cssBox));
418
419         // progress bar
420         if (is<RenderProgress>(cssBox))
421             return AccessibilityProgressIndicator::create(&downcast<RenderProgress>(cssBox));
422
423 #if ENABLE(METER_ELEMENT)
424         if (is<RenderMeter>(cssBox))
425             return AccessibilityProgressIndicator::create(&downcast<RenderMeter>(cssBox));
426 #endif
427
428         // input type=range
429         if (is<RenderSlider>(cssBox))
430             return AccessibilitySlider::create(&downcast<RenderSlider>(cssBox));
431     }
432
433     return AccessibilityRenderObject::create(renderer);
434 }
435
436 static Ref<AccessibilityObject> createFromNode(Node* node)
437 {
438     return AccessibilityNodeObject::create(node);
439 }
440
441 AccessibilityObject* AXObjectCache::getOrCreate(Widget* widget)
442 {
443     if (!widget)
444         return nullptr;
445
446     if (AccessibilityObject* obj = get(widget))
447         return obj;
448     
449     RefPtr<AccessibilityObject> newObj;
450     if (is<ScrollView>(*widget))
451         newObj = AccessibilityScrollView::create(downcast<ScrollView>(widget));
452     else if (is<Scrollbar>(*widget))
453         newObj = AccessibilityScrollbar::create(downcast<Scrollbar>(widget));
454
455     // Will crash later if we have two objects for the same widget.
456     ASSERT(!get(widget));
457
458     // Catch the case if an (unsupported) widget type is used. Only FrameView and ScrollBar are supported now.
459     ASSERT(newObj);
460     if (!newObj)
461         return nullptr;
462
463     getAXID(newObj.get());
464     
465     m_widgetObjectMapping.set(widget, newObj->axObjectID());
466     m_objects.set(newObj->axObjectID(), newObj);    
467     newObj->init();
468     attachWrapper(newObj.get());
469     return newObj.get();
470 }
471
472 AccessibilityObject* AXObjectCache::getOrCreate(Node* node)
473 {
474     if (!node)
475         return nullptr;
476
477     if (AccessibilityObject* obj = get(node))
478         return obj;
479
480     if (node->renderer())
481         return getOrCreate(node->renderer());
482
483     if (!node->parentElement())
484         return nullptr;
485     
486     // It's only allowed to create an AccessibilityObject from a Node if it's in a canvas subtree.
487     // Or if it's a hidden element, but we still want to expose it because of other ARIA attributes.
488     bool inCanvasSubtree = lineageOfType<HTMLCanvasElement>(*node->parentElement()).first();
489     bool isHidden = isNodeAriaVisible(node);
490
491     bool insideMeterElement = false;
492 #if ENABLE(METER_ELEMENT)
493     insideMeterElement = is<HTMLMeterElement>(*node->parentElement());
494 #endif
495     
496     if (!inCanvasSubtree && !isHidden && !insideMeterElement)
497         return nullptr;
498
499     // Fallback content is only focusable as long as the canvas is displayed and visible.
500     // Update the style before Element::isFocusable() gets called.
501     if (inCanvasSubtree)
502         node->document().updateStyleIfNeeded();
503
504     RefPtr<AccessibilityObject> newObj = createFromNode(node);
505
506     // Will crash later if we have two objects for the same node.
507     ASSERT(!get(node));
508
509     getAXID(newObj.get());
510
511     m_nodeObjectMapping.set(node, newObj->axObjectID());
512     m_objects.set(newObj->axObjectID(), newObj);
513     newObj->init();
514     attachWrapper(newObj.get());
515     newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
516     // Sometimes asking accessibilityIsIgnored() will cause the newObject to be deallocated, and then
517     // it will disappear when this function is finished, leading to a use-after-free.
518     if (newObj->isDetached())
519         return nullptr;
520     
521     return newObj.get();
522 }
523
524 AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
525 {
526     if (!renderer)
527         return nullptr;
528
529     if (AccessibilityObject* obj = get(renderer))
530         return obj;
531
532     RefPtr<AccessibilityObject> newObj = createFromRenderer(renderer);
533
534     // Will crash later if we have two objects for the same renderer.
535     ASSERT(!get(renderer));
536
537     getAXID(newObj.get());
538
539     m_renderObjectMapping.set(renderer, newObj->axObjectID());
540     m_objects.set(newObj->axObjectID(), newObj);
541     newObj->init();
542     attachWrapper(newObj.get());
543     newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
544     // Sometimes asking accessibilityIsIgnored() will cause the newObject to be deallocated, and then
545     // it will disappear when this function is finished, leading to a use-after-free.
546     if (newObj->isDetached())
547         return nullptr;
548     
549     return newObj.get();
550 }
551     
552 AccessibilityObject* AXObjectCache::rootObject()
553 {
554     if (!gAccessibilityEnabled)
555         return nullptr;
556
557     return getOrCreate(m_document.view());
558 }
559
560 AccessibilityObject* AXObjectCache::rootObjectForFrame(Frame* frame)
561 {
562     if (!gAccessibilityEnabled)
563         return nullptr;
564
565     if (!frame)
566         return nullptr;
567     return getOrCreate(frame->view());
568 }    
569     
570 AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole role)
571 {
572     RefPtr<AccessibilityObject> obj = nullptr;
573     
574     // will be filled in...
575     switch (role) {
576     case ListBoxOptionRole:
577         obj = AccessibilityListBoxOption::create();
578         break;
579     case ImageMapLinkRole:
580         obj = AccessibilityImageMapLink::create();
581         break;
582     case ColumnRole:
583         obj = AccessibilityTableColumn::create();
584         break;            
585     case TableHeaderContainerRole:
586         obj = AccessibilityTableHeaderContainer::create();
587         break;   
588     case SliderThumbRole:
589         obj = AccessibilitySliderThumb::create();
590         break;
591     case MenuListPopupRole:
592         obj = AccessibilityMenuListPopup::create();
593         break;
594     case MenuListOptionRole:
595         obj = AccessibilityMenuListOption::create();
596         break;
597     case SpinButtonRole:
598         obj = AccessibilitySpinButton::create();
599         break;
600     case SpinButtonPartRole:
601         obj = AccessibilitySpinButtonPart::create();
602         break;
603     default:
604         obj = nullptr;
605     }
606     
607     if (obj)
608         getAXID(obj.get());
609     else
610         return nullptr;
611
612     m_objects.set(obj->axObjectID(), obj);    
613     obj->init();
614     attachWrapper(obj.get());
615     return obj.get();
616 }
617
618 void AXObjectCache::remove(AXID axID)
619 {
620     if (!axID)
621         return;
622     
623     // first fetch object to operate some cleanup functions on it 
624     AccessibilityObject* obj = m_objects.get(axID);
625     if (!obj)
626         return;
627     
628     detachWrapper(obj, ElementDestroyed);
629     obj->detach(ElementDestroyed, this);
630     removeAXID(obj);
631     
632     // finally remove the object
633     if (!m_objects.take(axID))
634         return;
635     
636     ASSERT(m_objects.size() >= m_idsInUse.size());    
637 }
638     
639 void AXObjectCache::remove(RenderObject* renderer)
640 {
641     if (!renderer)
642         return;
643     
644     AXID axID = m_renderObjectMapping.get(renderer);
645     remove(axID);
646     m_renderObjectMapping.remove(renderer);
647 }
648
649 void AXObjectCache::remove(Node* node)
650 {
651     if (!node)
652         return;
653
654     removeNodeForUse(node);
655
656     // This is all safe even if we didn't have a mapping.
657     AXID axID = m_nodeObjectMapping.get(node);
658     remove(axID);
659     m_nodeObjectMapping.remove(node);
660
661     // Cleanup for aria modal nodes.
662     if (m_currentAriaModalNode == node)
663         m_currentAriaModalNode = nullptr;
664     if (m_ariaModalNodesSet.contains(node))
665         m_ariaModalNodesSet.remove(node);
666     
667     if (node->renderer()) {
668         remove(node->renderer());
669         return;
670     }
671 }
672
673 void AXObjectCache::remove(Widget* view)
674 {
675     if (!view)
676         return;
677         
678     AXID axID = m_widgetObjectMapping.get(view);
679     remove(axID);
680     m_widgetObjectMapping.remove(view);
681 }
682     
683     
684 #if !PLATFORM(WIN)
685 AXID AXObjectCache::platformGenerateAXID() const
686 {
687     static AXID lastUsedID = 0;
688
689     // Generate a new ID.
690     AXID objID = lastUsedID;
691     do {
692         ++objID;
693     } while (!objID || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
694
695     lastUsedID = objID;
696
697     return objID;
698 }
699 #endif
700
701 AXID AXObjectCache::getAXID(AccessibilityObject* obj)
702 {
703     // check for already-assigned ID
704     AXID objID = obj->axObjectID();
705     if (objID) {
706         ASSERT(m_idsInUse.contains(objID));
707         return objID;
708     }
709
710     objID = platformGenerateAXID();
711
712     m_idsInUse.add(objID);
713     obj->setAXObjectID(objID);
714     
715     return objID;
716 }
717
718 void AXObjectCache::removeAXID(AccessibilityObject* object)
719 {
720     if (!object)
721         return;
722     
723     AXID objID = object->axObjectID();
724     if (!objID)
725         return;
726     ASSERT(!HashTraits<AXID>::isDeletedValue(objID));
727     ASSERT(m_idsInUse.contains(objID));
728     object->setAXObjectID(0);
729     m_idsInUse.remove(objID);
730 }
731
732 void AXObjectCache::textChanged(Node* node)
733 {
734     textChanged(getOrCreate(node));
735 }
736
737 void AXObjectCache::textChanged(RenderObject* renderer)
738 {
739     textChanged(getOrCreate(renderer));
740 }
741
742 void AXObjectCache::textChanged(AccessibilityObject* obj)
743 {
744     if (!obj)
745         return;
746
747     bool parentAlreadyExists = obj->parentObjectIfExists();
748     obj->textChanged();
749     postNotification(obj, obj->document(), AXObjectCache::AXTextChanged);
750     if (parentAlreadyExists)
751         obj->notifyIfIgnoredValueChanged();
752 }
753
754 void AXObjectCache::updateCacheAfterNodeIsAttached(Node* node)
755 {
756     // Calling get() will update the AX object if we had an AccessibilityNodeObject but now we need
757     // an AccessibilityRenderObject, because it was reparented to a location outside of a canvas.
758     get(node);
759 }
760
761 void AXObjectCache::handleMenuOpened(Node* node)
762 {
763     if (!node || !node->renderer() || !nodeHasRole(node, "menu"))
764         return;
765     
766     postNotification(getOrCreate(node), &document(), AXMenuOpened);
767 }
768     
769 void AXObjectCache::handleLiveRegionCreated(Node* node)
770 {
771     if (!is<Element>(node) || !node->renderer())
772         return;
773     
774     Element* element = downcast<Element>(node);
775     String liveRegionStatus = element->fastGetAttribute(aria_liveAttr);
776     if (liveRegionStatus.isEmpty()) {
777         const AtomicString& ariaRole = element->fastGetAttribute(roleAttr);
778         if (!ariaRole.isEmpty())
779             liveRegionStatus = AccessibilityObject::defaultLiveRegionStatusForRole(AccessibilityObject::ariaRoleToWebCoreRole(ariaRole));
780     }
781     
782     if (AccessibilityObject::liveRegionStatusIsEnabled(liveRegionStatus))
783         postNotification(getOrCreate(node), &document(), AXLiveRegionCreated);
784 }
785     
786 void AXObjectCache::childrenChanged(Node* node, Node* newChild)
787 {
788     if (newChild) {
789         handleMenuOpened(newChild);
790         handleLiveRegionCreated(newChild);
791     }
792     
793     childrenChanged(get(node));
794 }
795
796 void AXObjectCache::childrenChanged(RenderObject* renderer, RenderObject* newChild)
797 {
798     if (!renderer)
799         return;
800     
801     if (newChild) {
802         handleMenuOpened(newChild->node());
803         handleLiveRegionCreated(newChild->node());
804     }
805     
806     childrenChanged(get(renderer));
807 }
808
809 void AXObjectCache::childrenChanged(AccessibilityObject* obj)
810 {
811     if (!obj)
812         return;
813
814     obj->childrenChanged();
815 }
816     
817 void AXObjectCache::notificationPostTimerFired()
818 {
819     Ref<Document> protectorForCacheOwner(m_document);
820     m_notificationPostTimer.stop();
821     
822     // In tests, posting notifications has a tendency to immediately queue up other notifications, which can lead to unexpected behavior
823     // when the notification list is cleared at the end. Instead copy this list at the start.
824     auto notifications = WTFMove(m_notificationsToPost);
825     
826     for (const auto& note : notifications) {
827         AccessibilityObject* obj = note.first.get();
828         if (!obj->axObjectID())
829             continue;
830
831         if (!obj->axObjectCache())
832             continue;
833         
834 #ifndef NDEBUG
835         // Make sure none of the render views are in the process of being layed out.
836         // Notifications should only be sent after the renderer has finished
837         if (is<AccessibilityRenderObject>(*obj)) {
838             if (auto* renderer = downcast<AccessibilityRenderObject>(*obj).renderer())
839                 ASSERT(!renderer->view().layoutState());
840         }
841 #endif
842
843         AXNotification notification = note.second;
844         
845         // Ensure that this menu really is a menu. We do this check here so that we don't have to create
846         // the axChildren when the menu is marked as opening.
847         if (notification == AXMenuOpened) {
848             obj->updateChildrenIfNecessary();
849             if (obj->roleValue() != MenuRole)
850                 continue;
851         }
852         
853         postPlatformNotification(obj, notification);
854
855         if (notification == AXChildrenChanged && obj->parentObjectIfExists() && obj->lastKnownIsIgnoredValue() != obj->accessibilityIsIgnored())
856             childrenChanged(obj->parentObject());
857     }
858 }
859
860 void AXObjectCache::passwordNotificationPostTimerFired()
861 {
862 #if PLATFORM(COCOA)
863     m_passwordNotificationPostTimer.stop();
864
865     // In tests, posting notifications has a tendency to immediately queue up other notifications, which can lead to unexpected behavior
866     // when the notification list is cleared at the end. Instead copy this list at the start.
867     auto notifications = WTFMove(m_passwordNotificationsToPost);
868
869     for (auto& notification : notifications)
870         postTextStateChangePlatformNotification(notification.get(), AXTextEditTypeInsert, " ", VisiblePosition());
871 #endif
872 }
873     
874 void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, PostTarget postTarget, PostType postType)
875 {
876     if (!renderer)
877         return;
878     
879     stopCachingComputedObjectAttributes();
880
881     // Get an accessibility object that already exists. One should not be created here
882     // because a render update may be in progress and creating an AX object can re-trigger a layout
883     RefPtr<AccessibilityObject> object = get(renderer);
884     while (!object && renderer) {
885         renderer = renderer->parent();
886         object = get(renderer); 
887     }
888     
889     if (!renderer)
890         return;
891     
892     postNotification(object.get(), &renderer->document(), notification, postTarget, postType);
893 }
894
895 void AXObjectCache::postNotification(Node* node, AXNotification notification, PostTarget postTarget, PostType postType)
896 {
897     if (!node)
898         return;
899     
900     stopCachingComputedObjectAttributes();
901
902     // Get an accessibility object that already exists. One should not be created here
903     // because a render update may be in progress and creating an AX object can re-trigger a layout
904     RefPtr<AccessibilityObject> object = get(node);
905     while (!object && node) {
906         node = node->parentNode();
907         object = get(node);
908     }
909     
910     if (!node)
911         return;
912     
913     postNotification(object.get(), &node->document(), notification, postTarget, postType);
914 }
915
916 void AXObjectCache::postNotification(AccessibilityObject* object, Document* document, AXNotification notification, PostTarget postTarget, PostType postType)
917 {
918     stopCachingComputedObjectAttributes();
919
920     if (object && postTarget == TargetObservableParent)
921         object = object->observableObject();
922
923     if (!object && document)
924         object = get(document->renderView());
925
926     if (!object)
927         return;
928
929     if (postType == PostAsynchronously) {
930         m_notificationsToPost.append(std::make_pair(object, notification));
931         if (!m_notificationPostTimer.isActive())
932             m_notificationPostTimer.startOneShot(0);
933     } else
934         postPlatformNotification(object, notification);
935 }
936
937 void AXObjectCache::checkedStateChanged(Node* node)
938 {
939     postNotification(node, AXObjectCache::AXCheckedStateChanged);
940 }
941
942 void AXObjectCache::handleMenuItemSelected(Node* node)
943 {
944     if (!node)
945         return;
946     
947     if (!nodeHasRole(node, "menuitem") && !nodeHasRole(node, "menuitemradio") && !nodeHasRole(node, "menuitemcheckbox"))
948         return;
949     
950     if (!downcast<Element>(*node).focused() && !equalIgnoringCase(downcast<Element>(*node).fastGetAttribute(aria_selectedAttr), "true"))
951         return;
952     
953     postNotification(getOrCreate(node), &document(), AXMenuListItemSelected);
954 }
955     
956 void AXObjectCache::handleFocusedUIElementChanged(Node* oldNode, Node* newNode)
957 {
958     handleMenuItemSelected(newNode);
959     platformHandleFocusedUIElementChanged(oldNode, newNode);
960 }
961     
962 void AXObjectCache::selectedChildrenChanged(Node* node)
963 {
964     handleMenuItemSelected(node);
965     
966     // postTarget is TargetObservableParent so that you can pass in any child of an element and it will go up the parent tree
967     // to find the container which should send out the notification.
968     postNotification(node, AXSelectedChildrenChanged, TargetObservableParent);
969 }
970
971 void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
972 {
973     if (renderer)
974         handleMenuItemSelected(renderer->node());
975
976     // postTarget is TargetObservableParent so that you can pass in any child of an element and it will go up the parent tree
977     // to find the container which should send out the notification.
978     postNotification(renderer, AXSelectedChildrenChanged, TargetObservableParent);
979 }
980
981 #ifndef NDEBUG
982 void AXObjectCache::showIntent(const AXTextStateChangeIntent &intent)
983 {
984     switch (intent.type) {
985     case AXTextStateChangeTypeUnknown:
986         dataLog("Unknown");
987         break;
988     case AXTextStateChangeTypeEdit:
989         dataLog("Edit::");
990         break;
991     case AXTextStateChangeTypeSelectionMove:
992         dataLog("Move::");
993         break;
994     case AXTextStateChangeTypeSelectionExtend:
995         dataLog("Extend::");
996         break;
997     }
998     switch (intent.type) {
999     case AXTextStateChangeTypeUnknown:
1000         break;
1001     case AXTextStateChangeTypeEdit:
1002         switch (intent.change) {
1003         case AXTextEditTypeUnknown:
1004             dataLog("Unknown");
1005             break;
1006         case AXTextEditTypeDelete:
1007             dataLog("Delete");
1008             break;
1009         case AXTextEditTypeInsert:
1010             dataLog("Insert");
1011             break;
1012         case AXTextEditTypeDictation:
1013             dataLog("DictationInsert");
1014             break;
1015         case AXTextEditTypeTyping:
1016             dataLog("TypingInsert");
1017             break;
1018         case AXTextEditTypeCut:
1019             dataLog("Cut");
1020             break;
1021         case AXTextEditTypePaste:
1022             dataLog("Paste");
1023             break;
1024         case AXTextEditTypeAttributesChange:
1025             dataLog("AttributesChange");
1026             break;
1027         }
1028         break;
1029     case AXTextStateChangeTypeSelectionMove:
1030     case AXTextStateChangeTypeSelectionExtend:
1031         switch (intent.selection.direction) {
1032         case AXTextSelectionDirectionUnknown:
1033             dataLog("Unknown::");
1034             break;
1035         case AXTextSelectionDirectionBeginning:
1036             dataLog("Beginning::");
1037             break;
1038         case AXTextSelectionDirectionEnd:
1039             dataLog("End::");
1040             break;
1041         case AXTextSelectionDirectionPrevious:
1042             dataLog("Previous::");
1043             break;
1044         case AXTextSelectionDirectionNext:
1045             dataLog("Next::");
1046             break;
1047         case AXTextSelectionDirectionDiscontiguous:
1048             dataLog("Discontiguous::");
1049             break;
1050         }
1051         switch (intent.selection.direction) {
1052         case AXTextSelectionDirectionUnknown:
1053         case AXTextSelectionDirectionBeginning:
1054         case AXTextSelectionDirectionEnd:
1055         case AXTextSelectionDirectionPrevious:
1056         case AXTextSelectionDirectionNext:
1057             switch (intent.selection.granularity) {
1058             case AXTextSelectionGranularityUnknown:
1059                 dataLog("Unknown");
1060                 break;
1061             case AXTextSelectionGranularityCharacter:
1062                 dataLog("Character");
1063                 break;
1064             case AXTextSelectionGranularityWord:
1065                 dataLog("Word");
1066                 break;
1067             case AXTextSelectionGranularityLine:
1068                 dataLog("Line");
1069                 break;
1070             case AXTextSelectionGranularitySentence:
1071                 dataLog("Sentence");
1072                 break;
1073             case AXTextSelectionGranularityParagraph:
1074                 dataLog("Paragraph");
1075                 break;
1076             case AXTextSelectionGranularityPage:
1077                 dataLog("Page");
1078                 break;
1079             case AXTextSelectionGranularityDocument:
1080                 dataLog("Document");
1081                 break;
1082             case AXTextSelectionGranularityAll:
1083                 dataLog("All");
1084                 break;
1085             }
1086             break;
1087         case AXTextSelectionDirectionDiscontiguous:
1088             break;
1089         }
1090         break;
1091     }
1092     dataLog("\n");
1093 }
1094 #endif
1095
1096 void AXObjectCache::setTextSelectionIntent(const AXTextStateChangeIntent& intent)
1097 {
1098     m_textSelectionIntent = intent;
1099 }
1100     
1101 void AXObjectCache::setIsSynchronizingSelection(bool isSynchronizing)
1102 {
1103     m_isSynchronizingSelection = isSynchronizing;
1104 }
1105
1106 static bool isPasswordFieldOrContainedByPasswordField(AccessibilityObject* object)
1107 {
1108     return object && (object->isPasswordField() || object->isContainedByPasswordField());
1109 }
1110
1111 void AXObjectCache::postTextStateChangeNotification(Node* node, const AXTextStateChangeIntent& intent, const VisibleSelection& selection)
1112 {
1113     if (!node)
1114         return;
1115
1116 #if PLATFORM(COCOA)
1117     stopCachingComputedObjectAttributes();
1118
1119     postTextStateChangeNotification(getOrCreate(node), intent, selection);
1120 #else
1121     postNotification(node->renderer(), AXObjectCache::AXSelectedTextChanged, TargetObservableParent);
1122     UNUSED_PARAM(intent);
1123     UNUSED_PARAM(selection);
1124 #endif
1125 }
1126
1127 void AXObjectCache::postTextStateChangeNotification(const Position& position, const AXTextStateChangeIntent& intent, const VisibleSelection& selection)
1128 {
1129     Node* node = position.deprecatedNode();
1130     if (!node)
1131         return;
1132
1133     stopCachingComputedObjectAttributes();
1134
1135 #if PLATFORM(COCOA)
1136     AccessibilityObject* object = getOrCreate(node);
1137     if (object && object->accessibilityIsIgnored()) {
1138         if (position.atLastEditingPositionForNode()) {
1139             if (AccessibilityObject* nextSibling = object->nextSiblingUnignored(1))
1140                 object = nextSibling;
1141         } else if (position.atFirstEditingPositionForNode()) {
1142             if (AccessibilityObject* previousSibling = object->previousSiblingUnignored(1))
1143                 object = previousSibling;
1144         }
1145     }
1146
1147     postTextStateChangeNotification(object, intent, selection);
1148 #else
1149     postTextStateChangeNotification(node, intent, selection);
1150 #endif
1151 }
1152
1153 void AXObjectCache::postTextStateChangeNotification(AccessibilityObject* object, const AXTextStateChangeIntent& intent, const VisibleSelection& selection)
1154 {
1155     stopCachingComputedObjectAttributes();
1156
1157 #if PLATFORM(COCOA)
1158     if (object) {
1159         if (isPasswordFieldOrContainedByPasswordField(object))
1160             return;
1161
1162         if (auto observableObject = object->observableObject())
1163             object = observableObject;
1164     }
1165
1166     const AXTextStateChangeIntent& newIntent = (intent.type == AXTextStateChangeTypeUnknown || (m_isSynchronizingSelection && m_textSelectionIntent.type != AXTextStateChangeTypeUnknown)) ? m_textSelectionIntent : intent;
1167     postTextStateChangePlatformNotification(object, newIntent, selection);
1168 #else
1169     UNUSED_PARAM(object);
1170     UNUSED_PARAM(intent);
1171     UNUSED_PARAM(selection);
1172 #endif
1173
1174     setTextSelectionIntent(AXTextStateChangeIntent());
1175     setIsSynchronizingSelection(false);
1176 }
1177
1178 void AXObjectCache::postTextStateChangeNotification(Node* node, AXTextEditType type, const String& text, const VisiblePosition& position)
1179 {
1180     if (!node)
1181         return;
1182     ASSERT(type != AXTextEditTypeUnknown);
1183
1184     stopCachingComputedObjectAttributes();
1185
1186     AccessibilityObject* object = getOrCreate(node);
1187 #if PLATFORM(COCOA)
1188     if (object) {
1189         if (enqueuePasswordValueChangeNotification(object))
1190             return;
1191         object = object->observableObject();
1192     }
1193
1194     postTextStateChangePlatformNotification(object, type, text, position);
1195 #else
1196     nodeTextChangePlatformNotification(object, textChangeForEditType(type), position.deepEquivalent().deprecatedEditingOffset(), text);
1197 #endif
1198 }
1199
1200 void AXObjectCache::postTextReplacementNotification(Node* node, AXTextEditType deletionType, const String& deletedText, AXTextEditType insertionType, const String& insertedText, const VisiblePosition& position)
1201 {
1202     if (!node)
1203         return;
1204     ASSERT(deletionType == AXTextEditTypeDelete);
1205     ASSERT(insertionType == AXTextEditTypeInsert || insertionType == AXTextEditTypeTyping || insertionType == AXTextEditTypeDictation || insertionType == AXTextEditTypePaste);
1206
1207     stopCachingComputedObjectAttributes();
1208
1209     AccessibilityObject* object = getOrCreate(node);
1210 #if PLATFORM(COCOA)
1211     if (object) {
1212         if (enqueuePasswordValueChangeNotification(object))
1213             return;
1214         object = object->observableObject();
1215     }
1216
1217     postTextReplacementPlatformNotification(object, deletionType, deletedText, insertionType, insertedText, position);
1218 #else
1219     nodeTextChangePlatformNotification(object, textChangeForEditType(deletionType), position.deepEquivalent().deprecatedEditingOffset(), deletedText);
1220     nodeTextChangePlatformNotification(object, textChangeForEditType(insertionType), position.deepEquivalent().deprecatedEditingOffset(), insertedText);
1221 #endif
1222 }
1223
1224 bool AXObjectCache::enqueuePasswordValueChangeNotification(AccessibilityObject* object)
1225 {
1226     if (!isPasswordFieldOrContainedByPasswordField(object))
1227         return false;
1228
1229     AccessibilityObject* observableObject = object->observableObject();
1230     if (!observableObject) {
1231         ASSERT_NOT_REACHED();
1232         // return true even though the enqueue didn't happen because this is a password field and caller shouldn't post a notification
1233         return true;
1234     }
1235
1236     m_passwordNotificationsToPost.add(observableObject);
1237     if (!m_passwordNotificationPostTimer.isActive())
1238         m_passwordNotificationPostTimer.startOneShot(AccessibilityPasswordValueChangeNotificationInterval);
1239
1240     return true;
1241 }
1242
1243 void AXObjectCache::frameLoadingEventNotification(Frame* frame, AXLoadingEvent loadingEvent)
1244 {
1245     if (!frame)
1246         return;
1247
1248     // Delegate on the right platform
1249     RenderView* contentRenderer = frame->contentRenderer();
1250     if (!contentRenderer)
1251         return;
1252
1253     AccessibilityObject* obj = getOrCreate(contentRenderer);
1254     frameLoadingEventPlatformNotification(obj, loadingEvent);
1255 }
1256
1257 void AXObjectCache::postLiveRegionChangeNotification(AccessibilityObject* object)
1258 {
1259     if (m_liveRegionChangedPostTimer.isActive())
1260         m_liveRegionChangedPostTimer.stop();
1261
1262     if (!m_liveRegionObjectsSet.contains(object))
1263         m_liveRegionObjectsSet.add(object);
1264
1265     m_liveRegionChangedPostTimer.startOneShot(AccessibilityLiveRegionChangedNotificationInterval);
1266 }
1267
1268 void AXObjectCache::liveRegionChangedNotificationPostTimerFired()
1269 {
1270     m_liveRegionChangedPostTimer.stop();
1271
1272     if (m_liveRegionObjectsSet.isEmpty())
1273         return;
1274
1275     for (auto& object : m_liveRegionObjectsSet)
1276         postNotification(object.get(), object->document(), AXObjectCache::AXLiveRegionChanged);
1277     m_liveRegionObjectsSet.clear();
1278 }
1279
1280 void AXObjectCache::handleScrollbarUpdate(ScrollView* view)
1281 {
1282     if (!view)
1283         return;
1284     
1285     // We don't want to create a scroll view from this method, only update an existing one.
1286     if (AccessibilityObject* scrollViewObject = get(view)) {
1287         stopCachingComputedObjectAttributes();
1288         scrollViewObject->updateChildrenIfNecessary();
1289     }
1290 }
1291     
1292 void AXObjectCache::handleAriaExpandedChange(Node* node)
1293 {
1294     if (AccessibilityObject* obj = getOrCreate(node))
1295         obj->handleAriaExpandedChanged();
1296 }
1297     
1298 void AXObjectCache::handleActiveDescendantChanged(Node* node)
1299 {
1300     if (AccessibilityObject* obj = getOrCreate(node))
1301         obj->handleActiveDescendantChanged();
1302 }
1303
1304 void AXObjectCache::handleAriaRoleChanged(Node* node)
1305 {
1306     stopCachingComputedObjectAttributes();
1307
1308     if (AccessibilityObject* obj = getOrCreate(node)) {
1309         obj->updateAccessibilityRole();
1310         obj->notifyIfIgnoredValueChanged();
1311     }
1312 }
1313
1314 void AXObjectCache::handleAttributeChanged(const QualifiedName& attrName, Element* element)
1315 {
1316     if (attrName == roleAttr)
1317         handleAriaRoleChanged(element);
1318     else if (attrName == altAttr || attrName == titleAttr)
1319         textChanged(element);
1320     else if (attrName == forAttr && is<HTMLLabelElement>(*element))
1321         labelChanged(element);
1322
1323     if (!attrName.localName().string().startsWith("aria-"))
1324         return;
1325
1326     if (attrName == aria_activedescendantAttr)
1327         handleActiveDescendantChanged(element);
1328     else if (attrName == aria_busyAttr)
1329         postNotification(element, AXObjectCache::AXElementBusyChanged);
1330     else if (attrName == aria_valuenowAttr || attrName == aria_valuetextAttr)
1331         postNotification(element, AXObjectCache::AXValueChanged);
1332     else if (attrName == aria_labelAttr || attrName == aria_labeledbyAttr || attrName == aria_labelledbyAttr)
1333         textChanged(element);
1334     else if (attrName == aria_checkedAttr)
1335         checkedStateChanged(element);
1336     else if (attrName == aria_selectedAttr)
1337         selectedChildrenChanged(element);
1338     else if (attrName == aria_expandedAttr)
1339         handleAriaExpandedChange(element);
1340     else if (attrName == aria_hiddenAttr)
1341         childrenChanged(element->parentNode(), element);
1342     else if (attrName == aria_invalidAttr)
1343         postNotification(element, AXObjectCache::AXInvalidStatusChanged);
1344     else if (attrName == aria_modalAttr)
1345         handleAriaModalChange(element);
1346     else
1347         postNotification(element, AXObjectCache::AXAriaAttributeChanged);
1348 }
1349
1350 void AXObjectCache::handleAriaModalChange(Node* node)
1351 {
1352     if (!is<Element>(node))
1353         return;
1354     
1355     if (!nodeHasRole(node, "dialog") && !nodeHasRole(node, "alertdialog"))
1356         return;
1357     
1358     stopCachingComputedObjectAttributes();
1359     if (equalIgnoringCase(downcast<Element>(*node).fastGetAttribute(aria_modalAttr), "true")) {
1360         // Add the newly modified node to the modal nodes set, and set it to be the current valid aria modal node.
1361         // We will recompute the current valid aria modal node in ariaModalNode() when this node is not visible.
1362         m_ariaModalNodesSet.add(node);
1363         m_currentAriaModalNode = node;
1364     } else {
1365         // Remove the node from the modal nodes set. There might be other visible modal nodes, so we recompute here.
1366         m_ariaModalNodesSet.remove(node);
1367         updateCurrentAriaModalNode();
1368     }
1369     startCachingComputedObjectAttributesUntilTreeMutates();
1370 }
1371
1372 void AXObjectCache::labelChanged(Element* element)
1373 {
1374     ASSERT(is<HTMLLabelElement>(*element));
1375     HTMLElement* correspondingControl = downcast<HTMLLabelElement>(*element).control();
1376     textChanged(correspondingControl);
1377 }
1378
1379 void AXObjectCache::recomputeIsIgnored(RenderObject* renderer)
1380 {
1381     if (AccessibilityObject* obj = get(renderer))
1382         obj->notifyIfIgnoredValueChanged();
1383 }
1384
1385 void AXObjectCache::startCachingComputedObjectAttributesUntilTreeMutates()
1386 {
1387     if (!m_computedObjectAttributeCache)
1388         m_computedObjectAttributeCache = std::make_unique<AXComputedObjectAttributeCache>();
1389 }
1390
1391 void AXObjectCache::stopCachingComputedObjectAttributes()
1392 {
1393     m_computedObjectAttributeCache = nullptr;
1394 }
1395
1396 VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData& textMarkerData)
1397 {
1398     if (!isNodeInUse(textMarkerData.node))
1399         return VisiblePosition();
1400     
1401     // FIXME: Accessability should make it clear these are DOM-compliant offsets or store Position objects.
1402     VisiblePosition visiblePos = VisiblePosition(createLegacyEditingPosition(textMarkerData.node, textMarkerData.offset), textMarkerData.affinity);
1403     Position deepPos = visiblePos.deepEquivalent();
1404     if (deepPos.isNull())
1405         return VisiblePosition();
1406     
1407     RenderObject* renderer = deepPos.deprecatedNode()->renderer();
1408     if (!renderer)
1409         return VisiblePosition();
1410     
1411     AXObjectCache* cache = renderer->document().axObjectCache();
1412     if (!cache->isIDinUse(textMarkerData.axID))
1413         return VisiblePosition();
1414     
1415     if (deepPos.deprecatedNode() != textMarkerData.node || deepPos.deprecatedEditingOffset() != textMarkerData.offset)
1416         return VisiblePosition();
1417     
1418     return visiblePos;
1419 }
1420
1421 void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos)
1422 {
1423     // This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence.
1424     // This also allows callers to check for failure by looking at textMarkerData upon return.
1425     memset(&textMarkerData, 0, sizeof(TextMarkerData));
1426     
1427     if (visiblePos.isNull())
1428         return;
1429     
1430     Position deepPos = visiblePos.deepEquivalent();
1431     Node* domNode = deepPos.deprecatedNode();
1432     ASSERT(domNode);
1433     if (!domNode)
1434         return;
1435     
1436     if (is<HTMLInputElement>(*domNode) && downcast<HTMLInputElement>(*domNode).isPasswordField())
1437         return;
1438     
1439     // find or create an accessibility object for this node
1440     AXObjectCache* cache = domNode->document().axObjectCache();
1441     RefPtr<AccessibilityObject> obj = cache->getOrCreate(domNode);
1442     
1443     textMarkerData.axID = obj.get()->axObjectID();
1444     textMarkerData.node = domNode;
1445     textMarkerData.offset = deepPos.deprecatedEditingOffset();
1446     textMarkerData.affinity = visiblePos.affinity();   
1447     
1448     cache->setNodeInUse(domNode);
1449 }
1450
1451 const Element* AXObjectCache::rootAXEditableElement(const Node* node)
1452 {
1453     const Element* result = node->rootEditableElement();
1454     const Element* element = is<Element>(*node) ? downcast<Element>(node) : node->parentElement();
1455
1456     for (; element; element = element->parentElement()) {
1457         if (nodeIsTextControl(element))
1458             result = element;
1459     }
1460
1461     return result;
1462 }
1463
1464 void AXObjectCache::clearTextMarkerNodesInUse(Document* document)
1465 {
1466     if (!document)
1467         return;
1468     
1469     // Check each node to see if it's inside the document being deleted, of if it no longer belongs to a document.
1470     HashSet<Node*> nodesToDelete;
1471     for (const auto& node : m_textMarkerNodes) {
1472         if (!node->inDocument() || &(node)->document() == document)
1473             nodesToDelete.add(node);
1474     }
1475     
1476     for (const auto& node : nodesToDelete)
1477         m_textMarkerNodes.remove(node);
1478 }
1479     
1480 bool AXObjectCache::nodeIsTextControl(const Node* node)
1481 {
1482     if (!node)
1483         return false;
1484
1485     const AccessibilityObject* axObject = getOrCreate(const_cast<Node*>(node));
1486     return axObject && axObject->isTextControl();
1487 }
1488     
1489 bool isNodeAriaVisible(Node* node)
1490 {
1491     if (!node)
1492         return false;
1493
1494     // ARIA Node visibility is controlled by aria-hidden
1495     //  1) if aria-hidden=true, the whole subtree is hidden
1496     //  2) if aria-hidden=false, and the object is rendered, there's no effect
1497     //  3) if aria-hidden=false, and the object is NOT rendered, then it must have
1498     //     aria-hidden=false on each parent until it gets to a rendered object
1499     //  3b) a text node inherits a parents aria-hidden value
1500     bool requiresAriaHiddenFalse = !node->renderer();
1501     bool ariaHiddenFalsePresent = false;
1502     for (Node* testNode = node; testNode; testNode = testNode->parentNode()) {
1503         if (is<Element>(*testNode)) {
1504             const AtomicString& ariaHiddenValue = downcast<Element>(*testNode).fastGetAttribute(aria_hiddenAttr);
1505             if (equalIgnoringCase(ariaHiddenValue, "true"))
1506                 return false;
1507             
1508             bool ariaHiddenFalse = equalIgnoringCase(ariaHiddenValue, "false");
1509             if (!testNode->renderer() && !ariaHiddenFalse)
1510                 return false;
1511             if (!ariaHiddenFalsePresent && ariaHiddenFalse)
1512                 ariaHiddenFalsePresent = true;
1513         }
1514     }
1515     
1516     return !requiresAriaHiddenFalse || ariaHiddenFalsePresent;
1517 }
1518
1519 AccessibilityObject* AXObjectCache::rootWebArea()
1520 {
1521     AccessibilityObject* rootObject = this->rootObject();
1522     if (!rootObject || !rootObject->isAccessibilityScrollView())
1523         return nullptr;
1524     return downcast<AccessibilityScrollView>(*rootObject).webAreaObject();
1525 }
1526
1527 AXAttributeCacheEnabler::AXAttributeCacheEnabler(AXObjectCache* cache)
1528     : m_cache(cache)
1529 {
1530     if (m_cache)
1531         m_cache->startCachingComputedObjectAttributesUntilTreeMutates();
1532 }
1533     
1534 AXAttributeCacheEnabler::~AXAttributeCacheEnabler()
1535 {
1536     if (m_cache)
1537         m_cache->stopCachingComputedObjectAttributes();
1538 }
1539
1540 #if !PLATFORM(COCOA)
1541 AXTextChange AXObjectCache::textChangeForEditType(AXTextEditType type)
1542 {
1543     switch (type) {
1544     case AXTextEditTypeCut:
1545     case AXTextEditTypeDelete:
1546         return AXTextDeleted;
1547     case AXTextEditTypeInsert:
1548     case AXTextEditTypeDictation:
1549     case AXTextEditTypeTyping:
1550     case AXTextEditTypePaste:
1551         return AXTextInserted;
1552     case AXTextEditTypeAttributesChange:
1553         return AXTextAttributesChanged;
1554     case AXTextEditTypeUnknown:
1555         break;
1556     }
1557     ASSERT_NOT_REACHED();
1558     return AXTextInserted;
1559 }
1560 #endif
1561     
1562 } // namespace WebCore
1563
1564 #endif // HAVE(ACCESSIBILITY)