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