~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/3rdparty/webkit/WebCore/accessibility/AXObjectCache.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alessandro Ghersi
  • Date: 2009-11-02 18:30:08 UTC
  • mfrom: (1.2.2 upstream)
  • mto: (15.2.5 experimental)
  • mto: This revision was merged to the branch mainline in revision 88.
  • Revision ID: james.westby@ubuntu.com-20091102183008-b6a4gcs128mvfb3m
Tags: upstream-4.6.0~beta1
ImportĀ upstreamĀ versionĀ 4.6.0~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2008, 2009 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
#include "AXObjectCache.h"
 
31
 
 
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"
 
48
#include "Frame.h"
 
49
#include "HTMLNames.h"
 
50
#if ENABLE(VIDEO)
 
51
#include "MediaControlElements.h"
 
52
#endif
 
53
#include "InputElement.h"
 
54
#include "Page.h"
 
55
#include "RenderObject.h"
 
56
#include "RenderView.h"
 
57
 
 
58
#include <wtf/PassRefPtr.h>
 
59
 
 
60
namespace WebCore {
 
61
 
 
62
using namespace HTMLNames;
 
63
    
 
64
bool AXObjectCache::gAccessibilityEnabled = false;
 
65
bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false;
 
66
 
 
67
AXObjectCache::AXObjectCache()
 
68
    : m_notificationPostTimer(this, &AXObjectCache::notificationPostTimerFired)
 
69
{
 
70
}
 
71
 
 
72
AXObjectCache::~AXObjectCache()
 
73
{
 
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();
 
77
        detachWrapper(obj);
 
78
        obj->detach();
 
79
        removeAXID(obj);
 
80
    }
 
81
}
 
82
 
 
83
AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page)
 
84
{
 
85
    // get the focused node in the page
 
86
    Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document();
 
87
    Node* focusedNode = focusedDocument->focusedNode();
 
88
    if (!focusedNode)
 
89
        focusedNode = focusedDocument;
 
90
 
 
91
    RenderObject* focusedNodeRenderer = focusedNode->renderer();
 
92
    if (!focusedNodeRenderer)
 
93
        return 0;
 
94
 
 
95
    AccessibilityObject* obj = focusedNodeRenderer->document()->axObjectCache()->getOrCreate(focusedNodeRenderer);
 
96
 
 
97
    if (obj->shouldFocusActiveDescendant()) {
 
98
        if (AccessibilityObject* descendant = obj->activeDescendant())
 
99
            obj = descendant;
 
100
    }
 
101
 
 
102
    // the HTML element, for example, is focusable but has an AX object that is ignored
 
103
    if (obj->accessibilityIsIgnored())
 
104
        obj = obj->parentObjectUnignored();
 
105
 
 
106
    return obj;
 
107
}
 
108
 
 
109
AccessibilityObject* AXObjectCache::get(RenderObject* renderer)
 
110
{
 
111
    if (!renderer)
 
112
        return 0;
 
113
    
 
114
    AccessibilityObject* obj = 0;
 
115
    AXID axID = m_renderObjectMapping.get(renderer);
 
116
    ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
 
117
 
 
118
    if (axID)
 
119
        obj = m_objects.get(axID).get();
 
120
    
 
121
    return obj;
 
122
}
 
123
    
 
124
bool AXObjectCache::nodeIsAriaType(Node* node, String role)
 
125
{
 
126
    if (!node || !node->isElementNode())
 
127
        return false;
 
128
    
 
129
    return equalIgnoringCase(static_cast<Element*>(node)->getAttribute(roleAttr), role);
 
130
}
 
131
 
 
132
AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
 
133
{
 
134
    if (!renderer)
 
135
        return 0;
 
136
    
 
137
    AccessibilityObject* obj = get(renderer);
 
138
 
 
139
    if (!obj) {
 
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);
 
146
        
 
147
        // aria tables
 
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);
 
154
 
 
155
        // standard tables
 
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);
 
162
 
 
163
#if ENABLE(VIDEO)
 
164
        // media controls
 
165
        else if (renderer->node() && renderer->node()->isMediaControlElement())
 
166
            newObj = AccessibilityMediaControl::create(renderer);
 
167
#endif
 
168
 
 
169
        // input type=range
 
170
        else if (renderer->isSlider())
 
171
            newObj = AccessibilitySlider::create(renderer);
 
172
 
 
173
        else
 
174
            newObj = AccessibilityRenderObject::create(renderer);
 
175
        
 
176
        obj = newObj.get();
 
177
        
 
178
        getAXID(obj);
 
179
        
 
180
        m_renderObjectMapping.set(renderer, obj->axObjectID());
 
181
        m_objects.set(obj->axObjectID(), obj);    
 
