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 #if HAVE(ACCESSIBILITY)
33 #include "AXObjectCache.h"
35 #include "AccessibilityARIAGrid.h"
36 #include "AccessibilityARIAGridCell.h"
37 #include "AccessibilityARIAGridRow.h"
38 #include "AccessibilityImageMapLink.h"
39 #include "AccessibilityList.h"
40 #include "AccessibilityListBox.h"
41 #include "AccessibilityListBoxOption.h"
42 #include "AccessibilityMediaControls.h"
43 #include "AccessibilityMenuList.h"
44 #include "AccessibilityMenuListOption.h"
45 #include "AccessibilityMenuListPopup.h"
46 #include "AccessibilityProgressIndicator.h"
47 #include "AccessibilityRenderObject.h"
48 #include "AccessibilitySVGRoot.h"
49 #include "AccessibilityScrollView.h"
50 #include "AccessibilityScrollbar.h"
51 #include "AccessibilitySlider.h"
52 #include "AccessibilitySpinButton.h"
53 #include "AccessibilityTable.h"
54 #include "AccessibilityTableCell.h"
55 #include "AccessibilityTableColumn.h"
56 #include "AccessibilityTableHeaderContainer.h"
57 #include "AccessibilityTableRow.h"
60 #include "FocusController.h"
62 #include "HTMLAreaElement.h"
63 #include "HTMLImageElement.h"
64 #include "HTMLInputElement.h"
65 #include "HTMLLabelElement.h"
66 #include "HTMLMeterElement.h"
67 #include "HTMLNames.h"
69 #include "RenderListBox.h"
70 #include "RenderMenuList.h"
71 #include "RenderMeter.h"
72 #include "RenderProgress.h"
73 #include "RenderSlider.h"
74 #include "RenderTable.h"
75 #include "RenderTableCell.h"
76 #include "RenderTableRow.h"
77 #include "RenderView.h"
78 #include "ScrollView.h"
79 #include <wtf/PassRefPtr.h>
82 #include "MediaControlElements.h"
87 using namespace HTMLNames;
89 AccessibilityObjectInclusion AXComputedObjectAttributeCache::getIgnored(AXID id) const
91 HashMap<AXID, CachedAXObjectAttributes>::const_iterator it = m_idMapping.find(id);
92 return it != m_idMapping.end() ? it->value.ignored : DefaultBehavior;
95 void AXComputedObjectAttributeCache::setIgnored(AXID id, AccessibilityObjectInclusion inclusion)
97 HashMap<AXID, CachedAXObjectAttributes>::iterator it = m_idMapping.find(id);
98 if (it != m_idMapping.end())
99 it->value.ignored = inclusion;
101 CachedAXObjectAttributes attributes;
102 attributes.ignored = inclusion;
103 m_idMapping.set(id, attributes);
107 bool AXObjectCache::gAccessibilityEnabled = false;
108 bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false;
110 AXObjectCache::AXObjectCache(const Document* doc)
111 : m_notificationPostTimer(this, &AXObjectCache::notificationPostTimerFired)
113 m_document = const_cast<Document*>(doc);
116 AXObjectCache::~AXObjectCache()
118 m_notificationPostTimer.stop();
120 HashMap<AXID, RefPtr<AccessibilityObject> >::iterator end = m_objects.end();
121 for (HashMap<AXID, RefPtr<AccessibilityObject> >::iterator it = m_objects.begin(); it != end; ++it) {
122 AccessibilityObject* obj = (*it).value.get();
129 AccessibilityObject* AXObjectCache::focusedImageMapUIElement(HTMLAreaElement* areaElement)
131 // Find the corresponding accessibility object for the HTMLAreaElement. This should be
132 // in the list of children for its corresponding image.
136 HTMLImageElement* imageElement = areaElement->imageElement();
140 AccessibilityObject* axRenderImage = areaElement->document()->axObjectCache()->getOrCreate(imageElement);
144 AccessibilityObject::AccessibilityChildrenVector imageChildren = axRenderImage->children();
145 unsigned count = imageChildren.size();
146 for (unsigned k = 0; k < count; ++k) {
147 AccessibilityObject* child = imageChildren[k].get();
148 if (!child->isImageMapLink())
151 if (static_cast<AccessibilityImageMapLink*>(child)->areaElement() == areaElement)
158 AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page)
160 if (!gAccessibilityEnabled)
163 // get the focused node in the page
164 Document* focusedDocument = page->focusController().focusedOrMainFrame().document();
165 Element* focusedElement = focusedDocument->focusedElement();
166 if (focusedElement && isHTMLAreaElement(focusedElement))
167 return focusedImageMapUIElement(toHTMLAreaElement(focusedElement));
169 AccessibilityObject* obj = focusedDocument->axObjectCache()->getOrCreate(focusedElement ? static_cast<Node*>(focusedElement) : focusedDocument);
173 if (obj->shouldFocusActiveDescendant()) {
174 if (AccessibilityObject* descendant = obj->activeDescendant())
178 // the HTML element, for example, is focusable but has an AX object that is ignored
179 if (obj->accessibilityIsIgnored())
180 obj = obj->parentObjectUnignored();
185 AccessibilityObject* AXObjectCache::get(Widget* widget)
190 AXID axID = m_widgetObjectMapping.get(widget);
191 ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
195 return m_objects.get(axID);
198 AccessibilityObject* AXObjectCache::get(RenderObject* renderer)
203 AXID axID = m_renderObjectMapping.get(renderer);
204 ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
208 return m_objects.get(axID);
211 AccessibilityObject* AXObjectCache::get(Node* node)
216 AXID renderID = node->renderer() ? m_renderObjectMapping.get(node->renderer()) : 0;
217 ASSERT(!HashTraits<AXID>::isDeletedValue(renderID));
219 AXID nodeID = m_nodeObjectMapping.get(node);
220 ASSERT(!HashTraits<AXID>::isDeletedValue(nodeID));
222 if (node->renderer() && nodeID && !renderID) {
223 // This can happen if an AccessibilityNodeObject is created for a node that's not
224 // rendered, but later something changes and it gets a renderer (like if it's
231 return m_objects.get(renderID);
236 return m_objects.get(nodeID);
239 // FIXME: This probably belongs on Node.
240 // FIXME: This should take a const char*, but one caller passes nullAtom.
241 bool nodeHasRole(Node* node, const String& role)
243 if (!node || !node->isElementNode())
246 return equalIgnoringCase(toElement(node)->getAttribute(roleAttr), role);
249 static PassRefPtr<AccessibilityObject> createFromRenderer(RenderObject* renderer)
251 // FIXME: How could renderer->node() ever not be an Element?
252 Node* node = renderer->node();
254 // If the node is aria role="list" or the aria role is empty and its a
255 // ul/ol/dl type (it shouldn't be a list if aria says otherwise).
256 if (node && ((nodeHasRole(node, "list") || nodeHasRole(node, "directory"))
257 || (nodeHasRole(node, nullAtom) && (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(dlTag)))))
258 return AccessibilityList::create(renderer);
261 if (nodeHasRole(node, "grid") || nodeHasRole(node, "treegrid"))
262 return AccessibilityARIAGrid::create(renderer);
263 if (nodeHasRole(node, "row"))
264 return AccessibilityARIAGridRow::create(renderer);
265 if (nodeHasRole(node, "gridcell") || nodeHasRole(node, "columnheader") || nodeHasRole(node, "rowheader"))
266 return AccessibilityARIAGridCell::create(renderer);
270 if (node && node->isMediaControlElement())
271 return AccessibilityMediaControl::create(renderer);
275 if (renderer->isSVGRoot())
276 return AccessibilitySVGRoot::create(renderer);
279 if (renderer->isBoxModelObject()) {
280 RenderBoxModelObject* cssBox = toRenderBoxModelObject(renderer);
281 if (cssBox->isListBox())
282 return AccessibilityListBox::create(toRenderListBox(cssBox));
283 if (cssBox->isMenuList())
284 return AccessibilityMenuList::create(toRenderMenuList(cssBox));
287 if (cssBox->isTable())
288 return AccessibilityTable::create(toRenderTable(cssBox));
289 if (cssBox->isTableRow())
290 return AccessibilityTableRow::create(toRenderTableRow(cssBox));
291 if (cssBox->isTableCell())
292 return AccessibilityTableCell::create(toRenderTableCell(cssBox));
294 #if ENABLE(PROGRESS_ELEMENT)
296 if (cssBox->isProgress())
297 return AccessibilityProgressIndicator::create(toRenderProgress(cssBox));
299 #if ENABLE(METER_ELEMENT)
300 if (cssBox->isMeter())
301 return AccessibilityProgressIndicator::create(toRenderMeter(cssBox));
305 if (cssBox->isSlider())
306 return AccessibilitySlider::create(toRenderSlider(cssBox));
309 return AccessibilityRenderObject::create(renderer);
312 static PassRefPtr<AccessibilityObject> createFromNode(Node* node)
314 return AccessibilityNodeObject::create(node);
317 AccessibilityObject* AXObjectCache::getOrCreate(Widget* widget)
322 if (AccessibilityObject* obj = get(widget))
325 RefPtr<AccessibilityObject> newObj = 0;
326 if (widget->isFrameView())
327 newObj = AccessibilityScrollView::create(static_cast<ScrollView*>(widget));
328 else if (widget->isScrollbar())
329 newObj = AccessibilityScrollbar::create(static_cast<Scrollbar*>(widget));
331 // Will crash later if we have two objects for the same widget.
332 ASSERT(!get(widget));
334 // Catch the case if an (unsupported) widget type is used. Only FrameView and ScrollBar are supported now.
339 getAXID(newObj.get());
341 m_widgetObjectMapping.set(widget, newObj->axObjectID());
342 m_objects.set(newObj->axObjectID(), newObj);
344 attachWrapper(newObj.get());
348 AccessibilityObject* AXObjectCache::getOrCreate(Node* node)
353 if (AccessibilityObject* obj = get(node))
356 if (node->renderer())
357 return getOrCreate(node->renderer());
359 if (!node->parentElement())
362 // It's only allowed to create an AccessibilityObject from a Node if it's in a canvas subtree.
363 // Or if it's a hidden element, but we still want to expose it because of other ARIA attributes.
364 bool inCanvasSubtree = node->parentElement()->isInCanvasSubtree();
365 bool isHidden = !node->renderer() && isNodeAriaVisible(node);
367 bool insideMeterElement = false;
368 #if ENABLE(METER_ELEMENT)
369 insideMeterElement = isHTMLMeterElement(node->parentElement());
372 if (!inCanvasSubtree && !isHidden && !insideMeterElement)
375 RefPtr<AccessibilityObject> newObj = createFromNode(node);
377 // Will crash later if we have two objects for the same node.
380 getAXID(newObj.get());
382 m_nodeObjectMapping.set(node, newObj->axObjectID());
383 m_objects.set(newObj->axObjectID(), newObj);
385 attachWrapper(newObj.get());
386 newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
391 AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
396 if (AccessibilityObject* obj = get(renderer))
399 RefPtr<AccessibilityObject> newObj = createFromRenderer(renderer);
401 // Will crash later if we have two objects for the same renderer.
402 ASSERT(!get(renderer));
404 getAXID(newObj.get());
406 m_renderObjectMapping.set(renderer, newObj->axObjectID());
407 m_objects.set(newObj->axObjectID(), newObj);
409 attachWrapper(newObj.get());
410 newObj->setLastKnownIsIgnoredValue(newObj->accessibilityIsIgnored());
415 AccessibilityObject* AXObjectCache::rootObject()
417 if (!gAccessibilityEnabled)
420 return getOrCreate(m_document->view());
423 AccessibilityObject* AXObjectCache::rootObjectForFrame(Frame* frame)
425 if (!gAccessibilityEnabled)
430 return getOrCreate(frame->view());
433 AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole role)
435 RefPtr<AccessibilityObject> obj = 0;
437 // will be filled in...
439 case ListBoxOptionRole:
440 obj = AccessibilityListBoxOption::create();
442 case ImageMapLinkRole:
443 obj = AccessibilityImageMapLink::create();
446 obj = AccessibilityTableColumn::create();
448 case TableHeaderContainerRole:
449 obj = AccessibilityTableHeaderContainer::create();
451 case SliderThumbRole:
452 obj = AccessibilitySliderThumb::create();
454 case MenuListPopupRole:
455 obj = AccessibilityMenuListPopup::create();
457 case MenuListOptionRole:
458 obj = AccessibilityMenuListOption::create();
461 obj = AccessibilitySpinButton::create();
463 case SpinButtonPartRole:
464 obj = AccessibilitySpinButtonPart::create();
475 m_objects.set(obj->axObjectID(), obj);
477 attachWrapper(obj.get());
481 void AXObjectCache::remove(AXID axID)
486 // first fetch object to operate some cleanup functions on it
487 AccessibilityObject* obj = m_objects.get(axID);
495 // finally remove the object
496 if (!m_objects.take(axID))
499 ASSERT(m_objects.size() >= m_idsInUse.size());
502 void AXObjectCache::remove(RenderObject* renderer)
507 AXID axID = m_renderObjectMapping.get(renderer);
509 m_renderObjectMapping.remove(renderer);
512 void AXObjectCache::remove(Node* node)
517 removeNodeForUse(node);
519 // This is all safe even if we didn't have a mapping.
520 AXID axID = m_nodeObjectMapping.get(node);
522 m_nodeObjectMapping.remove(node);
524 if (node->renderer()) {
525 remove(node->renderer());
530 void AXObjectCache::remove(Widget* view)
535 AXID axID = m_widgetObjectMapping.get(view);
537 m_widgetObjectMapping.remove(view);
541 #if !PLATFORM(WIN) || OS(WINCE)
542 AXID AXObjectCache::platformGenerateAXID() const
544 static AXID lastUsedID = 0;
546 // Generate a new ID.
547 AXID objID = lastUsedID;
550 } while (!objID || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
558 AXID AXObjectCache::getAXID(AccessibilityObject* obj)
560 // check for already-assigned ID
561 AXID objID = obj->axObjectID();
563 ASSERT(m_idsInUse.contains(objID));
567 objID = platformGenerateAXID();
569 m_idsInUse.add(objID);
570 obj->setAXObjectID(objID);
575 void AXObjectCache::removeAXID(AccessibilityObject* object)
580 AXID objID = object->axObjectID();
583 ASSERT(!HashTraits<AXID>::isDeletedValue(objID));
584 ASSERT(m_idsInUse.contains(objID));
585 object->setAXObjectID(0);
586 m_idsInUse.remove(objID);
589 void AXObjectCache::textChanged(Node* node)
591 textChanged(getOrCreate(node));
594 void AXObjectCache::textChanged(RenderObject* renderer)
596 textChanged(getOrCreate(renderer));
599 void AXObjectCache::textChanged(AccessibilityObject* obj)
604 bool parentAlreadyExists = obj->parentObjectIfExists();
606 postNotification(obj, obj->document(), AXObjectCache::AXTextChanged, true);
607 if (parentAlreadyExists)
608 obj->notifyIfIgnoredValueChanged();
611 void AXObjectCache::updateCacheAfterNodeIsAttached(Node* node)
613 // Calling get() will update the AX object if we had an AccessibilityNodeObject but now we need
614 // an AccessibilityRenderObject, because it was reparented to a location outside of a canvas.
618 void AXObjectCache::childrenChanged(Node* node)
620 childrenChanged(get(node));
623 void AXObjectCache::childrenChanged(RenderObject* renderer)
625 childrenChanged(get(renderer));
628 void AXObjectCache::childrenChanged(AccessibilityObject* obj)
633 obj->childrenChanged();
636 void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*)
638 RefPtr<Document> protectorForCacheOwner(m_document);
640 m_notificationPostTimer.stop();
642 unsigned i = 0, count = m_notificationsToPost.size();
643 for (i = 0; i < count; ++i) {
644 AccessibilityObject* obj = m_notificationsToPost[i].first.get();
645 if (!obj->axObjectID())
648 if (!obj->axObjectCache())
652 // Make sure none of the render views are in the process of being layed out.
653 // Notifications should only be sent after the renderer has finished
654 if (obj->isAccessibilityRenderObject()) {
655 AccessibilityRenderObject* renderObj = static_cast<AccessibilityRenderObject*>(obj);
656 RenderObject* renderer = renderObj->renderer();
658 ASSERT(!renderer->view().layoutState());
662 AXNotification notification = m_notificationsToPost[i].second;
663 postPlatformNotification(obj, notification);
665 if (notification == AXChildrenChanged && obj->parentObjectIfExists() && obj->lastKnownIsIgnoredValue() != obj->accessibilityIsIgnored())
666 childrenChanged(obj->parentObject());
669 m_notificationsToPost.clear();
672 void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, bool postToElement, PostType postType)
677 stopCachingComputedObjectAttributes();
679 // Get an accessibility object that already exists. One should not be created here
680 // because a render update may be in progress and creating an AX object can re-trigger a layout
681 RefPtr<AccessibilityObject> object = get(renderer);
682 while (!object && renderer) {
683 renderer = renderer->parent();
684 object = get(renderer);
690 postNotification(object.get(), &renderer->document(), notification, postToElement, postType);
693 void AXObjectCache::postNotification(Node* node, AXNotification notification, bool postToElement, PostType postType)
698 stopCachingComputedObjectAttributes();
700 // Get an accessibility object that already exists. One should not be created here
701 // because a render update may be in progress and creating an AX object can re-trigger a layout
702 RefPtr<AccessibilityObject> object = get(node);
703 while (!object && node) {
704 node = node->parentNode();
711 postNotification(object.get(), node->document(), notification, postToElement, postType);
714 void AXObjectCache::postNotification(AccessibilityObject* object, Document* document, AXNotification notification, bool postToElement, PostType postType)
716 stopCachingComputedObjectAttributes();
718 if (object && !postToElement)
719 object = object->observableObject();
721 if (!object && document)
722 object = get(document->renderer());
727 if (postType == PostAsynchronously) {
728 m_notificationsToPost.append(std::make_pair(object, notification));
729 if (!m_notificationPostTimer.isActive())
730 m_notificationPostTimer.startOneShot(0);
732 postPlatformNotification(object, notification);
735 void AXObjectCache::checkedStateChanged(Node* node)
737 postNotification(node, AXObjectCache::AXCheckedStateChanged, true);
740 void AXObjectCache::selectedChildrenChanged(Node* node)
742 // postToElement is false so that you can pass in any child of an element and it will go up the parent tree
743 // to find the container which should send out the notification.
744 postNotification(node, AXSelectedChildrenChanged, false);
747 void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
749 // postToElement is false so that you can pass in any child of an element and it will go up the parent tree
750 // to find the container which should send out the notification.
751 postNotification(renderer, AXSelectedChildrenChanged, false);
754 void AXObjectCache::nodeTextChangeNotification(Node* node, AXTextChange textChange, unsigned offset, const String& text)
759 stopCachingComputedObjectAttributes();
761 // Delegate on the right platform
762 AccessibilityObject* obj = getOrCreate(node);
763 nodeTextChangePlatformNotification(obj, textChange, offset, text);
766 void AXObjectCache::frameLoadingEventNotification(Frame* frame, AXLoadingEvent loadingEvent)
771 // Delegate on the right platform
772 RenderView* contentRenderer = frame->contentRenderer();
773 if (!contentRenderer)
776 AccessibilityObject* obj = getOrCreate(contentRenderer);
777 frameLoadingEventPlatformNotification(obj, loadingEvent);
780 void AXObjectCache::handleScrollbarUpdate(ScrollView* view)
785 // We don't want to create a scroll view from this method, only update an existing one.
786 if (AccessibilityObject* scrollViewObject = get(view)) {
787 stopCachingComputedObjectAttributes();
788 scrollViewObject->updateChildrenIfNecessary();
792 void AXObjectCache::handleAriaExpandedChange(Node* node)
794 if (AccessibilityObject* obj = getOrCreate(node))
795 obj->handleAriaExpandedChanged();
798 void AXObjectCache::handleActiveDescendantChanged(Node* node)
800 if (AccessibilityObject* obj = getOrCreate(node))
801 obj->handleActiveDescendantChanged();
804 void AXObjectCache::handleAriaRoleChanged(Node* node)
806 stopCachingComputedObjectAttributes();
808 if (AccessibilityObject* obj = getOrCreate(node)) {
809 obj->updateAccessibilityRole();
810 obj->notifyIfIgnoredValueChanged();
814 void AXObjectCache::handleAttributeChanged(const QualifiedName& attrName, Element* element)
816 if (attrName == roleAttr)
817 handleAriaRoleChanged(element);
818 else if (attrName == altAttr || attrName == titleAttr)
819 textChanged(element);
820 else if (attrName == forAttr && isHTMLLabelElement(element))
821 labelChanged(element);
823 if (!attrName.localName().string().startsWith("aria-"))
826 if (attrName == aria_activedescendantAttr)
827 handleActiveDescendantChanged(element);
828 else if (attrName == aria_valuenowAttr || attrName == aria_valuetextAttr)
829 postNotification(element, AXObjectCache::AXValueChanged, true);
830 else if (attrName == aria_labelAttr || attrName == aria_labeledbyAttr || attrName == aria_labelledbyAttr)
831 textChanged(element);
832 else if (attrName == aria_checkedAttr)
833 checkedStateChanged(element);
834 else if (attrName == aria_selectedAttr)
835 selectedChildrenChanged(element);
836 else if (attrName == aria_expandedAttr)
837 handleAriaExpandedChange(element);
838 else if (attrName == aria_hiddenAttr)
839 childrenChanged(element->parentNode());
840 else if (attrName == aria_invalidAttr)
841 postNotification(element, AXObjectCache::AXInvalidStatusChanged, true);
843 postNotification(element, AXObjectCache::AXAriaAttributeChanged, true);
846 void AXObjectCache::labelChanged(Element* element)
848 ASSERT(isHTMLLabelElement(element));
849 HTMLElement* correspondingControl = toHTMLLabelElement(element)->control();
850 textChanged(correspondingControl);
853 void AXObjectCache::recomputeIsIgnored(RenderObject* renderer)
855 if (AccessibilityObject* obj = get(renderer))
856 obj->notifyIfIgnoredValueChanged();
859 void AXObjectCache::startCachingComputedObjectAttributesUntilTreeMutates()
861 if (!m_computedObjectAttributeCache)
862 m_computedObjectAttributeCache = AXComputedObjectAttributeCache::create();
865 void AXObjectCache::stopCachingComputedObjectAttributes()
867 if (m_computedObjectAttributeCache)
868 m_computedObjectAttributeCache.clear();
871 VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData& textMarkerData)
873 if (!isNodeInUse(textMarkerData.node))
874 return VisiblePosition();
876 // FIXME: Accessability should make it clear these are DOM-compliant offsets or store Position objects.
877 VisiblePosition visiblePos = VisiblePosition(createLegacyEditingPosition(textMarkerData.node, textMarkerData.offset), textMarkerData.affinity);
878 Position deepPos = visiblePos.deepEquivalent();
879 if (deepPos.isNull())
880 return VisiblePosition();
882 RenderObject* renderer = deepPos.deprecatedNode()->renderer();
884 return VisiblePosition();
886 AXObjectCache* cache = renderer->document().axObjectCache();
887 if (!cache->isIDinUse(textMarkerData.axID))
888 return VisiblePosition();
890 if (deepPos.deprecatedNode() != textMarkerData.node || deepPos.deprecatedEditingOffset() != textMarkerData.offset)
891 return VisiblePosition();
896 void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos)
898 // This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence.
899 // This also allows callers to check for failure by looking at textMarkerData upon return.
900 memset(&textMarkerData, 0, sizeof(TextMarkerData));
902 if (visiblePos.isNull())
905 Position deepPos = visiblePos.deepEquivalent();
906 Node* domNode = deepPos.deprecatedNode();
911 if (domNode->isHTMLElement()) {
912 HTMLInputElement* inputElement = domNode->toInputElement();
913 if (inputElement && inputElement->isPasswordField())
917 // find or create an accessibility object for this node
918 AXObjectCache* cache = domNode->document()->axObjectCache();
919 RefPtr<AccessibilityObject> obj = cache->getOrCreate(domNode);
921 textMarkerData.axID = obj.get()->axObjectID();
922 textMarkerData.node = domNode;
923 textMarkerData.offset = deepPos.deprecatedEditingOffset();
924 textMarkerData.affinity = visiblePos.affinity();
926 cache->setNodeInUse(domNode);
929 const Element* AXObjectCache::rootAXEditableElement(const Node* node)
931 const Element* result = node->rootEditableElement();
932 const Element* element = node->isElementNode() ? toElement(node) : node->parentElement();
934 for (; element; element = element->parentElement()) {
935 if (nodeIsTextControl(element))
942 bool AXObjectCache::nodeIsTextControl(const Node* node)
947 const AccessibilityObject* axObject = getOrCreate(const_cast<Node*>(node));
948 return axObject && axObject->isTextControl();
951 bool isNodeAriaVisible(Node* node)
956 if (!node->isElementNode())
959 return equalIgnoringCase(toElement(node)->getAttribute(aria_hiddenAttr), "false");
962 AXAttributeCacheEnabler::AXAttributeCacheEnabler(AXObjectCache* cache)
966 m_cache->startCachingComputedObjectAttributesUntilTreeMutates();
969 AXAttributeCacheEnabler::~AXAttributeCacheEnabler()
972 m_cache->stopCachingComputedObjectAttributes();
975 } // namespace WebCore
977 #endif // HAVE(ACCESSIBILITY)