• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, 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 #include "core/accessibility/AXObjectCache.h"
32 
33 #include "core/HTMLNames.h"
34 #include "core/accessibility/AXARIAGrid.h"
35 #include "core/accessibility/AXARIAGridCell.h"
36 #include "core/accessibility/AXARIAGridRow.h"
37 #include "core/accessibility/AXImageMapLink.h"
38 #include "core/accessibility/AXInlineTextBox.h"
39 #include "core/accessibility/AXList.h"
40 #include "core/accessibility/AXListBox.h"
41 #include "core/accessibility/AXListBoxOption.h"
42 #include "core/accessibility/AXMediaControls.h"
43 #include "core/accessibility/AXMenuList.h"
44 #include "core/accessibility/AXMenuListOption.h"
45 #include "core/accessibility/AXMenuListPopup.h"
46 #include "core/accessibility/AXProgressIndicator.h"
47 #include "core/accessibility/AXRenderObject.h"
48 #include "core/accessibility/AXSVGRoot.h"
49 #include "core/accessibility/AXScrollView.h"
50 #include "core/accessibility/AXScrollbar.h"
51 #include "core/accessibility/AXSlider.h"
52 #include "core/accessibility/AXSpinButton.h"
53 #include "core/accessibility/AXTable.h"
54 #include "core/accessibility/AXTableCell.h"
55 #include "core/accessibility/AXTableColumn.h"
56 #include "core/accessibility/AXTableHeaderContainer.h"
57 #include "core/accessibility/AXTableRow.h"
58 #include "core/dom/Document.h"
59 #include "core/frame/LocalFrame.h"
60 #include "core/frame/Settings.h"
61 #include "core/html/HTMLAreaElement.h"
62 #include "core/html/HTMLImageElement.h"
63 #include "core/html/HTMLInputElement.h"
64 #include "core/html/HTMLLabelElement.h"
65 #include "core/page/Chrome.h"
66 #include "core/page/ChromeClient.h"
67 #include "core/page/FocusController.h"
68 #include "core/page/Page.h"
69 #include "core/rendering/AbstractInlineTextBox.h"
70 #include "core/rendering/RenderListBox.h"
71 #include "core/rendering/RenderMenuList.h"
72 #include "core/rendering/RenderProgress.h"
73 #include "core/rendering/RenderSlider.h"
74 #include "core/rendering/RenderTable.h"
75 #include "core/rendering/RenderTableCell.h"
76 #include "core/rendering/RenderTableRow.h"
77 #include "core/rendering/RenderView.h"
78 #include "platform/scroll/ScrollView.h"
79 #include "wtf/PassRefPtr.h"
80 
81 namespace blink {
82 
83 using namespace HTMLNames;
84 
getIgnored(AXID id) const85 AXObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID id) const
86 {
87     HashMap<AXID, CachedAXObjectAttributes>::const_iterator it = m_idMapping.find(id);
88     return it != m_idMapping.end() ? it->value.ignored : DefaultBehavior;
89 }
90 
setIgnored(AXID id,AXObjectInclusion inclusion)91 void AXComputedObjectAttributeCache::setIgnored(AXID id, AXObjectInclusion inclusion)
92 {
93     HashMap<AXID, CachedAXObjectAttributes>::iterator it = m_idMapping.find(id);
94     if (it != m_idMapping.end()) {
95         it->value.ignored = inclusion;
96     } else {
97         CachedAXObjectAttributes attributes;
98         attributes.ignored = inclusion;
99         m_idMapping.set(id, attributes);
100     }
101 }
102 
clear()103 void AXComputedObjectAttributeCache::clear()
104 {
105     m_idMapping.clear();
106 }
107 
AXObjectCache(Document & document)108 AXObjectCache::AXObjectCache(Document& document)
109     : m_document(document)
110     , m_notificationPostTimer(this, &AXObjectCache::notificationPostTimerFired)
111 {
112     m_computedObjectAttributeCache = AXComputedObjectAttributeCache::create();
113 }
114 
~AXObjectCache()115 AXObjectCache::~AXObjectCache()
116 {
117     m_notificationPostTimer.stop();
118 
119     HashMap<AXID, RefPtr<AXObject> >::iterator end = m_objects.end();
120     for (HashMap<AXID, RefPtr<AXObject> >::iterator it = m_objects.begin(); it != end; ++it) {
121         AXObject* obj = (*it).value.get();
122         detachWrapper(obj);
123         obj->detach();
124         removeAXID(obj);
125     }
126 }
127 
focusedImageMapUIElement(HTMLAreaElement * areaElement)128 AXObject* AXObjectCache::focusedImageMapUIElement(HTMLAreaElement* areaElement)
129 {
130     // Find the corresponding accessibility object for the HTMLAreaElement. This should be
131     // in the list of children for its corresponding image.
132     if (!areaElement)
133         return 0;
134 
135     HTMLImageElement* imageElement = areaElement->imageElement();
136     if (!imageElement)
137         return 0;
138 
139     AXObject* axRenderImage = areaElement->document().axObjectCache()->getOrCreate(imageElement);
140     if (!axRenderImage)
141         return 0;
142 
143     AXObject::AccessibilityChildrenVector imageChildren = axRenderImage->children();
144     unsigned count = imageChildren.size();
145     for (unsigned k = 0; k < count; ++k) {
146         AXObject* child = imageChildren[k].get();
147         if (!child->isImageMapLink())
148             continue;
149 
150         if (toAXImageMapLink(child)->areaElement() == areaElement)
151             return child;
152     }
153 
154     return 0;
155 }
156 
focusedUIElementForPage(const Page * page)157 AXObject* AXObjectCache::focusedUIElementForPage(const Page* page)
158 {
159     if (!page->settings().accessibilityEnabled())
160         return 0;
161 
162     // Cross-process accessibility is not yet implemented.
163     if (!page->focusController().focusedOrMainFrame()->isLocalFrame())
164         return 0;
165 
166     // get the focused node in the page
167     Document* focusedDocument = toLocalFrame(page->focusController().focusedOrMainFrame())->document();
168     Node* focusedNode = focusedDocument->focusedElement();
169     if (!focusedNode)
170         focusedNode = focusedDocument;
171 
172     if (isHTMLAreaElement(*focusedNode))
173         return focusedImageMapUIElement(toHTMLAreaElement(focusedNode));
174 
175     AXObject* obj = focusedNode->document().axObjectCache()->getOrCreate(focusedNode);
176     if (!obj)
177         return 0;
178 
179     if (obj->shouldFocusActiveDescendant()) {
180         if (AXObject* descendant = obj->activeDescendant())
181             obj = descendant;
182     }
183 
184     // the HTML element, for example, is focusable but has an AX object that is ignored
185     if (obj->accessibilityIsIgnored())
186         obj = obj->parentObjectUnignored();
187 
188     return obj;
189 }
190 
get(Widget * widget)191 AXObject* AXObjectCache::get(Widget* widget)
192 {
193     if (!widget)
194         return 0;
195 
196     AXID axID = m_widgetObjectMapping.get(widget);
197     ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
198     if (!axID)
199         return 0;
200 
201     return m_objects.get(axID);
202 }
203 
get(RenderObject * renderer)204 AXObject* AXObjectCache::get(RenderObject* renderer)
205 {
206     if (!renderer)
207         return 0;
208 
209     AXID axID = m_renderObjectMapping.get(renderer);
210     ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
211     if (!axID)
212         return 0;
213 
214     return m_objects.get(axID);
215 }
216 
get(Node * node)217 AXObject* AXObjectCache::get(Node* node)
218 {
219     if (!node)
220         return 0;
221 
222     AXID renderID = node->renderer() ? m_renderObjectMapping.get(node->renderer()) : 0;
223     ASSERT(!HashTraits<AXID>::isDeletedValue(renderID));
224 
225     AXID nodeID = m_nodeObjectMapping.get(node);
226     ASSERT(!HashTraits<AXID>::isDeletedValue(nodeID));
227 
228     if (node->renderer() && nodeID && !renderID) {
229         // This can happen if an AXNodeObject is created for a node that's not
230         // rendered, but later something changes and it gets a renderer (like if it's
231         // reparented).
232         remove(nodeID);
233         return 0;
234     }
235 
236     if (renderID)
237         return m_objects.get(renderID);
238 
239     if (!nodeID)
240         return 0;
241 
242     return m_objects.get(nodeID);
243 }
244 
get(AbstractInlineTextBox * inlineTextBox)245 AXObject* AXObjectCache::get(AbstractInlineTextBox* inlineTextBox)
246 {
247     if (!inlineTextBox)
248         return 0;
249 
250     AXID axID = m_inlineTextBoxObjectMapping.get(inlineTextBox);
251     ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
252     if (!axID)
253         return 0;
254 
255     return m_objects.get(axID);
256 }
257 
258 // FIXME: This probably belongs on Node.
259 // FIXME: This should take a const char*, but one caller passes nullAtom.
nodeHasRole(Node * node,const String & role)260 bool nodeHasRole(Node* node, const String& role)
261 {
262     if (!node || !node->isElementNode())
263         return false;
264 
265     return equalIgnoringCase(toElement(node)->getAttribute(roleAttr), role);
266 }
267 
createFromRenderer(RenderObject * renderer)268 static PassRefPtr<AXObject> createFromRenderer(RenderObject* renderer)
269 {
270     // FIXME: How could renderer->node() ever not be an Element?
271     Node* node = renderer->node();
272 
273     // If the node is aria role="list" or the aria role is empty and its a
274     // ul/ol/dl type (it shouldn't be a list if aria says otherwise).
275     if (node && ((nodeHasRole(node, "list") || nodeHasRole(node, "directory"))
276         || (nodeHasRole(node, nullAtom) && (isHTMLUListElement(*node) || isHTMLOListElement(*node) || isHTMLDListElement(*node)))))
277         return AXList::create(renderer);
278 
279     // aria tables
280     if (nodeHasRole(node, "grid") || nodeHasRole(node, "treegrid"))
281         return AXARIAGrid::create(renderer);
282     if (nodeHasRole(node, "row"))
283         return AXARIAGridRow::create(renderer);
284     if (nodeHasRole(node, "gridcell") || nodeHasRole(node, "columnheader") || nodeHasRole(node, "rowheader"))
285         return AXARIAGridCell::create(renderer);
286 
287     // media controls
288     if (node && node->isMediaControlElement())
289         return AccessibilityMediaControl::create(renderer);
290 
291     if (isHTMLOptionElement(node))
292         return AXListBoxOption::create(renderer);
293 
294     if (renderer->isSVGRoot())
295         return AXSVGRoot::create(renderer);
296 
297     if (renderer->isBoxModelObject()) {
298         RenderBoxModelObject* cssBox = toRenderBoxModelObject(renderer);
299         if (cssBox->isListBox())
300             return AXListBox::create(toRenderListBox(cssBox));
301         if (cssBox->isMenuList())
302             return AXMenuList::create(toRenderMenuList(cssBox));
303 
304         // standard tables
305         if (cssBox->isTable())
306             return AXTable::create(toRenderTable(cssBox));
307         if (cssBox->isTableRow())
308             return AXTableRow::create(toRenderTableRow(cssBox));
309         if (cssBox->isTableCell())
310             return AXTableCell::create(toRenderTableCell(cssBox));
311 
312         // progress bar
313         if (cssBox->isProgress())
314             return AXProgressIndicator::create(toRenderProgress(cssBox));
315 
316         // input type=range
317         if (cssBox->isSlider())
318             return AXSlider::create(toRenderSlider(cssBox));
319     }
320 
321     return AXRenderObject::create(renderer);
322 }
323 
createFromNode(Node * node)324 static PassRefPtr<AXObject> createFromNode(Node* node)
325 {
326     return AXNodeObject::create(node);
327 }
328 
createFromInlineTextBox(AbstractInlineTextBox * inlineTextBox)329 static PassRefPtr<AXObject> createFromInlineTextBox(AbstractInlineTextBox* inlineTextBox)
330 {
331     return AXInlineTextBox::create(inlineTextBox);
332 }
333 
getOrCreate(Widget * widget)334 AXObject* AXObjectCache::getOrCreate(Widget* widget)
335 {
336     if (!widget)
337         return 0;
338 
339     if (AXObject* obj = get(widget))
340         return obj;
341 
342     RefPtr<AXObject> newObj = nullptr;
343     if (widget->isFrameView())
344         newObj = AXScrollView::create(toScrollView(widget));
345     else if (widget->isScrollbar())
346         newObj = AXScrollbar::create(toScrollbar(widget));
347 
348     // Will crash later if we have two objects for the same widget.
349     ASSERT(!get(widget));
350 
351     // Catch the case if an (unsupported) widget type is used. Only FrameView and ScrollBar are supported now.
352     ASSERT(newObj);
353     if (!newObj)
354         return 0;
355 
356     getAXID(newObj.get());
357 
358     m_widgetObjectMapping.set(widget, newObj->axObjectID());
359     m_objects.set(newObj->axObjectID(), newObj);
360     newObj->init();
361     attachWrapper(newObj.get());
362     return newObj.get();
363 }
364 
getOrCreate(Node * node)365 AXObject* AXObjectCache::getOrCreate(Node* node)
366 {
367     if (!node)
368         return 0;
369 
370     if (AXObject* obj = get(node))
371         return obj;
372 
373     if (node->renderer())
374         return getOrCreate(node->renderer());
375 
376     if (!node->parentElement())
377         return 0;
378 
379     // It's only allowed to create an AXObject from a Node if it's in a canvas subtree.
380     // Or if it's a hidden element, but we still want to expose it because of other ARIA attributes.
381     bool inCanvasSubtree = node->parentElement()->isInCanvasSubtree();
382     bool isHidden = !node->renderer() && isNodeAriaVisible(node);
383     if (!inCanvasSubtree && !isHidden)
384         return 0;
385 
386     RefPtr<AXObject> newObj = createFromNode(node);
387 
388     // Will crash later if we have two objects for the same node.
389     ASSERT(!get(node));
390 
391     getAXID(newObj.get());
392 
393     m_nodeObjectMapping.set(node, newObj->axObjectID());
394     m_objects.set(newObj->axObjectID(), newObj);
395     newObj->init();
396     attachWrapper(newObj.get());
397     newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
398 
399     return newObj.get();
400 }
401 
getOrCreate(RenderObject * renderer)402 AXObject* AXObjectCache::getOrCreate(RenderObject* renderer)
403 {
404     if (!renderer)
405         return 0;
406 
407     if (AXObject* obj = get(renderer))
408         return obj;
409 
410     RefPtr<AXObject> newObj = createFromRenderer(renderer);
411 
412     // Will crash later if we have two objects for the same renderer.
413     ASSERT(!get(renderer));
414 
415     getAXID(newObj.get());
416 
417     m_renderObjectMapping.set(renderer, newObj->axObjectID());
418     m_objects.set(newObj->axObjectID(), newObj);
419     newObj->init();
420     attachWrapper(newObj.get());
421     newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
422 
423     return newObj.get();
424 }
425 
getOrCreate(AbstractInlineTextBox * inlineTextBox)426 AXObject* AXObjectCache::getOrCreate(AbstractInlineTextBox* inlineTextBox)
427 {
428     if (!inlineTextBox)
429         return 0;
430 
431     if (AXObject* obj = get(inlineTextBox))
432         return obj;
433 
434     RefPtr<AXObject> newObj = createFromInlineTextBox(inlineTextBox);
435 
436     // Will crash later if we have two objects for the same inlineTextBox.
437     ASSERT(!get(inlineTextBox));
438 
439     getAXID(newObj.get());
440 
441     m_inlineTextBoxObjectMapping.set(inlineTextBox, newObj->axObjectID());
442     m_objects.set(newObj->axObjectID(), newObj);
443     newObj->init();
444     attachWrapper(newObj.get());
445     newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
446 
447     return newObj.get();
448 }
449 
rootObject()450 AXObject* AXObjectCache::rootObject()
451 {
452     if (!accessibilityEnabled())
453         return 0;
454 
455     return getOrCreate(m_document.view());
456 }
457 
getOrCreate(AccessibilityRole role)458 AXObject* AXObjectCache::getOrCreate(AccessibilityRole role)
459 {
460     RefPtr<AXObject> obj = nullptr;
461 
462     // will be filled in...
463     switch (role) {
464     case ImageMapLinkRole:
465         obj = AXImageMapLink::create();
466         break;
467     case ColumnRole:
468         obj = AXTableColumn::create();
469         break;
470     case TableHeaderContainerRole:
471         obj = AXTableHeaderContainer::create();
472         break;
473     case SliderThumbRole:
474         obj = AXSliderThumb::create();
475         break;
476     case MenuListPopupRole:
477         obj = AXMenuListPopup::create();
478         break;
479     case MenuListOptionRole:
480         obj = AXMenuListOption::create();
481         break;
482     case SpinButtonRole:
483         obj = AXSpinButton::create();
484         break;
485     case SpinButtonPartRole:
486         obj = AXSpinButtonPart::create();
487         break;
488     default:
489         obj = nullptr;
490     }
491 
492     if (obj)
493         getAXID(obj.get());
494     else
495         return 0;
496 
497     m_objects.set(obj->axObjectID(), obj);
498     obj->init();
499     attachWrapper(obj.get());
500     return obj.get();
501 }
502 
remove(AXID axID)503 void AXObjectCache::remove(AXID axID)
504 {
505     if (!axID)
506         return;
507 
508     // first fetch object to operate some cleanup functions on it
509     AXObject* obj = m_objects.get(axID);
510     if (!obj)
511         return;
512 
513     detachWrapper(obj);
514     obj->detach();
515     removeAXID(obj);
516 
517     // finally remove the object
518     if (!m_objects.take(axID))
519         return;
520 
521     ASSERT(m_objects.size() >= m_idsInUse.size());
522 }
523 
remove(RenderObject * renderer)524 void AXObjectCache::remove(RenderObject* renderer)
525 {
526     if (!renderer)
527         return;
528 
529     AXID axID = m_renderObjectMapping.get(renderer);
530     remove(axID);
531     m_renderObjectMapping.remove(renderer);
532 }
533 
remove(Node * node)534 void AXObjectCache::remove(Node* node)
535 {
536     if (!node)
537         return;
538 
539     removeNodeForUse(node);
540 
541     // This is all safe even if we didn't have a mapping.
542     AXID axID = m_nodeObjectMapping.get(node);
543     remove(axID);
544     m_nodeObjectMapping.remove(node);
545 
546     if (node->renderer()) {
547         remove(node->renderer());
548         return;
549     }
550 }
551 
remove(Widget * view)552 void AXObjectCache::remove(Widget* view)
553 {
554     if (!view)
555         return;
556 
557     AXID axID = m_widgetObjectMapping.get(view);
558     remove(axID);
559     m_widgetObjectMapping.remove(view);
560 }
561 
remove(AbstractInlineTextBox * inlineTextBox)562 void AXObjectCache::remove(AbstractInlineTextBox* inlineTextBox)
563 {
564     if (!inlineTextBox)
565         return;
566 
567     AXID axID = m_inlineTextBoxObjectMapping.get(inlineTextBox);
568     remove(axID);
569     m_inlineTextBoxObjectMapping.remove(inlineTextBox);
570 }
571 
572 // FIXME: Oilpan: Use a weak hashmap for this instead.
clearWeakMembers(Visitor * visitor)573 void AXObjectCache::clearWeakMembers(Visitor* visitor)
574 {
575     Vector<Node*> deadNodes;
576     for (HashMap<Node*, AXID>::iterator it = m_nodeObjectMapping.begin(); it != m_nodeObjectMapping.end(); ++it) {
577         if (!visitor->isAlive(it->key))
578             deadNodes.append(it->key);
579     }
580     for (unsigned i = 0; i < deadNodes.size(); ++i)
581         remove(deadNodes[i]);
582 }
583 
platformGenerateAXID() const584 AXID AXObjectCache::platformGenerateAXID() const
585 {
586     static AXID lastUsedID = 0;
587 
588     // Generate a new ID.
589     AXID objID = lastUsedID;
590     do {
591         ++objID;
592     } while (!objID || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
593 
594     lastUsedID = objID;
595 
596     return objID;
597 }
598 
getAXID(AXObject * obj)599 AXID AXObjectCache::getAXID(AXObject* obj)
600 {
601     // check for already-assigned ID
602     AXID objID = obj->axObjectID();
603     if (objID) {
604         ASSERT(m_idsInUse.contains(objID));
605         return objID;
606     }
607 
608     objID = platformGenerateAXID();
609 
610     m_idsInUse.add(objID);
611     obj->setAXObjectID(objID);
612 
613     return objID;
614 }
615 
removeAXID(AXObject * object)616 void AXObjectCache::removeAXID(AXObject* object)
617 {
618     if (!object)
619         return;
620 
621     AXID objID = object->axObjectID();
622     if (!objID)
623         return;
624     ASSERT(!HashTraits<AXID>::isDeletedValue(objID));
625     ASSERT(m_idsInUse.contains(objID));
626     object->setAXObjectID(0);
627     m_idsInUse.remove(objID);
628 }
629 
selectionChanged(Node * node)630 void AXObjectCache::selectionChanged(Node* node)
631 {
632     // Find the nearest ancestor that already has an accessibility object, since we
633     // might be in the middle of a layout.
634     while (node) {
635         if (AXObject* obj = get(node)) {
636             obj->selectionChanged();
637             return;
638         }
639         node = node->parentNode();
640     }
641 }
642 
textChanged(Node * node)643 void AXObjectCache::textChanged(Node* node)
644 {
645     textChanged(getOrCreate(node));
646 }
647 
textChanged(RenderObject * renderer)648 void AXObjectCache::textChanged(RenderObject* renderer)
649 {
650     textChanged(getOrCreate(renderer));
651 }
652 
textChanged(AXObject * obj)653 void AXObjectCache::textChanged(AXObject* obj)
654 {
655     if (!obj)
656         return;
657 
658     bool parentAlreadyExists = obj->parentObjectIfExists();
659     obj->textChanged();
660     postNotification(obj, obj->document(), AXObjectCache::AXTextChanged, true);
661     if (parentAlreadyExists)
662         obj->notifyIfIgnoredValueChanged();
663 }
664 
updateCacheAfterNodeIsAttached(Node * node)665 void AXObjectCache::updateCacheAfterNodeIsAttached(Node* node)
666 {
667     // Calling get() will update the AX object if we had an AXNodeObject but now we need
668     // an AXRenderObject, because it was reparented to a location outside of a canvas.
669     get(node);
670 }
671 
childrenChanged(Node * node)672 void AXObjectCache::childrenChanged(Node* node)
673 {
674     childrenChanged(get(node));
675 }
676 
childrenChanged(RenderObject * renderer)677 void AXObjectCache::childrenChanged(RenderObject* renderer)
678 {
679     childrenChanged(get(renderer));
680 }
681 
childrenChanged(AXObject * obj)682 void AXObjectCache::childrenChanged(AXObject* obj)
683 {
684     if (!obj)
685         return;
686 
687     obj->childrenChanged();
688 }
689 
notificationPostTimerFired(Timer<AXObjectCache> *)690 void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*)
691 {
692     RefPtrWillBeRawPtr<Document> protectorForCacheOwner(m_document);
693 
694     m_notificationPostTimer.stop();
695 
696     unsigned i = 0, count = m_notificationsToPost.size();
697     for (i = 0; i < count; ++i) {
698         AXObject* obj = m_notificationsToPost[i].first.get();
699         if (!obj->axObjectID())
700             continue;
701 
702         if (!obj->axObjectCache())
703             continue;
704 
705 #if ENABLE(ASSERT)
706         // Make sure none of the render views are in the process of being layed out.
707         // Notifications should only be sent after the renderer has finished
708         if (obj->isAXRenderObject()) {
709             AXRenderObject* renderObj = toAXRenderObject(obj);
710             RenderObject* renderer = renderObj->renderer();
711             if (renderer && renderer->view())
712                 ASSERT(!renderer->view()->layoutState());
713         }
714 #endif
715 
716         AXNotification notification = m_notificationsToPost[i].second;
717         postPlatformNotification(obj, notification);
718 
719         if (notification == AXChildrenChanged && obj->parentObjectIfExists() && obj->lastKnownIsIgnoredValue() != obj->accessibilityIsIgnored())
720             childrenChanged(obj->parentObject());
721     }
722 
723     m_notificationsToPost.clear();
724 }
725 
postNotification(RenderObject * renderer,AXNotification notification,bool postToElement,PostType postType)726 void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, bool postToElement, PostType postType)
727 {
728     if (!renderer)
729         return;
730 
731     m_computedObjectAttributeCache->clear();
732 
733     // Get an accessibility object that already exists. One should not be created here
734     // because a render update may be in progress and creating an AX object can re-trigger a layout
735     RefPtr<AXObject> object = get(renderer);
736     while (!object && renderer) {
737         renderer = renderer->parent();
738         object = get(renderer);
739     }
740 
741     if (!renderer)
742         return;
743 
744     postNotification(object.get(), &renderer->document(), notification, postToElement, postType);
745 }
746 
postNotification(Node * node,AXNotification notification,bool postToElement,PostType postType)747 void AXObjectCache::postNotification(Node* node, AXNotification notification, bool postToElement, PostType postType)
748 {
749     if (!node)
750         return;
751 
752     m_computedObjectAttributeCache->clear();
753 
754     // Get an accessibility object that already exists. One should not be created here
755     // because a render update may be in progress and creating an AX object can re-trigger a layout
756     RefPtr<AXObject> object = get(node);
757     while (!object && node) {
758         node = node->parentNode();
759         object = get(node);
760     }
761 
762     if (!node)
763         return;
764 
765     postNotification(object.get(), &node->document(), notification, postToElement, postType);
766 }
767 
postNotification(AXObject * object,Document * document,AXNotification notification,bool postToElement,PostType postType)768 void AXObjectCache::postNotification(AXObject* object, Document* document, AXNotification notification, bool postToElement, PostType postType)
769 {
770     m_computedObjectAttributeCache->clear();
771 
772     if (object && !postToElement)
773         object = object->observableObject();
774 
775     if (!object && document)
776         object = get(document->renderView());
777 
778     if (!object)
779         return;
780 
781     if (postType == PostAsynchronously) {
782         m_notificationsToPost.append(std::make_pair(object, notification));
783         if (!m_notificationPostTimer.isActive())
784             m_notificationPostTimer.startOneShot(0, FROM_HERE);
785     } else {
786         postPlatformNotification(object, notification);
787     }
788 }
789 
checkedStateChanged(Node * node)790 void AXObjectCache::checkedStateChanged(Node* node)
791 {
792     postNotification(node, AXObjectCache::AXCheckedStateChanged, true);
793 }
794 
selectedChildrenChanged(Node * node)795 void AXObjectCache::selectedChildrenChanged(Node* node)
796 {
797     // postToElement is false so that you can pass in any child of an element and it will go up the parent tree
798     // to find the container which should send out the notification.
799     postNotification(node, AXSelectedChildrenChanged, false);
800 }
801 
selectedChildrenChanged(RenderObject * renderer)802 void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
803 {
804     // postToElement is false so that you can pass in any child of an element and it will go up the parent tree
805     // to find the container which should send out the notification.
806     postNotification(renderer, AXSelectedChildrenChanged, false);
807 }
808 
handleScrollbarUpdate(ScrollView * view)809 void AXObjectCache::handleScrollbarUpdate(ScrollView* view)
810 {
811     if (!view)
812         return;
813 
814     // We don't want to create a scroll view from this method, only update an existing one.
815     if (AXObject* scrollViewObject = get(view)) {
816         m_computedObjectAttributeCache->clear();
817         scrollViewObject->updateChildrenIfNecessary();
818     }
819 }
820 
handleLayoutComplete(RenderObject * renderer)821 void AXObjectCache::handleLayoutComplete(RenderObject* renderer)
822 {
823     if (!renderer)
824         return;
825 
826     m_computedObjectAttributeCache->clear();
827 
828     // Create the AXObject if it didn't yet exist - that's always safe at the end of a layout, and it
829     // allows an AX notification to be sent when a page has its first layout, rather than when the
830     // document first loads.
831     if (AXObject* obj = getOrCreate(renderer))
832         postNotification(obj, obj->document(), AXLayoutComplete, true);
833 }
834 
handleAriaExpandedChange(Node * node)835 void AXObjectCache::handleAriaExpandedChange(Node* node)
836 {
837     if (AXObject* obj = getOrCreate(node))
838         obj->handleAriaExpandedChanged();
839 }
840 
handleActiveDescendantChanged(Node * node)841 void AXObjectCache::handleActiveDescendantChanged(Node* node)
842 {
843     if (AXObject* obj = getOrCreate(node))
844         obj->handleActiveDescendantChanged();
845 }
846 
handleAriaRoleChanged(Node * node)847 void AXObjectCache::handleAriaRoleChanged(Node* node)
848 {
849     if (AXObject* obj = getOrCreate(node)) {
850         obj->updateAccessibilityRole();
851         m_computedObjectAttributeCache->clear();
852         obj->notifyIfIgnoredValueChanged();
853     }
854 }
855 
handleAttributeChanged(const QualifiedName & attrName,Element * element)856 void AXObjectCache::handleAttributeChanged(const QualifiedName& attrName, Element* element)
857 {
858     if (attrName == roleAttr)
859         handleAriaRoleChanged(element);
860     else if (attrName == altAttr || attrName == titleAttr)
861         textChanged(element);
862     else if (attrName == forAttr && isHTMLLabelElement(*element))
863         labelChanged(element);
864 
865     if (!attrName.localName().startsWith("aria-"))
866         return;
867 
868     if (attrName == aria_activedescendantAttr)
869         handleActiveDescendantChanged(element);
870     else if (attrName == aria_valuenowAttr || attrName == aria_valuetextAttr)
871         postNotification(element, AXObjectCache::AXValueChanged, true);
872     else if (attrName == aria_labelAttr || attrName == aria_labeledbyAttr || attrName == aria_labelledbyAttr)
873         textChanged(element);
874     else if (attrName == aria_checkedAttr)
875         checkedStateChanged(element);
876     else if (attrName == aria_selectedAttr)
877         selectedChildrenChanged(element);
878     else if (attrName == aria_expandedAttr)
879         handleAriaExpandedChange(element);
880     else if (attrName == aria_hiddenAttr)
881         childrenChanged(element->parentNode());
882     else if (attrName == aria_invalidAttr)
883         postNotification(element, AXObjectCache::AXInvalidStatusChanged, true);
884     else
885         postNotification(element, AXObjectCache::AXAriaAttributeChanged, true);
886 }
887 
labelChanged(Element * element)888 void AXObjectCache::labelChanged(Element* element)
889 {
890     textChanged(toHTMLLabelElement(element)->control());
891 }
892 
recomputeIsIgnored(RenderObject * renderer)893 void AXObjectCache::recomputeIsIgnored(RenderObject* renderer)
894 {
895     if (AXObject* obj = get(renderer))
896         obj->notifyIfIgnoredValueChanged();
897 }
898 
inlineTextBoxesUpdated(RenderObject * renderer)899 void AXObjectCache::inlineTextBoxesUpdated(RenderObject* renderer)
900 {
901     if (!inlineTextBoxAccessibilityEnabled())
902         return;
903 
904     // Only update if the accessibility object already exists and it's
905     // not already marked as dirty.
906     if (AXObject* obj = get(renderer)) {
907         if (!obj->needsToUpdateChildren()) {
908             obj->setNeedsToUpdateChildren();
909             postNotification(renderer, AXChildrenChanged, true);
910         }
911     }
912 }
913 
settings()914 Settings* AXObjectCache::settings()
915 {
916     return m_document.settings();
917 }
918 
accessibilityEnabled()919 bool AXObjectCache::accessibilityEnabled()
920 {
921     Settings* settings = this->settings();
922     if (!settings)
923         return false;
924     return settings->accessibilityEnabled();
925 }
926 
inlineTextBoxAccessibilityEnabled()927 bool AXObjectCache::inlineTextBoxAccessibilityEnabled()
928 {
929     Settings* settings = this->settings();
930     if (!settings)
931         return false;
932     return settings->inlineTextBoxAccessibilityEnabled();
933 }
934 
rootAXEditableElement(const Node * node)935 const Element* AXObjectCache::rootAXEditableElement(const Node* node)
936 {
937     const Element* result = node->rootEditableElement();
938     const Element* element = node->isElementNode() ? toElement(node) : node->parentElement();
939 
940     for (; element; element = element->parentElement()) {
941         if (nodeIsTextControl(element))
942             result = element;
943     }
944 
945     return result;
946 }
947 
nodeIsTextControl(const Node * node)948 bool AXObjectCache::nodeIsTextControl(const Node* node)
949 {
950     if (!node)
951         return false;
952 
953     const AXObject* axObject = getOrCreate(const_cast<Node*>(node));
954     return axObject && axObject->isTextControl();
955 }
956 
isNodeAriaVisible(Node * node)957 bool isNodeAriaVisible(Node* node)
958 {
959     if (!node)
960         return false;
961 
962     if (!node->isElementNode())
963         return false;
964 
965     return equalIgnoringCase(toElement(node)->getAttribute(aria_hiddenAttr), "false");
966 }
967 
detachWrapper(AXObject * obj)968 void AXObjectCache::detachWrapper(AXObject* obj)
969 {
970     // In Chromium, AXObjects are not wrapped.
971 }
972 
attachWrapper(AXObject *)973 void AXObjectCache::attachWrapper(AXObject*)
974 {
975     // In Chromium, AXObjects are not wrapped.
976 }
977 
postPlatformNotification(AXObject * obj,AXNotification notification)978 void AXObjectCache::postPlatformNotification(AXObject* obj, AXNotification notification)
979 {
980     if (obj && obj->isAXScrollbar() && notification == AXValueChanged) {
981         // Send document value changed on scrollbar value changed notification.
982         Scrollbar* scrollBar = toAXScrollbar(obj)->scrollbar();
983         if (!scrollBar || !scrollBar->parent() || !scrollBar->parent()->isFrameView())
984             return;
985         Document* document = toFrameView(scrollBar->parent())->frame().document();
986         if (document != document->topDocument())
987             return;
988         obj = get(document->renderView());
989     }
990 
991     if (!obj || !obj->document() || !obj->documentFrameView() || !obj->documentFrameView()->frame().page())
992         return;
993 
994     ChromeClient& client = obj->document()->axObjectCacheOwner().page()->chrome().client();
995 
996     if (notification == AXActiveDescendantChanged
997         && obj->document()->focusedElement()
998         && obj->node() == obj->document()->focusedElement()) {
999         // Calling handleFocusedUIElementChanged will focus the new active
1000         // descendant and send the AXFocusedUIElementChanged notification.
1001         handleFocusedUIElementChanged(0, obj->document()->focusedElement());
1002     }
1003 
1004     client.postAccessibilityNotification(obj, notification);
1005 }
1006 
handleFocusedUIElementChanged(Node *,Node * newFocusedNode)1007 void AXObjectCache::handleFocusedUIElementChanged(Node*, Node* newFocusedNode)
1008 {
1009     if (!newFocusedNode)
1010         return;
1011 
1012     Page* page = newFocusedNode->document().page();
1013     if (!page)
1014         return;
1015 
1016     AXObject* focusedObject = focusedUIElementForPage(page);
1017     if (!focusedObject)
1018         return;
1019 
1020     postPlatformNotification(focusedObject, AXFocusedUIElementChanged);
1021 }
1022 
handleScrolledToAnchor(const Node * anchorNode)1023 void AXObjectCache::handleScrolledToAnchor(const Node* anchorNode)
1024 {
1025     // The anchor node may not be accessible. Post the notification for the
1026     // first accessible object.
1027     postPlatformNotification(AXObject::firstAccessibleObjectFromNode(anchorNode), AXScrolledToAnchor);
1028 }
1029 
handleScrollPositionChanged(ScrollView * scrollView)1030 void AXObjectCache::handleScrollPositionChanged(ScrollView* scrollView)
1031 {
1032     postPlatformNotification(getOrCreate(scrollView), AXScrollPositionChanged);
1033 }
1034 
handleScrollPositionChanged(RenderObject * renderObject)1035 void AXObjectCache::handleScrollPositionChanged(RenderObject* renderObject)
1036 {
1037     postPlatformNotification(getOrCreate(renderObject), AXScrollPositionChanged);
1038 }
1039 
setCanvasObjectBounds(Element * element,const LayoutRect & rect)1040 void AXObjectCache::setCanvasObjectBounds(Element* element, const LayoutRect& rect)
1041 {
1042     AXObject* obj = getOrCreate(element);
1043     if (!obj)
1044         return;
1045 
1046     obj->setElementRect(rect);
1047 }
1048 
1049 } // namespace blink
1050