182
        attachWrapper(obj);
 
183
    }
 
184
    
 
185
    return obj;
 
186
}
 
187
 
 
188
AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole role)
 
189
{
 
190
    RefPtr<AccessibilityObject> obj = 0;
 
191
    
 
192
    // will be filled in...
 
193
    switch (role) {
 
194
        case ListBoxOptionRole:
 
195
            obj = AccessibilityListBoxOption::create();
 
196
            break;
 
197
        case ImageMapLinkRole:
 
198
            obj = AccessibilityImageMapLink::create();
 
199
            break;
 
200
        case ColumnRole:
 
201
            obj = AccessibilityTableColumn::create();
 
202
            break;            
 
203
        case TableHeaderContainerRole:
 
204
            obj = AccessibilityTableHeaderContainer::create();
 
205
            break;   
 
206
        case SliderThumbRole:
 
207
            obj = AccessibilitySliderThumb::create();
 
208
            break;
 
209
        default:
 
210
            obj = 0;
 
211
    }
 
212
    
 
213
    if (obj)
 
214
        getAXID(obj.get());
 
215
    else
 
216
        return 0;
 
217
 
 
218
    m_objects.set(obj->axObjectID(), obj);    
 
219
    attachWrapper(obj.get());
 
220
    return obj.get();
 
221
}
 
222
 
 
223
void AXObjectCache::remove(AXID axID)
 
224
{
 
225
    if (!axID)
 
226
        return;
 
227
    
 
228
    // first fetch object to operate some cleanup functions on it 
 
229
    AccessibilityObject* obj = m_objects.get(axID).get();
 
230
    if (!obj)
 
231
        return;
 
232
    
 
233
    detachWrapper(obj);
 
234
    obj->detach();
 
235
    removeAXID(obj);
 
236
    
 
237
    // finally remove the object
 
238
    if (!m_objects.take(axID)) {
 
239
        return;
 
240
    }
 
241
    
 
242
    ASSERT(m_objects.size() >= m_idsInUse.size());    
 
243
}
 
244
    
 
245
void AXObjectCache::remove(RenderObject* renderer)
 
246
{
 
247
    if (!renderer)
 
248
        return;
 
249
    
 
250
    AXID axID = m_renderObjectMapping.get(renderer);
 
251
    remove(axID);
 
252
    m_renderObjectMapping.remove(renderer);
 
253
}
 
254
 
 
255
#if !PLATFORM(WIN)
 
256
AXID AXObjectCache::platformGenerateAXID() const
 
257
{
 
258
    static AXID lastUsedID = 0;
 
259
 
 
260
    // Generate a new ID.
 
261
    AXID objID = lastUsedID;
 
262
    do {
 
263
        ++objID;
 
264
    } while (objID == 0 || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
 
265
 
 
266
    lastUsedID = objID;
 
267
 
 
268
    return objID;
 
269
}
 
270
#endif
 
271
 
 
272
AXID AXObjectCache::getAXID(AccessibilityObject* obj)
 
273
{
 
274
    // check for already-assigned ID
 
275
    AXID objID = obj->axObjectID();
 
276
    if (objID) {
 
277
        ASSERT(m_idsInUse.contains(objID));
 
278
        return objID;
 
279
    }
 
280
 
 
281
    objID = platformGenerateAXID();
 
282
 
 
283
    m_idsInUse.add(objID);
 
284
    obj->setAXObjectID(objID);
 
285
    
 
286
    return objID;
 
287
}
 
288
 
 
289
void AXObjectCache::removeAXID(AccessibilityObject* obj)
 
290
{
 
291
    if (!obj)
 
292
        return;
 
293
    
 
294
    AXID objID = obj->axObjectID();
 
295
    if (objID == 0)
 
296
        return;
 
297
    ASSERT(!HashTraits<AXID>::isDeletedValue(objID));
 
298
    ASSERT(m_idsInUse.contains(objID));
 
299
    obj->setAXObjectID(0);
 
300
    m_idsInUse.remove(objID);
 
301
}
 
302
 
 
303
void AXObjectCache::childrenChanged(RenderObject* renderer)
 
304
{
 
305
    if (!renderer)
 
306
        return;
 
307
 
 
308
    AXID axID = m_renderObjectMapping.get(renderer);
 
309
    if (!axID)
 
310
        return;
 
311
    
 
312
    AccessibilityObject* obj = m_objects.get(axID).get();
 
313
    if (obj)
 
314
        obj->childrenChanged();
 
315
}
 
316
    
 
317
void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*)
 
318
{
 
319
    m_notificationPostTimer.stop();
 
320
 
 
321
    unsigned i = 0, count = m_notificationsToPost.size();
 
322
    for (i = 0; i < count; ++i) {
 
323
        AccessibilityObject* obj = m_notificationsToPost[i].first.get();
 
324
#ifndef NDEBUG
 
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());
 
332
        }
 
