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