2
* Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
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.
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.
30
#include "AXObjectCache.h"
32
#include "AccessibilityARIAGrid.h"
33
#include "AccessibilityARIAGridRow.h"
34
#include "AccessibilityARIAGridCell.h"
35
#include "AccessibilityList.h"
36
#include "AccessibilityListBox.h"
37
#include "AccessibilityListBoxOption.h"
38
#include "AccessibilityImageMapLink.h"
39
#include "AccessibilityMediaControls.h"
40
#include "AccessibilityRenderObject.h"
41
#include "AccessibilitySlider.h"
42
#include "AccessibilityTable.h"
43
#include "AccessibilityTableCell.h"
44
#include "AccessibilityTableColumn.h"
45
#include "AccessibilityTableHeaderContainer.h"
46
#include "AccessibilityTableRow.h"
47
#include "FocusController.h"
49
#include "HTMLNames.h"
51
#include "MediaControlElements.h"
53
#include "InputElement.h"
55
#include "RenderObject.h"
56
#include "RenderView.h"
58
#include <wtf/PassRefPtr.h>
62
using namespace HTMLNames;
64
bool AXObjectCache::gAccessibilityEnabled = false;
65
bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false;
67
AXObjectCache::AXObjectCache()
68
: m_notificationPostTimer(this, &AXObjectCache::notificationPostTimerFired)
72
AXObjectCache::~AXObjectCache()
74
HashMap<AXID, RefPtr<AccessibilityObject> >::iterator end = m_objects.end();
75
for (HashMap<AXID, RefPtr<AccessibilityObject> >::iterator it = m_objects.begin(); it != end; ++it) {
76
AccessibilityObject* obj = (*it).second.get();
83
AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page)
85
// get the focused node in the page
86
Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document();
87
Node* focusedNode = focusedDocument->focusedNode();
89
focusedNode = focusedDocument;
91
RenderObject* focusedNodeRenderer = focusedNode->renderer();
92
if (!focusedNodeRenderer)
95
AccessibilityObject* obj = focusedNodeRenderer->document()->axObjectCache()->getOrCreate(focusedNodeRenderer);
97
if (obj->shouldFocusActiveDescendant()) {
98
if (AccessibilityObject* descendant = obj->activeDescendant())
102
// the HTML element, for example, is focusable but has an AX object that is ignored
103
if (obj->accessibilityIsIgnored())
104
obj = obj->parentObjectUnignored();
109
AccessibilityObject* AXObjectCache::get(RenderObject* renderer)
114
AccessibilityObject* obj = 0;
115
AXID axID = m_renderObjectMapping.get(renderer);
116
ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
119
obj = m_objects.get(axID).get();
124
bool AXObjectCache::nodeIsAriaType(Node* node, String role)
126
if (!node || !node->isElementNode())
129
return equalIgnoringCase(static_cast<Element*>(node)->getAttribute(roleAttr), role);
132
AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
137
AccessibilityObject* obj = get(renderer);
140
Node* node = renderer->node();
141
RefPtr<AccessibilityObject> newObj = 0;
142
if (renderer->isListBox())
143
newObj = AccessibilityListBox::create(renderer);
144
else if (node && (nodeIsAriaType(node, "list") || node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(dlTag)))
145
newObj = AccessibilityList::create(renderer);
148
else if (nodeIsAriaType(node, "grid"))
149
newObj = AccessibilityARIAGrid::create(renderer);
150
else if (nodeIsAriaType(node, "row"))
151
newObj = AccessibilityARIAGridRow::create(renderer);
152
else if (nodeIsAriaType(node, "gridcell") || nodeIsAriaType(node, "columnheader") || nodeIsAriaType(node, "rowheader"))
153
newObj = AccessibilityARIAGridCell::create(renderer);
156
else if (renderer->isTable())
157
newObj = AccessibilityTable::create(renderer);
158
else if (renderer->isTableRow())
159
newObj = AccessibilityTableRow::create(renderer);
160
else if (renderer->isTableCell())
161
newObj = AccessibilityTableCell::create(renderer);
165
else if (renderer->node() && renderer->node()->isMediaControlElement())
166
newObj = AccessibilityMediaControl::create(renderer);
170
else if (renderer->isSlider())
171
newObj = AccessibilitySlider::create(renderer);
174
newObj = AccessibilityRenderObject::create(renderer);
180
m_renderObjectMapping.set(renderer, obj->axObjectID());
181
m_objects.set(obj->axObjectID(), obj);
188
AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole role)
190
RefPtr<AccessibilityObject> obj = 0;
192
// will be filled in...
194
case ListBoxOptionRole:
195
obj = AccessibilityListBoxOption::create();
197
case ImageMapLinkRole:
198
obj = AccessibilityImageMapLink::create();
201
obj = AccessibilityTableColumn::create();
203
case TableHeaderContainerRole:
204
obj = AccessibilityTableHeaderContainer::create();
206
case SliderThumbRole:
207
obj = AccessibilitySliderThumb::create();
218
m_objects.set(obj->axObjectID(), obj);
219
attachWrapper(obj.get());
223
void AXObjectCache::remove(AXID axID)
228
// first fetch object to operate some cleanup functions on it
229
AccessibilityObject* obj = m_objects.get(axID).get();
237
// finally remove the object
238
if (!m_objects.take(axID)) {
242
ASSERT(m_objects.size() >= m_idsInUse.size());
245
void AXObjectCache::remove(RenderObject* renderer)
250
AXID axID = m_renderObjectMapping.get(renderer);
252
m_renderObjectMapping.remove(renderer);
256
AXID AXObjectCache::platformGenerateAXID() const
258
static AXID lastUsedID = 0;
260
// Generate a new ID.
261
AXID objID = lastUsedID;
264
} while (objID == 0 || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
272
AXID AXObjectCache::getAXID(AccessibilityObject* obj)
274
// check for already-assigned ID
275
AXID objID = obj->axObjectID();
277
ASSERT(m_idsInUse.contains(objID));
281
objID = platformGenerateAXID();
283
m_idsInUse.add(objID);
284
obj->setAXObjectID(objID);
289
void AXObjectCache::removeAXID(AccessibilityObject* obj)
294
AXID objID = obj->axObjectID();
297
ASSERT(!HashTraits<AXID>::isDeletedValue(objID));
298
ASSERT(m_idsInUse.contains(objID));
299
obj->setAXObjectID(0);
300
m_idsInUse.remove(objID);
303
void AXObjectCache::childrenChanged(RenderObject* renderer)
308
AXID axID = m_renderObjectMapping.get(renderer);
312
AccessibilityObject* obj = m_objects.get(axID).get();
314
obj->childrenChanged();
317
void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*)
319
m_notificationPostTimer.stop();
321
unsigned i = 0, count = m_notificationsToPost.size();
322
for (i = 0; i < count; ++i) {
323
AccessibilityObject* obj = m_notificationsToPost[i].first.get();
325
// Make sure none of the render views are in the process of being layed out.
326
// Notifications should only be sent after the renderer has finished
327
if (obj->isAccessibilityRenderObject()) {
328
AccessibilityRenderObject* renderObj = static_cast<AccessibilityRenderObject*>(obj);
329
RenderObject* renderer = renderObj->renderer();
330
if (renderer && renderer->view())
331
ASSERT(!renderer->view()->layoutState());
335
postPlatformNotification(obj, m_notificationsToPost[i].second);
338
m_notificationsToPost.clear();
341
#if HAVE(ACCESSIBILITY)
342
void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, bool postToElement)
344
// Notifications for text input objects are sent to that object.
345
// All others are sent to the top WebArea.
349
// Get an accessibility object that already exists. One should not be created here
350
// because a render update may be in progress and creating an AX object can re-trigger a layout
351
RefPtr<AccessibilityObject> obj = get(renderer);
352
while (!obj && renderer) {
353
renderer = renderer->parent();
360
if (obj && !postToElement)
361
obj = obj->observableObject();
363
Document* document = renderer->document();
364
if (!obj && document)
365
obj = get(document->renderer());
370
m_notificationsToPost.append(make_pair(obj, notification));
371
if (!m_notificationPostTimer.isActive())
372
m_notificationPostTimer.startOneShot(0);
375
void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
377
postNotification(renderer, AXSelectedChildrenChanged, true);
381
#if HAVE(ACCESSIBILITY)
382
void AXObjectCache::handleActiveDescendantChanged(RenderObject* renderer)
386
AccessibilityObject* obj = getOrCreate(renderer);
388
obj->handleActiveDescendantChanged();
391
void AXObjectCache::handleAriaRoleChanged(RenderObject* renderer)
395
AccessibilityObject* obj = getOrCreate(renderer);
396
if (obj && obj->isAccessibilityRenderObject())
397
static_cast<AccessibilityRenderObject*>(obj)->updateAccessibilityRole();
401
VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData& textMarkerData)
403
VisiblePosition visiblePos = VisiblePosition(textMarkerData.node, textMarkerData.offset, textMarkerData.affinity);
404
Position deepPos = visiblePos.deepEquivalent();
405
if (deepPos.isNull())
406
return VisiblePosition();
408
RenderObject* renderer = deepPos.node()->renderer();
410
return VisiblePosition();
412
AXObjectCache* cache = renderer->document()->axObjectCache();
413
if (!cache->isIDinUse(textMarkerData.axID))
414
return VisiblePosition();
416
if (deepPos.node() != textMarkerData.node || deepPos.deprecatedEditingOffset() != textMarkerData.offset)
417
return VisiblePosition();
422
void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos)
424
// This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence.
425
// This also allows callers to check for failure by looking at textMarkerData upon return.
426
memset(&textMarkerData, 0, sizeof(TextMarkerData));
428
if (visiblePos.isNull())
431
Position deepPos = visiblePos.deepEquivalent();
432
Node* domNode = deepPos.node();
437
if (domNode->isHTMLElement()) {
438
InputElement* inputElement = toInputElement(static_cast<Element*>(domNode));
439
if (inputElement && inputElement->isPasswordField())
443
// locate the renderer, which must exist for a visible dom node
444
RenderObject* renderer = domNode->renderer();
447
// find or create an accessibility object for this renderer
448
AXObjectCache* cache = renderer->document()->axObjectCache();
449
RefPtr<AccessibilityObject> obj = cache->getOrCreate(renderer);
451
textMarkerData.axID = obj.get()->axObjectID();
452
textMarkerData.node = domNode;
453
textMarkerData.offset = deepPos.deprecatedEditingOffset();
454
textMarkerData.affinity = visiblePos.affinity();
457
} // namespace WebCore