333
#endif
 
334
        
 
335
        postPlatformNotification(obj, m_notificationsToPost[i].second);
 
336
    }
 
337
    
 
338
    m_notificationsToPost.clear();
 
339
}
 
340
    
 
341
#if HAVE(ACCESSIBILITY)
 
342
void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, bool postToElement)
 
343
{
 
344
    // Notifications for text input objects are sent to that object.
 
345
    // All others are sent to the top WebArea.
 
346
    if (!renderer)
 
347
        return;
 
348
    
 
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();
 
354
        obj = get(renderer); 
 
355
    }
 
356
    
 
357
    if (!renderer)
 
358
        return;
 
359
 
 
360
    if (obj && !postToElement)
 
361
        obj = obj->observableObject();
 
362
    
 
363
    Document* document = renderer->document();
 
364
    if (!obj && document)
 
365
        obj = get(document->renderer());
 
366
    
 
367
    if (!obj)
 
368
        return;
 
369
 
 
370
    m_notificationsToPost.append(make_pair(obj, notification));
 
371
    if (!m_notificationPostTimer.isActive())
 
372
        m_notificationPostTimer.startOneShot(0);
 
373
}
 
374
 
 
375
void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
 
376
{
 
377
    postNotification(renderer, AXSelectedChildrenChanged, true);
 
378
}
 
379
#endif
 
380
 
 
381
#if HAVE(ACCESSIBILITY)
 
382
void AXObjectCache::handleActiveDescendantChanged(RenderObject* renderer)
 
383
{
 
384
    if (!renderer)
 
385
        return;
 
386
    AccessibilityObject* obj = getOrCreate(renderer);
 
387
    if (obj)
 
388
        obj->handleActiveDescendantChanged();
 
389
}
 
390
 
 
391
void AXObjectCache::handleAriaRoleChanged(RenderObject* renderer)
 
392
{
 
393
    if (!renderer)
 
394
        return;
 
395
    AccessibilityObject* obj = getOrCreate(renderer);
 
396
    if (obj && obj->isAccessibilityRenderObject())
 
397
        static_cast<AccessibilityRenderObject*>(obj)->updateAccessibilityRole();
 
398
}
 
399
#endif
 
400
    
 
401
VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData& textMarkerData)
 
402
{
 
403
    VisiblePosition visiblePos = VisiblePosition(textMarkerData.node, textMarkerData.offset, textMarkerData.affinity);
 
404
    Position deepPos = visiblePos.deepEquivalent();
 
405
    if (deepPos.isNull())
 
406
        return VisiblePosition();
 
407
    
 
408
    RenderObject* renderer = deepPos.node()->renderer();
 
409
    if (!renderer)
 
410
        return VisiblePosition();
 
411
    
 
412
    AXObjectCache* cache = renderer->document()->axObjectCache();
 
413
    if (!cache->isIDinUse(textMarkerData.axID))
 
414
        return VisiblePosition();
 
415
    
 
416
    if (deepPos.node() != textMarkerData.node || deepPos.deprecatedEditingOffset() != textMarkerData.offset)
 
417
        return VisiblePosition();
 
418
    
 
419
    return visiblePos;
 
420
}
 
421
 
 
422
void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos)
 
423
{
 
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));
 
427
    
 
428
    if (visiblePos.isNull())
 
429
        return;
 
430
    
 
431
    Position deepPos = visiblePos.deepEquivalent();
 
432
    Node* domNode = deepPos.node();
 
433
    ASSERT(domNode);
 
434
    if (!domNode)
 
435
        return;
 
436
    
 
437
    if (domNode->isHTMLElement()) {
 
438
        InputElement* inputElement = toInputElement(static_cast<Element*>(domNode));
 
439
        if (inputElement && inputElement->isPasswordField())
 
440
            return;
 
441
    }
 
442
    
 
443
    // locate the renderer, which must exist for a visible dom node
 
444
    RenderObject* renderer = domNode->renderer();
 
445
    ASSERT(renderer);
 
446
    
 
447
    // find or create an accessibility object for this renderer
 
448
    AXObjectCache* cache = renderer->document()->axObjectCache();
 
449
    RefPtr<AccessibilityObject> obj = cache->getOrCreate(renderer);
 
450
    
 
451
    textMarkerData.axID = obj.get()->axObjectID();
 
452
    textMarkerData.node = domNode;
 
453
    textMarkerData.offset = deepPos.deprecatedEditingOffset();
 
454
    textMarkerData.affinity = visiblePos.affinity();    
 
455
}
 
456
    
 
457
} // namespace WebCore