2 * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
31 #include "core/accessibility/AXObjectCache.h"
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"
83 using namespace HTMLNames;
85 AXObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID id) const
87 HashMap<AXID, CachedAXObjectAttributes>::const_iterator it = m_idMapping.find(id);
88 return it != m_idMapping.end() ? it->value.ignored : DefaultBehavior;
91 void AXComputedObjectAttributeCache::setIgnored(AXID id, AXObjectInclusion inclusion)
93 HashMap<AXID, CachedAXObjectAttributes>::iterator it = m_idMapping.find(id);
94 if (it != m_idMapping.end()) {
95 it->value.ignored = inclusion;
97 CachedAXObjectAttributes attributes;
98 attributes.ignored = inclusion;
99 m_idMapping.set(id, attributes);
103 void AXComputedObjectAttributeCache::clear()
108 AXObjectCache::AXObjectCache(Document& document)
109 : m_document(document)
110 , m_notificationPostTimer(this, &AXObjectCache::notificationPostTimerFired)
112 m_computedObjectAttributeCache = AXComputedObjectAttributeCache::create();
115 AXObjectCache::~AXObjectCache()
117 m_notificationPostTimer.stop();
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();
128 AXObject* AXObjectCache::focusedImageMapUIElement(HTMLAreaElement* areaElement)
130 // Find the corresponding accessibility object for the HTMLAreaElement. This should be
131 // in the list of children for its corresponding image.
135 HTMLImageElement* imageElement = areaElement->imageElement();
139 AXObject* axRenderImage = areaElement->document().axObjectCache()->getOrCreate(imageElement);
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())
150 if (toAXImageMapLink(child)->areaElement() == areaElement)
157 AXObject* AXObjectCache::focusedUIElementForPage(const Page* page)
159 if (!page->settings().accessibilityEnabled())
162 // Cross-process accessibility is not yet implemented.
163 if (!page->focusController().focusedOrMainFrame()->isLocalFrame())
166 // get the focused node in the page
167 Document* focusedDocument = toLocalFrame(page->focusController().focusedOrMainFrame())->document();
168 Node* focusedNode = focusedDocument->focusedElement();
170 focusedNode = focusedDocument;
172 if (isHTMLAreaElement(*focusedNode))
173 return focusedImageMapUIElement(toHTMLAreaElement(focusedNode));
175 AXObject* obj = focusedNode->document().axObjectCache()->getOrCreate(focusedNode);
179 if (obj->shouldFocusActiveDescendant()) {
180 if (AXObject* descendant = obj->activeDescendant())
184 // the HTML element, for example, is focusable but has an AX object that is ignored
185 if (obj->accessibilityIsIgnored())
186 obj = obj->parentObjectUnignored();
191 AXObject* AXObjectCache::get(Widget* widget)
196 AXID axID = m_widgetObjectMapping.get(widget);
197 ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
201 return m_objects.get(axID);
204 AXObject* AXObjectCache::get(RenderObject* renderer)
209 AXID axID = m_renderObjectMapping.get(renderer);
210 ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
214 return m_objects.get(axID);
217 AXObject* AXObjectCache::get(Node* node)
222 AXID renderID = node->renderer() ? m_renderObjectMapping.get(node->renderer()) : 0;
223 ASSERT(!HashTraits<AXID>::isDeletedValue(renderID));
225 AXID nodeID = m_nodeObjectMapping.get(node);
226 ASSERT(!HashTraits<AXID>::isDeletedValue(nodeID));
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
237 return m_objects.get(renderID);
242 return m_objects.get(nodeID);
245 AXObject* AXObjectCache::get(AbstractInlineTextBox* inlineTextBox)
250 AXID axID = m_inlineTextBoxObjectMapping.get(inlineTextBox);
251 ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
255 return m_objects.get(axID);
258 // FIXME: This probably belongs on Node.
259 // FIXME: This should take a const char*, but one caller passes nullAtom.
260 bool nodeHasRole(Node* node, const String& role)
262 if (!node || !node->isElementNode())
265 return equalIgnoringCase(toElement(node)->getAttribute(roleAttr), role);
268 static PassRefPtr<AXObject> createFromRenderer(RenderObject* renderer)
270 // FIXME: How could renderer->node() ever not be an Element?
271 Node* node = renderer->node();
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);
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);
288 if (node && node->isMediaControlElement())
289 return AccessibilityMediaControl::create(renderer);
291 if (isHTMLOptionElement(node))
292 return AXListBoxOption::create(renderer);
294 if (renderer->isSVGRoot())
295 return AXSVGRoot::create(renderer);
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));
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));
313 if (cssBox->isProgress())
314 return AXProgressIndicator::create(toRenderProgress(cssBox));
317 if (cssBox->isSlider())
318 return AXSlider::create(toRenderSlider(cssBox));
321 return AXRenderObject::create(renderer);
324 static PassRefPtr<AXObject> createFromNode(Node* node)
326 return AXNodeObject::create(node);
329 static PassRefPtr<AXObject> createFromInlineTextBox(AbstractInlineTextBox* inlineTextBox)
331 return AXInlineTextBox::create(inlineTextBox);
334 AXObject* AXObjectCache::getOrCreate(Widget* widget)
339 if (AXObject* obj = get(widget))
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));
348 // Will crash later if we have two objects for the same widget.
349 ASSERT(!get(widget));
351 // Catch the case if an (unsupported) widget type is used. Only FrameView and ScrollBar are supported now.
356 getAXID(newObj.get());
358 m_widgetObjectMapping.set(widget, newObj->axObjectID());
359 m_objects.set(newObj->axObjectID(), newObj);
361 attachWrapper(newObj.get());
365 AXObject* AXObjectCache::getOrCreate(Node* node)
370 if (AXObject* obj = get(node))
373 if (node->renderer())
374 return getOrCreate(node->renderer());
376 if (!node->parentElement())
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)
386 RefPtr<AXObject> newObj = createFromNode(node);
388 // Will crash later if we have two objects for the same node.
391 getAXID(newObj.get());
393 m_nodeObjectMapping.set(node, newObj->axObjectID());
394 m_objects.set(newObj->axObjectID(), newObj);
396 attachWrapper(newObj.get());
397 newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
402 AXObject* AXObjectCache::getOrCreate(RenderObject* renderer)
407 if (AXObject* obj = get(renderer))
410 RefPtr<AXObject> newObj = createFromRenderer(renderer);
412 // Will crash later if we have two objects for the same renderer.
413 ASSERT(!get(renderer));
415 getAXID(newObj.get());
417 m_renderObjectMapping.set(renderer, newObj->axObjectID());
418 m_objects.set(newObj->axObjectID(), newObj);
420 attachWrapper(newObj.get());
421 newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
426 AXObject* AXObjectCache::getOrCreate(AbstractInlineTextBox* inlineTextBox)
431 if (AXObject* obj = get(inlineTextBox))
434 RefPtr<AXObject> newObj = createFromInlineTextBox(inlineTextBox);
436 // Will crash later if we have two objects for the same inlineTextBox.
437 ASSERT(!get(inlineTextBox));
439 getAXID(newObj.get());
441 m_inlineTextBoxObjectMapping.set(inlineTextBox, newObj->axObjectID());
442 m_objects.set(newObj->axObjectID(), newObj);
444 attachWrapper(newObj.get());
445 newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
450 AXObject* AXObjectCache::rootObject()
452 if (!accessibilityEnabled())
455 return getOrCreate(m_document.view());
458 AXObject* AXObjectCache::getOrCreate(AccessibilityRole role)
460 RefPtr<AXObject> obj = nullptr;
462 // will be filled in...
464 case ImageMapLinkRole:
465 obj = AXImageMapLink::create();
468 obj = AXTableColumn::create();
470 case TableHeaderContainerRole:
471 obj = AXTableHeaderContainer::create();
473 case SliderThumbRole:
474 obj = AXSliderThumb::create();
476 case MenuListPopupRole:
477 obj = AXMenuListPopup::create();
479 case MenuListOptionRole:
480 obj = AXMenuListOption::create();
483 obj = AXSpinButton::create();
485 case SpinButtonPartRole:
486 obj = AXSpinButtonPart::create();
497 m_objects.set(obj->axObjectID(), obj);
499 attachWrapper(obj.get());
503 void AXObjectCache::remove(AXID axID)
508 // first fetch object to operate some cleanup functions on it
509 AXObject* obj = m_objects.get(axID);
517 // finally remove the object
518 if (!m_objects.take(axID))
521 ASSERT(m_objects.size() >= m_idsInUse.size());
524 void AXObjectCache::remove(RenderObject* renderer)
529 AXID axID = m_renderObjectMapping.get(renderer);
531 m_renderObjectMapping.remove(renderer);
534 void AXObjectCache::remove(Node* node)
539 removeNodeForUse(node);
541 // This is all safe even if we didn't have a mapping.
542 AXID axID = m_nodeObjectMapping.get(node);
544 m_nodeObjectMapping.remove(node);
546 if (node->renderer()) {
547 remove(node->renderer());
552 void AXObjectCache::remove(Widget* view)
557 AXID axID = m_widgetObjectMapping.get(view);
559 m_widgetObjectMapping.remove(view);
562 void AXObjectCache::remove(AbstractInlineTextBox* inlineTextBox)
567 AXID axID = m_inlineTextBoxObjectMapping.get(inlineTextBox);
569 m_inlineTextBoxObjectMapping.remove(inlineTextBox);
572 // FIXME: Oilpan: Use a weak hashmap for this instead.
573 void AXObjectCache::clearWeakMembers(Visitor* visitor)
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);
580 for (unsigned i = 0; i < deadNodes.size(); ++i)
581 remove(deadNodes[i]);
584 AXID AXObjectCache::platformGenerateAXID() const
586 static AXID lastUsedID = 0;
588 // Generate a new ID.
589 AXID objID = lastUsedID;
592 } while (!objID || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
599 AXID AXObjectCache::getAXID(AXObject* obj)
601 // check for already-assigned ID
602 AXID objID = obj->axObjectID();
604 ASSERT(m_idsInUse.contains(objID));
608 objID = platformGenerateAXID();
610 m_idsInUse.add(objID);
611 obj->setAXObjectID(objID);
616 void AXObjectCache::removeAXID(AXObject* object)
621 AXID objID = object->axObjectID();
624 ASSERT(!HashTraits<AXID>::isDeletedValue(objID));
625 ASSERT(m_idsInUse.contains(objID));
626 object->setAXObjectID(0);
627 m_idsInUse.remove(objID);
630 void AXObjectCache::selectionChanged(Node* node)
632 // Find the nearest ancestor that already has an accessibility object, since we
633 // might be in the middle of a layout.
635 if (AXObject* obj = get(node)) {
636 obj->selectionChanged();
639 node = node->parentNode();
643 void AXObjectCache::textChanged(Node* node)
645 textChanged(getOrCreate(node));
648 void AXObjectCache::textChanged(RenderObject* renderer)
650 textChanged(getOrCreate(renderer));
653 void AXObjectCache::textChanged(AXObject* obj)
658 bool parentAlreadyExists = obj->parentObjectIfExists();
660 postNotification(obj, obj->document(), AXObjectCache::AXTextChanged, true);
661 if (parentAlreadyExists)
662 obj->notifyIfIgnoredValueChanged();
665 void AXObjectCache::updateCacheAfterNodeIsAttached(Node* node)
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.
672 void AXObjectCache::childrenChanged(Node* node)
674 childrenChanged(get(node));
677 void AXObjectCache::childrenChanged(RenderObject* renderer)
679 childrenChanged(get(renderer));
682 void AXObjectCache::childrenChanged(AXObject* obj)
687 obj->childrenChanged();
690 void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*)
692 RefPtrWillBeRawPtr<Document> protectorForCacheOwner(m_document);
694 m_notificationPostTimer.stop();
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())
702 if (!obj->axObjectCache())
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());
716 AXNotification notification = m_notificationsToPost[i].second;
717 postPlatformNotification(obj, notification);
719 if (notification == AXChildrenChanged && obj->parentObjectIfExists() && obj->lastKnownIsIgnoredValue() != obj->accessibilityIsIgnored())
720 childrenChanged(obj->parentObject());
723 m_notificationsToPost.clear();
726 void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, bool postToElement, PostType postType)
731 m_computedObjectAttributeCache->clear();
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);
744 postNotification(object.get(), &renderer->document(), notification, postToElement, postType);
747 void AXObjectCache::postNotification(Node* node, AXNotification notification, bool postToElement, PostType postType)
752 m_computedObjectAttributeCache->clear();
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();
765 postNotification(object.get(), &node->document(), notification, postToElement, postType);
768 void AXObjectCache::postNotification(AXObject* object, Document* document, AXNotification notification, bool postToElement, PostType postType)
770 m_computedObjectAttributeCache->clear();
772 if (object && !postToElement)
773 object = object->observableObject();
775 if (!object && document)
776 object = get(document->renderView());
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);
786 postPlatformNotification(object, notification);
790 void AXObjectCache::checkedStateChanged(Node* node)
792 postNotification(node, AXObjectCache::AXCheckedStateChanged, true);
795 void AXObjectCache::selectedChildrenChanged(Node* node)
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);
802 void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
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);
809 void AXObjectCache::handleScrollbarUpdate(ScrollView* view)
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();
821 void AXObjectCache::handleLayoutComplete(RenderObject* renderer)
826 m_computedObjectAttributeCache->clear();
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);
835 void AXObjectCache::handleAriaExpandedChange(Node* node)
837 if (AXObject* obj = getOrCreate(node))
838 obj->handleAriaExpandedChanged();
841 void AXObjectCache::handleActiveDescendantChanged(Node* node)
843 if (AXObject* obj = getOrCreate(node))
844 obj->handleActiveDescendantChanged();
847 void AXObjectCache::handleAriaRoleChanged(Node* node)
849 if (AXObject* obj = getOrCreate(node)) {
850 obj->updateAccessibilityRole();
851 m_computedObjectAttributeCache->clear();
852 obj->notifyIfIgnoredValueChanged();
856 void AXObjectCache::handleAttributeChanged(const QualifiedName& attrName, Element* element)
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);
865 if (!attrName.localName().startsWith("aria-"))
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);
885 postNotification(element, AXObjectCache::AXAriaAttributeChanged, true);
888 void AXObjectCache::labelChanged(Element* element)
890 textChanged(toHTMLLabelElement(element)->control());
893 void AXObjectCache::recomputeIsIgnored(RenderObject* renderer)
895 if (AXObject* obj = get(renderer))
896 obj->notifyIfIgnoredValueChanged();
899 void AXObjectCache::inlineTextBoxesUpdated(RenderObject* renderer)
901 if (!inlineTextBoxAccessibilityEnabled())
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);
914 Settings* AXObjectCache::settings()
916 return m_document.settings();
919 bool AXObjectCache::accessibilityEnabled()
921 Settings* settings = this->settings();
924 return settings->accessibilityEnabled();
927 bool AXObjectCache::inlineTextBoxAccessibilityEnabled()
929 Settings* settings = this->settings();
932 return settings->inlineTextBoxAccessibilityEnabled();
935 const Element* AXObjectCache::rootAXEditableElement(const Node* node)
937 const Element* result = node->rootEditableElement();
938 const Element* element = node->isElementNode() ? toElement(node) : node->parentElement();
940 for (; element; element = element->parentElement()) {
941 if (nodeIsTextControl(element))
948 bool AXObjectCache::nodeIsTextControl(const Node* node)
953 const AXObject* axObject = getOrCreate(const_cast<Node*>(node));
954 return axObject && axObject->isTextControl();
957 bool isNodeAriaVisible(Node* node)
962 if (!node->isElementNode())
965 return equalIgnoringCase(toElement(node)->getAttribute(aria_hiddenAttr), "false");
968 void AXObjectCache::detachWrapper(AXObject* obj)
970 // In Chromium, AXObjects are not wrapped.
973 void AXObjectCache::attachWrapper(AXObject*)
975 // In Chromium, AXObjects are not wrapped.
978 void AXObjectCache::postPlatformNotification(AXObject* obj, AXNotification notification)
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())
985 Document* document = toFrameView(scrollBar->parent())->frame().document();
986 if (document != document->topDocument())
988 obj = get(document->renderView());
991 if (!obj || !obj->document() || !obj->documentFrameView() || !obj->documentFrameView()->frame().page())
994 ChromeClient& client = obj->document()->axObjectCacheOwner().page()->chrome().client();
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());
1004 client.postAccessibilityNotification(obj, notification);
1007 void AXObjectCache::handleFocusedUIElementChanged(Node*, Node* newFocusedNode)
1009 if (!newFocusedNode)
1012 Page* page = newFocusedNode->document().page();
1016 AXObject* focusedObject = focusedUIElementForPage(page);
1020 postPlatformNotification(focusedObject, AXFocusedUIElementChanged);
1023 void AXObjectCache::handleScrolledToAnchor(const Node* anchorNode)
1025 // The anchor node may not be accessible. Post the notification for the
1026 // first accessible object.
1027 postPlatformNotification(AXObject::firstAccessibleObjectFromNode(anchorNode), AXScrolledToAnchor);
1030 void AXObjectCache::handleScrollPositionChanged(ScrollView* scrollView)
1032 postPlatformNotification(getOrCreate(scrollView), AXScrollPositionChanged);
1035 void AXObjectCache::handleScrollPositionChanged(RenderObject* renderObject)
1037 postPlatformNotification(getOrCreate(renderObject), AXScrollPositionChanged);
1040 void AXObjectCache::setCanvasObjectBounds(Element* element, const LayoutRect& rect)
1042 AXObject* obj = getOrCreate(element);
1046 obj->setElementRect(rect);
1049 } // namespace blink