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