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