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