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