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