• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2008 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#import "config.h"
30#import "AccessibilityObjectWrapper.h"
31
32#if HAVE(ACCESSIBILITY)
33
34#import "AXObjectCache.h"
35#import "AccessibilityListBox.h"
36#import "AccessibilityList.h"
37#import "AccessibilityRenderObject.h"
38#import "AccessibilityTable.h"
39#import "AccessibilityTableCell.h"
40#import "AccessibilityTableRow.h"
41#import "AccessibilityTableColumn.h"
42#import "ColorMac.h"
43#import "Frame.h"
44#import "HTMLAnchorElement.h"
45#import "HTMLAreaElement.h"
46#import "HTMLImageElement.h"
47#import "HTMLInputElement.h"
48#import "HTMLTextAreaElement.h"
49#import "LocalizedStrings.h"
50#import "RenderTextControl.h"
51#import "RenderView.h"
52#import "RenderWidget.h"
53#import "SelectionController.h"
54#import "SimpleFontData.h"
55#import "TextIterator.h"
56#import "WebCoreFrameView.h"
57#import "WebCoreObjCExtras.h"
58#import "WebCoreViewFactory.h"
59#import "htmlediting.h"
60#import "visible_units.h"
61#import <runtime/InitializeThreading.h>
62
63using namespace WebCore;
64using namespace HTMLNames;
65using namespace std;
66
67// Cell Tables
68#ifndef NSAccessibilitySelectedCellsAttribute
69#define NSAccessibilitySelectedCellsAttribute @"AXSelectedCells"
70#endif
71
72#ifndef NSAccessibilityVisibleCellsAttribute
73#define NSAccessibilityVisibleCellsAttribute @"AXVisibleCells"
74#endif
75
76#ifndef NSAccessibilityRowHeaderUIElementsAttribute
77#define NSAccessibilityRowHeaderUIElementsAttribute @"AXRowHeaderUIElements"
78#endif
79
80#ifndef NSAccessibilityRowIndexRangeAttribute
81#define NSAccessibilityRowIndexRangeAttribute @"AXRowIndexRange"
82#endif
83
84#ifndef NSAccessibilityColumnIndexRangeAttribute
85#define NSAccessibilityColumnIndexRangeAttribute @"AXColumnIndexRange"
86#endif
87
88#ifndef NSAccessibilityCellForColumnAndRowParameterizedAttribute
89#define NSAccessibilityCellForColumnAndRowParameterizedAttribute @"AXCellForColumnAndRow"
90#endif
91
92#ifndef NSAccessibilityCellRole
93#define NSAccessibilityCellRole @"AXCell"
94#endif
95
96// Lists
97#ifndef NSAccessibilityContentListSubrole
98#define NSAccessibilityContentListSubrole @"AXContentList"
99#endif
100
101#ifndef NSAccessibilityDefinitionListSubrole
102#define NSAccessibilityDefinitionListSubrole @"AXDefinitionList"
103#endif
104
105// Miscellaneous
106#ifndef NSAccessibilityBlockQuoteLevelAttribute
107#define NSAccessibilityBlockQuoteLevelAttribute @"AXBlockQuoteLevel"
108#endif
109
110#ifndef NSAccessibilityAccessKeyAttribute
111#define NSAccessibilityAccessKeyAttribute @"AXAccessKey"
112#endif
113
114#ifdef BUILDING_ON_TIGER
115typedef unsigned NSUInteger;
116#endif
117
118@interface NSObject (WebKitAccessibilityArrayCategory)
119
120- (NSUInteger)accessibilityIndexOfChild:(id)child;
121- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute;
122- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount;
123
124@end
125
126@implementation AccessibilityObjectWrapper
127
128+ (void)initialize
129{
130    JSC::initializeThreading();
131#ifndef BUILDING_ON_TIGER
132    WebCoreObjCFinalizeOnMainThread(self);
133#endif
134}
135
136- (id)initWithAccessibilityObject:(AccessibilityObject*)axObject
137{
138    [super init];
139
140    m_object = axObject;
141    return self;
142}
143
144- (void)unregisterUniqueIdForUIElement
145{
146    [[WebCoreViewFactory sharedFactory] unregisterUniqueIdForUIElement:self];
147}
148
149- (void)detach
150{
151    // Send unregisterUniqueIdForUIElement unconditionally because if it is
152    // ever accidently not done (via other bugs in our AX implementation) you
153    // end up with a crash like <rdar://problem/4273149>.  It is safe and not
154    // expensive to send even if the object is not registered.
155    [self unregisterUniqueIdForUIElement];
156    m_object = 0;
157}
158
159- (AccessibilityObject*)accessibilityObject
160{
161    return m_object;
162}
163
164- (NSView*)attachmentView
165{
166    ASSERT(m_object->isAttachment());
167    Widget* widget = m_object->widgetForAttachmentView();
168    if (!widget)
169        return nil;
170    return widget->platformWidget();
171}
172
173static WebCoreTextMarker* textMarkerForVisiblePosition(const VisiblePosition& visiblePos)
174{
175    if (visiblePos.isNull())
176        return nil;
177
178    Position deepPos = visiblePos.deepEquivalent();
179    Node* domNode = deepPos.node();
180    ASSERT(domNode);
181    if (!domNode)
182        return nil;
183
184    if (domNode->isHTMLElement())
185        if (static_cast<HTMLElement*>(domNode)->isPasswordField())
186            return nil;
187
188    // locate the renderer, which must exist for a visible dom node
189    RenderObject* renderer = domNode->renderer();
190    ASSERT(renderer);
191
192    // find or create an accessibility object for this renderer
193    AXObjectCache* cache = renderer->document()->axObjectCache();
194    RefPtr<AccessibilityObject> obj = cache->get(renderer);
195
196    // create a text marker, adding an ID for the AccessibilityObject if needed
197    TextMarkerData textMarkerData;
198
199    // The compiler can add padding to this struct.
200    // This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence.
201    bzero(&textMarkerData, sizeof(TextMarkerData));
202    textMarkerData.axID = obj.get()->axObjectID();
203    textMarkerData.node = domNode;
204    textMarkerData.offset = deepPos.offset();
205    textMarkerData.affinity = visiblePos.affinity();
206    return [[WebCoreViewFactory sharedFactory] textMarkerWithBytes:&textMarkerData length:sizeof(textMarkerData)];
207}
208
209static VisiblePosition visiblePositionForTextMarker(WebCoreTextMarker* textMarker)
210{
211    TextMarkerData textMarkerData;
212
213    if (![[WebCoreViewFactory sharedFactory] getBytes:&textMarkerData fromTextMarker:textMarker length:sizeof(textMarkerData)])
214        return VisiblePosition();
215
216    VisiblePosition visiblePos = VisiblePosition(textMarkerData.node, textMarkerData.offset, textMarkerData.affinity);
217    Position deepPos = visiblePos.deepEquivalent();
218    if (deepPos.isNull())
219        return VisiblePosition();
220
221    RenderObject* renderer = deepPos.node()->renderer();
222    if (!renderer)
223        return VisiblePosition();
224
225    AXObjectCache* cache = renderer->document()->axObjectCache();
226    if (!cache->isIDinUse(textMarkerData.axID))
227        return VisiblePosition();
228
229    if (deepPos.node() != textMarkerData.node || deepPos.offset() != textMarkerData.offset)
230        return VisiblePosition();
231
232    return visiblePos;
233}
234
235static VisiblePosition visiblePositionForStartOfTextMarkerRange(WebCoreTextMarkerRange* textMarkerRange)
236{
237    return visiblePositionForTextMarker([[WebCoreViewFactory sharedFactory] startOfTextMarkerRange:textMarkerRange]);
238}
239
240static VisiblePosition visiblePositionForEndOfTextMarkerRange(WebCoreTextMarkerRange* textMarkerRange)
241{
242    return visiblePositionForTextMarker([[WebCoreViewFactory sharedFactory] endOfTextMarkerRange:textMarkerRange]);
243}
244
245static WebCoreTextMarkerRange* textMarkerRangeFromMarkers(WebCoreTextMarker* textMarker1, WebCoreTextMarker* textMarker2)
246{
247    if (!textMarker1 || !textMarker2)
248        return nil;
249
250    return [[WebCoreViewFactory sharedFactory] textMarkerRangeWithStart:textMarker1 end:textMarker2];
251}
252
253static void AXAttributeStringSetFont(NSMutableAttributedString* attrString, NSString* attribute, NSFont* font, NSRange range)
254{
255    NSDictionary* dict;
256
257    if (font) {
258        dict = [NSDictionary dictionaryWithObjectsAndKeys:
259            [font fontName]                             , NSAccessibilityFontNameKey,
260            [font familyName]                           , NSAccessibilityFontFamilyKey,
261            [font displayName]                          , NSAccessibilityVisibleNameKey,
262            [NSNumber numberWithFloat:[font pointSize]] , NSAccessibilityFontSizeKey,
263        nil];
264
265        [attrString addAttribute:attribute value:dict range:range];
266    } else
267        [attrString removeAttribute:attribute range:range];
268
269}
270
271static CGColorRef CreateCGColorIfDifferent(NSColor* nsColor, CGColorRef existingColor)
272{
273    // get color information assuming NSDeviceRGBColorSpace
274    NSColor* rgbColor = [nsColor colorUsingColorSpaceName:NSDeviceRGBColorSpace];
275    if (rgbColor == nil)
276        rgbColor = [NSColor blackColor];
277    CGFloat components[4];
278    [rgbColor getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]];
279
280    // create a new CGColorRef to return
281    CGColorSpaceRef cgColorSpace = CGColorSpaceCreateDeviceRGB();
282    CGColorRef cgColor = CGColorCreate(cgColorSpace, components);
283    CGColorSpaceRelease(cgColorSpace);
284
285    // check for match with existing color
286    if (existingColor && CGColorEqualToColor(cgColor, existingColor)) {
287        CGColorRelease(cgColor);
288        cgColor = 0;
289    }
290
291    return cgColor;
292}
293
294static void AXAttributeStringSetColor(NSMutableAttributedString* attrString, NSString* attribute, NSColor* color, NSRange range)
295{
296    if (color) {
297        CGColorRef existingColor = (CGColorRef) [attrString attribute:attribute atIndex:range.location effectiveRange:nil];
298        CGColorRef cgColor = CreateCGColorIfDifferent(color, existingColor);
299        if (cgColor) {
300            [attrString addAttribute:attribute value:(id)cgColor range:range];
301            CGColorRelease(cgColor);
302        }
303    } else
304        [attrString removeAttribute:attribute range:range];
305}
306
307static void AXAttributeStringSetNumber(NSMutableAttributedString* attrString, NSString* attribute, NSNumber* number, NSRange range)
308{
309    if (number)
310        [attrString addAttribute:attribute value:number range:range];
311    else
312        [attrString removeAttribute:attribute range:range];
313}
314
315static void AXAttributeStringSetStyle(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
316{
317    RenderStyle* style = renderer->style();
318
319    // set basic font info
320    AXAttributeStringSetFont(attrString, NSAccessibilityFontTextAttribute, style->font().primaryFont()->getNSFont(), range);
321
322    // set basic colors
323    AXAttributeStringSetColor(attrString, NSAccessibilityForegroundColorTextAttribute, nsColor(style->color()), range);
324    AXAttributeStringSetColor(attrString, NSAccessibilityBackgroundColorTextAttribute, nsColor(style->backgroundColor()), range);
325
326    // set super/sub scripting
327    EVerticalAlign alignment = style->verticalAlign();
328    if (alignment == SUB)
329        AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:(-1)], range);
330    else if (alignment == SUPER)
331        AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:1], range);
332    else
333        [attrString removeAttribute:NSAccessibilitySuperscriptTextAttribute range:range];
334
335    // set shadow
336    if (style->textShadow())
337        AXAttributeStringSetNumber(attrString, NSAccessibilityShadowTextAttribute, [NSNumber numberWithBool:YES], range);
338    else
339        [attrString removeAttribute:NSAccessibilityShadowTextAttribute range:range];
340
341    // set underline and strikethrough
342    int decor = style->textDecorationsInEffect();
343    if ((decor & UNDERLINE) == 0) {
344        [attrString removeAttribute:NSAccessibilityUnderlineTextAttribute range:range];
345        [attrString removeAttribute:NSAccessibilityUnderlineColorTextAttribute range:range];
346    }
347
348    if ((decor & LINE_THROUGH) == 0) {
349        [attrString removeAttribute:NSAccessibilityStrikethroughTextAttribute range:range];
350        [attrString removeAttribute:NSAccessibilityStrikethroughColorTextAttribute range:range];
351    }
352
353    if ((decor & (UNDERLINE | LINE_THROUGH)) != 0) {
354        // find colors using quirk mode approach (strict mode would use current
355        // color for all but the root line box, which would use getTextDecorationColors)
356        Color underline, overline, linethrough;
357        renderer->getTextDecorationColors(decor, underline, overline, linethrough);
358
359        if ((decor & UNDERLINE) != 0) {
360            AXAttributeStringSetNumber(attrString, NSAccessibilityUnderlineTextAttribute, [NSNumber numberWithBool:YES], range);
361            AXAttributeStringSetColor(attrString, NSAccessibilityUnderlineColorTextAttribute, nsColor(underline), range);
362        }
363
364        if ((decor & LINE_THROUGH) != 0) {
365            AXAttributeStringSetNumber(attrString, NSAccessibilityStrikethroughTextAttribute, [NSNumber numberWithBool:YES], range);
366            AXAttributeStringSetColor(attrString, NSAccessibilityStrikethroughColorTextAttribute, nsColor(linethrough), range);
367        }
368    }
369}
370
371static int blockquoteLevel(RenderObject* renderer)
372{
373    if (!renderer)
374        return 0;
375
376    int result = 0;
377    for (Node* node = renderer->element(); node; node = node->parent()) {
378        if (node->hasTagName(blockquoteTag))
379            result += 1;
380    }
381
382    return result;
383}
384
385static void AXAttributeStringSetBlockquoteLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
386{
387    int quoteLevel = blockquoteLevel(renderer);
388
389    if (quoteLevel)
390        [attrString addAttribute:NSAccessibilityBlockQuoteLevelAttribute value:[NSNumber numberWithInt:quoteLevel] range:range];
391    else
392        [attrString removeAttribute:NSAccessibilityBlockQuoteLevelAttribute range:range];
393}
394
395static void AXAttributeStringSetSpelling(NSMutableAttributedString* attrString, Node* node, int offset, NSRange range)
396{
397    Vector<DocumentMarker> markers = node->renderer()->document()->markersForNode(node);
398    Vector<DocumentMarker>::iterator markerIt = markers.begin();
399
400    unsigned endOffset = (unsigned)offset + range.length;
401    for ( ; markerIt != markers.end(); markerIt++) {
402        DocumentMarker marker = *markerIt;
403
404        if (marker.type != DocumentMarker::Spelling)
405            continue;
406
407        if (marker.endOffset <= (unsigned)offset)
408            continue;
409
410        if (marker.startOffset > endOffset)
411            break;
412
413        // add misspelling attribute for the intersection of the marker and the range
414        int rStart = range.location + (marker.startOffset - offset);
415        int rLength = MIN(marker.endOffset, endOffset) - marker.startOffset;
416        NSRange spellRange = NSMakeRange(rStart, rLength);
417        AXAttributeStringSetNumber(attrString, NSAccessibilityMisspelledTextAttribute, [NSNumber numberWithBool:YES], spellRange);
418
419        if (marker.endOffset > endOffset + 1)
420            break;
421    }
422}
423
424static void AXAttributeStringSetHeadingLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
425{
426    int parentHeadingLevel = AccessibilityRenderObject::headingLevel(renderer->parent()->element());
427
428    if (parentHeadingLevel)
429        [attrString addAttribute:@"AXHeadingLevel" value:[NSNumber numberWithInt:parentHeadingLevel] range:range];
430    else
431        [attrString removeAttribute:@"AXHeadingLevel" range:range];
432}
433
434static AccessibilityObject* AXLinkElementForNode(Node* node)
435{
436    RenderObject* obj = node->renderer();
437    if (!obj)
438        return 0;
439
440    RefPtr<AccessibilityObject> axObj = obj->document()->axObjectCache()->get(obj);
441    Element* anchor = axObj->anchorElement();
442    if (!anchor)
443        return 0;
444
445    RenderObject* anchorRenderer = anchor->renderer();
446    if (!anchorRenderer)
447        return 0;
448
449    return anchorRenderer->document()->axObjectCache()->get(anchorRenderer);
450}
451
452static void AXAttributeStringSetElement(NSMutableAttributedString* attrString, NSString* attribute, AccessibilityObject* object, NSRange range)
453{
454    if (object && object->isAccessibilityRenderObject()) {
455        // make a serialiazable AX object
456
457        RenderObject* renderer = static_cast<AccessibilityRenderObject*>(object)->renderer();
458        if (!renderer)
459            return;
460
461        Document* doc = renderer->document();
462        if (!doc)
463            return;
464
465        AXObjectCache* cache = doc->axObjectCache();
466        if (!cache)
467            return;
468
469        AXUIElementRef axElement = [[WebCoreViewFactory sharedFactory] AXUIElementForElement:object->wrapper()];
470        if (axElement) {
471            [attrString addAttribute:attribute value:(id)axElement range:range];
472            CFRelease(axElement);
473        }
474    } else
475        [attrString removeAttribute:attribute range:range];
476}
477
478static void AXAttributedStringAppendText(NSMutableAttributedString* attrString, Node* node, int offset, const UChar* chars, int length)
479{
480    // skip invisible text
481    if (!node->renderer())
482        return;
483
484    // easier to calculate the range before appending the string
485    NSRange attrStringRange = NSMakeRange([attrString length], length);
486
487    // append the string from this node
488    [[attrString mutableString] appendString:[NSString stringWithCharacters:chars length:length]];
489
490    // add new attributes and remove irrelevant inherited ones
491    // NOTE: color attributes are handled specially because -[NSMutableAttributedString addAttribute: value: range:] does not merge
492    // identical colors.  Workaround is to not replace an existing color attribute if it matches what we are adding.  This also means
493    // we cannot just pre-remove all inherited attributes on the appended string, so we have to remove the irrelevant ones individually.
494
495    // remove inherited attachment from prior AXAttributedStringAppendReplaced
496    [attrString removeAttribute:NSAccessibilityAttachmentTextAttribute range:attrStringRange];
497
498    // set new attributes
499    AXAttributeStringSetStyle(attrString, node->renderer(), attrStringRange);
500    AXAttributeStringSetHeadingLevel(attrString, node->renderer(), attrStringRange);
501    AXAttributeStringSetBlockquoteLevel(attrString, node->renderer(), attrStringRange);
502    AXAttributeStringSetElement(attrString, NSAccessibilityLinkTextAttribute, AXLinkElementForNode(node), attrStringRange);
503
504    // do spelling last because it tends to break up the range
505    AXAttributeStringSetSpelling(attrString, node, offset, attrStringRange);
506}
507
508static NSString* nsStringForReplacedNode(Node* replacedNode)
509{
510    // we should always be given a rendered node and a replaced node, but be safe
511    // replaced nodes are either attachments (widgets) or images
512    if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode()) {
513        ASSERT_NOT_REACHED();
514        return nil;
515    }
516
517    // create an AX object, but skip it if it is not supposed to be seen
518    RefPtr<AccessibilityObject> obj = replacedNode->renderer()->document()->axObjectCache()->get(replacedNode->renderer());
519    if (obj->accessibilityIsIgnored())
520        return nil;
521
522    // use the attachmentCharacter to represent the replaced node
523    const UniChar attachmentChar = NSAttachmentCharacter;
524    return [NSString stringWithCharacters:&attachmentChar length:1];
525}
526
527- (NSAttributedString*)doAXAttributedStringForTextMarkerRange:(WebCoreTextMarkerRange*)textMarkerRange
528{
529    // extract the start and end VisiblePosition
530    VisiblePosition startVisiblePosition = visiblePositionForStartOfTextMarkerRange(textMarkerRange);
531    if (startVisiblePosition.isNull())
532        return nil;
533
534    VisiblePosition endVisiblePosition = visiblePositionForEndOfTextMarkerRange(textMarkerRange);
535    if (endVisiblePosition.isNull())
536        return nil;
537
538    // iterate over the range to build the AX attributed string
539    NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init];
540    TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get());
541    while (!it.atEnd()) {
542        // locate the node and starting offset for this range
543        int exception = 0;
544        Node* node = it.range()->startContainer(exception);
545        ASSERT(node == it.range()->endContainer(exception));
546        int offset = it.range()->startOffset(exception);
547
548        // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
549        if (it.length() != 0) {
550            AXAttributedStringAppendText(attrString, node, offset, it.characters(), it.length());
551        } else {
552            Node* replacedNode = node->childNode(offset);
553            NSString *attachmentString = nsStringForReplacedNode(replacedNode);
554            if (attachmentString) {
555                NSRange attrStringRange = NSMakeRange([attrString length], [attachmentString length]);
556
557                // append the placeholder string
558                [[attrString mutableString] appendString:attachmentString];
559
560                // remove all inherited attributes
561                [attrString setAttributes:nil range:attrStringRange];
562
563                // add the attachment attribute
564                AccessibilityObject* obj = replacedNode->renderer()->document()->axObjectCache()->get(replacedNode->renderer());
565                AXAttributeStringSetElement(attrString, NSAccessibilityAttachmentTextAttribute, obj, attrStringRange);
566            }
567        }
568        it.advance();
569    }
570
571    return [attrString autorelease];
572}
573
574static WebCoreTextMarkerRange* textMarkerRangeFromVisiblePositions(VisiblePosition startPosition, VisiblePosition endPosition)
575{
576    WebCoreTextMarker* startTextMarker = textMarkerForVisiblePosition(startPosition);
577    WebCoreTextMarker* endTextMarker   = textMarkerForVisiblePosition(endPosition);
578    return textMarkerRangeFromMarkers(startTextMarker, endTextMarker);
579}
580
581- (NSArray*)accessibilityActionNames
582{
583    m_object->updateBackingStore();
584
585    static NSArray* actionElementActions = [[NSArray alloc] initWithObjects: NSAccessibilityPressAction, NSAccessibilityShowMenuAction, nil];
586    static NSArray* defaultElementActions = [[NSArray alloc] initWithObjects: NSAccessibilityShowMenuAction, nil];
587    static NSArray* menuElementActions = [[NSArray alloc] initWithObjects: NSAccessibilityCancelAction, NSAccessibilityPressAction, nil];
588
589    NSArray *actions;
590    if (m_object->actionElement())
591        actions = actionElementActions;
592    else if (m_object->isMenuRelated())
593        actions = menuElementActions;
594    else if (m_object->isAttachment())
595        actions = [[self attachmentView] accessibilityActionNames];
596    else
597        actions = defaultElementActions;
598
599    return actions;
600}
601
602- (NSArray*)accessibilityAttributeNames
603{
604    m_object->updateBackingStore();
605
606    if (m_object->isAttachment())
607        return [[self attachmentView] accessibilityAttributeNames];
608
609    static NSArray* attributes = nil;
610    static NSArray* anchorAttrs = nil;
611    static NSArray* webAreaAttrs = nil;
612    static NSArray* textAttrs = nil;
613    static NSArray* listBoxAttrs = nil;
614    static NSArray* rangeAttrs = nil;
615    static NSArray* commonMenuAttrs = nil;
616    static NSArray* menuAttrs = nil;
617    static NSArray* menuBarAttrs = nil;
618    static NSArray* menuItemAttrs = nil;
619    static NSArray* menuButtonAttrs = nil;
620    static NSArray* controlAttrs = nil;
621    static NSArray* tableAttrs = nil;
622    static NSArray* tableRowAttrs = nil;
623    static NSArray* tableColAttrs = nil;
624    static NSArray* tableCellAttrs = nil;
625    static NSArray* groupAttrs = nil;
626    static NSArray* inputImageAttrs = nil;
627    NSMutableArray* tempArray;
628    if (attributes == nil) {
629        attributes = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute,
630                      NSAccessibilitySubroleAttribute,
631                      NSAccessibilityRoleDescriptionAttribute,
632                      NSAccessibilityChildrenAttribute,
633                      NSAccessibilityHelpAttribute,
634                      NSAccessibilityParentAttribute,
635                      NSAccessibilityPositionAttribute,
636                      NSAccessibilitySizeAttribute,
637                      NSAccessibilityTitleAttribute,
638                      NSAccessibilityDescriptionAttribute,
639                      NSAccessibilityValueAttribute,
640                      NSAccessibilityFocusedAttribute,
641                      NSAccessibilityEnabledAttribute,
642                      NSAccessibilityWindowAttribute,
643                      @"AXSelectedTextMarkerRange",
644                      @"AXStartTextMarker",
645                      @"AXEndTextMarker",
646                      @"AXVisited",
647                      NSAccessibilityLinkedUIElementsAttribute,
648                      NSAccessibilitySelectedAttribute,
649                      NSAccessibilityBlockQuoteLevelAttribute,
650                      NSAccessibilityTopLevelUIElementAttribute,
651                      nil];
652    }
653    if (commonMenuAttrs == nil) {
654        commonMenuAttrs = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute,
655                            NSAccessibilityRoleDescriptionAttribute,
656                            NSAccessibilityChildrenAttribute,
657                            NSAccessibilityParentAttribute,
658                            NSAccessibilityEnabledAttribute,
659                            NSAccessibilityPositionAttribute,
660                            NSAccessibilitySizeAttribute,
661                            nil];
662    }
663    if (anchorAttrs == nil) {
664        tempArray = [[NSMutableArray alloc] initWithArray:attributes];
665        [tempArray addObject:NSAccessibilityURLAttribute];
666        [tempArray addObject:NSAccessibilityAccessKeyAttribute];
667        anchorAttrs = [[NSArray alloc] initWithArray:tempArray];
668        [tempArray release];
669    }
670    if (webAreaAttrs == nil) {
671        tempArray = [[NSMutableArray alloc] initWithArray:attributes];
672        [tempArray addObject:@"AXLinkUIElements"];
673        [tempArray addObject:@"AXLoaded"];
674        [tempArray addObject:@"AXLayoutCount"];
675        [tempArray addObject:NSAccessibilityURLAttribute];
676        webAreaAttrs = [[NSArray alloc] initWithArray:tempArray];
677        [tempArray release];
678    }
679    if (textAttrs == nil) {
680        tempArray = [[NSMutableArray alloc] initWithArray:attributes];
681        [tempArray addObject:NSAccessibilityNumberOfCharactersAttribute];
682        [tempArray addObject:NSAccessibilitySelectedTextAttribute];
683        [tempArray addObject:NSAccessibilitySelectedTextRangeAttribute];
684        [tempArray addObject:NSAccessibilityVisibleCharacterRangeAttribute];
685        [tempArray addObject:NSAccessibilityInsertionPointLineNumberAttribute];
686        [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
687        [tempArray addObject:NSAccessibilityAccessKeyAttribute];
688        textAttrs = [[NSArray alloc] initWithArray:tempArray];
689        [tempArray release];
690    }
691    if (listBoxAttrs == nil) {
692        tempArray = [[NSMutableArray alloc] initWithArray:attributes];
693        [tempArray addObject:NSAccessibilitySelectedChildrenAttribute];
694        [tempArray addObject:NSAccessibilityVisibleChildrenAttribute];
695        [tempArray addObject:NSAccessibilityOrientationAttribute];
696        [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
697        [tempArray addObject:NSAccessibilityAccessKeyAttribute];
698        listBoxAttrs = [[NSArray alloc] initWithArray:tempArray];
699        [tempArray release];
700    }
701    if (rangeAttrs == nil) {
702        tempArray = [[NSMutableArray alloc] initWithArray:attributes];
703        [tempArray addObject:NSAccessibilityTopLevelUIElementAttribute];
704        [tempArray addObject:NSAccessibilityValueAttribute];
705        [tempArray addObject:NSAccessibilityMinValueAttribute];
706        [tempArray addObject:NSAccessibilityMaxValueAttribute];
707        rangeAttrs = [[NSArray alloc] initWithArray:tempArray];
708        [tempArray release];
709    }
710    if (menuBarAttrs == nil) {
711        tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs];
712        [tempArray addObject:NSAccessibilitySelectedChildrenAttribute];
713        [tempArray addObject:NSAccessibilityVisibleChildrenAttribute];
714        [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
715        menuBarAttrs = [[NSArray alloc] initWithArray:tempArray];
716        [tempArray release];
717    }
718    if (menuAttrs == nil) {
719        tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs];
720        [tempArray addObject:NSAccessibilitySelectedChildrenAttribute];
721        [tempArray addObject:NSAccessibilityVisibleChildrenAttribute];
722        [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
723        menuAttrs = [[NSArray alloc] initWithArray:tempArray];
724        [tempArray release];
725    }
726    if (menuItemAttrs == nil) {
727        tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs];
728        [tempArray addObject:NSAccessibilityTitleAttribute];
729        [tempArray addObject:NSAccessibilityHelpAttribute];
730        [tempArray addObject:NSAccessibilitySelectedAttribute];
731        [tempArray addObject:(NSString*)kAXMenuItemCmdCharAttribute];
732        [tempArray addObject:(NSString*)kAXMenuItemCmdVirtualKeyAttribute];
733        [tempArray addObject:(NSString*)kAXMenuItemCmdGlyphAttribute];
734        [tempArray addObject:(NSString*)kAXMenuItemCmdModifiersAttribute];
735        [tempArray addObject:(NSString*)kAXMenuItemMarkCharAttribute];
736        [tempArray addObject:(NSString*)kAXMenuItemPrimaryUIElementAttribute];
737        [tempArray addObject:NSAccessibilityServesAsTitleForUIElementsAttribute];
738        menuItemAttrs = [[NSArray alloc] initWithArray:tempArray];
739        [tempArray release];
740    }
741    if (menuButtonAttrs == nil) {
742        menuButtonAttrs = [[NSArray alloc] initWithObjects:NSAccessibilityRoleAttribute,
743            NSAccessibilityRoleDescriptionAttribute,
744            NSAccessibilityParentAttribute,
745            NSAccessibilityPositionAttribute,
746            NSAccessibilitySizeAttribute,
747            NSAccessibilityWindowAttribute,
748            NSAccessibilityTopLevelUIElementAttribute,
749            NSAccessibilityEnabledAttribute,
750            NSAccessibilityFocusedAttribute,
751            NSAccessibilityTitleAttribute,
752            NSAccessibilityChildrenAttribute, nil];
753    }
754    if (controlAttrs == nil) {
755        tempArray = [[NSMutableArray alloc] initWithArray:attributes];
756        [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
757        [tempArray addObject:NSAccessibilityAccessKeyAttribute];
758        controlAttrs = [[NSArray alloc] initWithArray:tempArray];
759        [tempArray release];
760    }
761    if (tableAttrs == nil) {
762        tempArray = [[NSMutableArray alloc] initWithArray:attributes];
763        [tempArray addObject:NSAccessibilityRowsAttribute];
764        [tempArray addObject:NSAccessibilityVisibleRowsAttribute];
765        [tempArray addObject:NSAccessibilityColumnsAttribute];
766        [tempArray addObject:NSAccessibilityVisibleColumnsAttribute];
767        [tempArray addObject:NSAccessibilityVisibleCellsAttribute];
768        [tempArray addObject:(NSString *)kAXColumnHeaderUIElementsAttribute];
769        [tempArray addObject:NSAccessibilityRowHeaderUIElementsAttribute];
770        [tempArray addObject:NSAccessibilityHeaderAttribute];
771        tableAttrs = [[NSArray alloc] initWithArray:tempArray];
772        [tempArray release];
773    }
774    if (tableRowAttrs == nil) {
775        tempArray = [[NSMutableArray alloc] initWithArray:attributes];
776        [tempArray addObject:NSAccessibilityIndexAttribute];
777        tableRowAttrs = [[NSArray alloc] initWithArray:tempArray];
778        [tempArray release];
779    }
780    if (tableColAttrs == nil) {
781        tempArray = [[NSMutableArray alloc] initWithArray:attributes];
782        [tempArray addObject:NSAccessibilityIndexAttribute];
783        [tempArray addObject:NSAccessibilityHeaderAttribute];
784        [tempArray addObject:NSAccessibilityRowsAttribute];
785        [tempArray addObject:NSAccessibilityVisibleRowsAttribute];
786        tableColAttrs = [[NSArray alloc] initWithArray:tempArray];
787        [tempArray release];
788    }
789    if (tableCellAttrs == nil) {
790        tempArray = [[NSMutableArray alloc] initWithArray:attributes];
791        [tempArray addObject:NSAccessibilityRowIndexRangeAttribute];
792        [tempArray addObject:NSAccessibilityColumnIndexRangeAttribute];
793        tableCellAttrs = [[NSArray alloc] initWithArray:tempArray];
794        [tempArray release];
795    }
796    if (groupAttrs == nil) {
797        tempArray = [[NSMutableArray alloc] initWithArray:attributes];
798        [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
799        groupAttrs = [[NSArray alloc] initWithArray:tempArray];
800        [tempArray release];
801    }
802    if (inputImageAttrs == nil) {
803        tempArray = [[NSMutableArray alloc] initWithArray:controlAttrs];
804        [tempArray addObject:NSAccessibilityURLAttribute];
805        [tempArray addObject:NSAccessibilityAccessKeyAttribute];
806        inputImageAttrs = [[NSArray alloc] initWithArray:tempArray];
807        [tempArray release];
808    }
809
810    if (m_object->isPasswordField())
811        return attributes;
812
813    if (m_object->isWebArea())
814        return webAreaAttrs;
815
816    if (m_object->isTextControl())
817        return textAttrs;
818
819    if (m_object->isAnchor() || m_object->isImage())
820        return anchorAttrs;
821
822    if (m_object->isDataTable())
823        return tableAttrs;
824    if (m_object->isTableRow())
825        return tableRowAttrs;
826    if (m_object->isTableColumn())
827        return tableColAttrs;
828    if (m_object->isTableCell())
829        return tableCellAttrs;
830
831    if (m_object->isListBox() || m_object->isList())
832        return listBoxAttrs;
833
834    if (m_object->isProgressIndicator() || m_object->isSlider())
835        return rangeAttrs;
836
837    if (m_object->isInputImage())
838        return inputImageAttrs;
839
840    if (m_object->isControl())
841        return controlAttrs;
842
843    if (m_object->isGroup())
844        return groupAttrs;
845
846    if (m_object->isMenu())
847        return menuAttrs;
848    if (m_object->isMenuBar())
849        return menuBarAttrs;
850    if (m_object->isMenuButton())
851        return menuButtonAttrs;
852    if (m_object->isMenuItem())
853        return menuItemAttrs;
854
855    return attributes;
856}
857
858- (VisiblePositionRange)visiblePositionRangeForTextMarkerRange:(WebCoreTextMarkerRange*) textMarkerRange
859{
860    return VisiblePositionRange(visiblePositionForStartOfTextMarkerRange(textMarkerRange), visiblePositionForEndOfTextMarkerRange(textMarkerRange));
861}
862
863- (NSArray*)renderWidgetChildren
864{
865    Widget* widget = m_object->widget();
866    if (!widget)
867        return nil;
868    return [(widget->platformWidget()) accessibilityAttributeValue: NSAccessibilityChildrenAttribute];
869}
870
871static void convertToVector(NSArray* array, AccessibilityObject::AccessibilityChildrenVector& vector)
872{
873    unsigned length = [array count];
874    vector.reserveCapacity(length);
875    for (unsigned i = 0; i < length; ++i) {
876        AccessibilityObject* obj = [[array objectAtIndex:i] accessibilityObject];
877        if (obj)
878            vector.append(obj);
879    }
880}
881
882static NSMutableArray* convertToNSArray(const AccessibilityObject::AccessibilityChildrenVector& vector)
883{
884    unsigned length = vector.size();
885    NSMutableArray* array = [NSMutableArray arrayWithCapacity: length];
886    for (unsigned i = 0; i < length; ++i) {
887        ASSERT(vector[i]->wrapper());
888        if (vector[i]->wrapper())
889            [array addObject:vector[i]->wrapper()];
890    }
891    return array;
892}
893
894- (WebCoreTextMarkerRange*)textMarkerRangeForSelection
895{
896    Selection selection = m_object->selection();
897    if (selection.isNone())
898        return nil;
899    return textMarkerRangeFromVisiblePositions(selection.visibleStart(), selection.visibleEnd());
900}
901
902- (NSValue*)position
903{
904    IntRect rect = m_object->elementRect();
905
906    // The Cocoa accessibility API wants the lower-left corner.
907    NSPoint point = NSMakePoint(rect.x(), rect.bottom());
908    FrameView* frameView = m_object->documentFrameView();
909    if (frameView) {
910        NSView* view = frameView->documentView();
911        point = [[view window] convertBaseToScreen: [view convertPoint: point toView:nil]];
912    }
913
914    return [NSValue valueWithPoint: point];
915}
916
917typedef HashMap<int, NSString*> AccessibilityRoleMap;
918
919static const AccessibilityRoleMap& createAccessibilityRoleMap()
920{
921    struct RoleEntry {
922        AccessibilityRole value;
923        NSString* string;
924    };
925
926    static const RoleEntry roles[] = {
927        { UnknownRole, NSAccessibilityUnknownRole },
928        { ButtonRole, NSAccessibilityButtonRole },
929        { RadioButtonRole, NSAccessibilityRadioButtonRole },
930        { CheckBoxRole, NSAccessibilityCheckBoxRole },
931        { SliderRole, NSAccessibilitySliderRole },
932        { TabGroupRole, NSAccessibilityTabGroupRole },
933        { TextFieldRole, NSAccessibilityTextFieldRole },
934        { StaticTextRole, NSAccessibilityStaticTextRole },
935        { TextAreaRole, NSAccessibilityTextAreaRole },
936        { ScrollAreaRole, NSAccessibilityScrollAreaRole },
937        { PopUpButtonRole, NSAccessibilityPopUpButtonRole },
938        { MenuButtonRole, NSAccessibilityMenuButtonRole },
939        { TableRole, NSAccessibilityTableRole },
940        { ApplicationRole, NSAccessibilityApplicationRole },
941        { GroupRole, NSAccessibilityGroupRole },
942        { RadioGroupRole, NSAccessibilityRadioGroupRole },
943        { ListRole, NSAccessibilityListRole },
944        { ScrollBarRole, NSAccessibilityScrollBarRole },
945        { ValueIndicatorRole, NSAccessibilityValueIndicatorRole },
946        { ImageRole, NSAccessibilityImageRole },
947        { MenuBarRole, NSAccessibilityMenuBarRole },
948        { MenuRole, NSAccessibilityMenuRole },
949        { MenuItemRole, NSAccessibilityMenuItemRole },
950        { ColumnRole, NSAccessibilityColumnRole },
951        { RowRole, NSAccessibilityRowRole },
952        { ToolbarRole, NSAccessibilityToolbarRole },
953        { BusyIndicatorRole, NSAccessibilityBusyIndicatorRole },
954        { ProgressIndicatorRole, NSAccessibilityProgressIndicatorRole },
955        { WindowRole, NSAccessibilityWindowRole },
956        { DrawerRole, NSAccessibilityDrawerRole },
957        { SystemWideRole, NSAccessibilitySystemWideRole },
958        { OutlineRole, NSAccessibilityOutlineRole },
959        { IncrementorRole, NSAccessibilityIncrementorRole },
960        { BrowserRole, NSAccessibilityBrowserRole },
961        { ComboBoxRole, NSAccessibilityComboBoxRole },
962        { SplitGroupRole, NSAccessibilitySplitGroupRole },
963        { SplitterRole, NSAccessibilitySplitterRole },
964        { ColorWellRole, NSAccessibilityColorWellRole },
965        { GrowAreaRole, NSAccessibilityGrowAreaRole },
966        { SheetRole, NSAccessibilitySheetRole },
967        { HelpTagRole, NSAccessibilityHelpTagRole },
968        { MatteRole, NSAccessibilityMatteRole },
969        { RulerRole, NSAccessibilityRulerRole },
970        { RulerMarkerRole, NSAccessibilityRulerMarkerRole },
971        { LinkRole, NSAccessibilityLinkRole },
972#ifndef BUILDING_ON_TIGER
973        { DisclosureTriangleRole, NSAccessibilityDisclosureTriangleRole },
974        { GridRole, NSAccessibilityGridRole },
975#endif
976        { WebCoreLinkRole, NSAccessibilityLinkRole },
977        { ImageMapLinkRole, NSAccessibilityLinkRole },
978        { ImageMapRole, @"AXImageMap" },
979        { ListMarkerRole, @"AXListMarker" },
980        { WebAreaRole, @"AXWebArea" },
981        { HeadingRole, @"AXHeading" },
982        { ListBoxRole, NSAccessibilityListRole },
983        { ListBoxOptionRole, NSAccessibilityStaticTextRole },
984        // cells don't exist on tiger or leopard
985#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)
986        { CellRole, NSAccessibilityGroupRole },
987#else
988        { CellRole, NSAccessibilityCellRole },
989#endif
990        { TableHeaderContainerRole, NSAccessibilityGroupRole },
991        { DefinitionListDefinitionRole, NSAccessibilityGroupRole },
992        { DefinitionListTermRole, NSAccessibilityGroupRole }
993
994    };
995    AccessibilityRoleMap& roleMap = *new AccessibilityRoleMap;
996
997    const unsigned numRoles = sizeof(roles) / sizeof(roles[0]);
998    for (unsigned i = 0; i < numRoles; ++i)
999        roleMap.set(roles[i].value, roles[i].string);
1000    return roleMap;
1001}
1002
1003static NSString* roleValueToNSString(AccessibilityRole value)
1004{
1005    ASSERT(value);
1006    static const AccessibilityRoleMap& roleMap = createAccessibilityRoleMap();
1007    return roleMap.get(value);
1008}
1009
1010- (NSString*)role
1011{
1012    if (m_object->isAttachment())
1013        return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleAttribute];
1014    NSString* string = roleValueToNSString(m_object->roleValue());
1015    if (string != nil)
1016        return string;
1017    return NSAccessibilityUnknownRole;
1018}
1019
1020- (NSString*)subrole
1021{
1022    if (m_object->isPasswordField())
1023        return NSAccessibilitySecureTextFieldSubrole;
1024
1025    if (m_object->isAttachment()) {
1026        NSView* attachView = [self attachmentView];
1027        if ([[attachView accessibilityAttributeNames] containsObject:NSAccessibilitySubroleAttribute]) {
1028            return [attachView accessibilityAttributeValue:NSAccessibilitySubroleAttribute];
1029        }
1030    }
1031
1032    if (m_object->isList()) {
1033        AccessibilityList* listObject = static_cast<AccessibilityList*>(m_object);
1034        if (listObject->isUnorderedList() || listObject->isOrderedList())
1035            return NSAccessibilityContentListSubrole;
1036        if (listObject->isDefinitionList())
1037            return NSAccessibilityDefinitionListSubrole;
1038    }
1039
1040    return nil;
1041}
1042
1043- (NSString*)roleDescription
1044{
1045    if (!m_object)
1046        return nil;
1047
1048    // attachments have the AXImage role, but a different subrole
1049    if (m_object->isAttachment())
1050        return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleDescriptionAttribute];
1051
1052    // FIXME 3447564: It would be better to call some AppKit API to get these strings
1053    // (which would be the best way to localize them)
1054
1055    NSString* axRole = [self role];
1056    if ([axRole isEqualToString:NSAccessibilityButtonRole])
1057        return NSAccessibilityRoleDescription(NSAccessibilityButtonRole, [self subrole]);
1058
1059    if ([axRole isEqualToString:NSAccessibilityPopUpButtonRole])
1060        return NSAccessibilityRoleDescription(NSAccessibilityPopUpButtonRole, [self subrole]);
1061
1062    if ([axRole isEqualToString:NSAccessibilityStaticTextRole])
1063        return NSAccessibilityRoleDescription(NSAccessibilityStaticTextRole, [self subrole]);
1064
1065    if ([axRole isEqualToString:NSAccessibilityImageRole])
1066        return NSAccessibilityRoleDescription(NSAccessibilityImageRole, [self subrole]);
1067
1068    if ([axRole isEqualToString:NSAccessibilityGroupRole])
1069        return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, [self subrole]);
1070
1071    if ([axRole isEqualToString:NSAccessibilityCheckBoxRole])
1072        return NSAccessibilityRoleDescription(NSAccessibilityCheckBoxRole, [self subrole]);
1073
1074    if ([axRole isEqualToString:NSAccessibilityRadioButtonRole])
1075        return NSAccessibilityRoleDescription(NSAccessibilityRadioButtonRole, [self subrole]);
1076
1077    if ([axRole isEqualToString:NSAccessibilityTextFieldRole])
1078        return NSAccessibilityRoleDescription(NSAccessibilityTextFieldRole, [self subrole]);
1079
1080    if ([axRole isEqualToString:NSAccessibilityTextAreaRole])
1081        return NSAccessibilityRoleDescription(NSAccessibilityTextAreaRole, [self subrole]);
1082
1083    if ([axRole isEqualToString:NSAccessibilityListRole])
1084        return NSAccessibilityRoleDescription(NSAccessibilityListRole, [self subrole]);
1085
1086    if ([axRole isEqualToString:NSAccessibilityTableRole])
1087        return NSAccessibilityRoleDescription(NSAccessibilityTableRole, [self subrole]);
1088
1089    if ([axRole isEqualToString:NSAccessibilityRowRole])
1090        return NSAccessibilityRoleDescription(NSAccessibilityRowRole, [self subrole]);
1091
1092    if ([axRole isEqualToString:NSAccessibilityColumnRole])
1093        return NSAccessibilityRoleDescription(NSAccessibilityColumnRole, [self subrole]);
1094
1095    if ([axRole isEqualToString:NSAccessibilityCellRole])
1096        return NSAccessibilityRoleDescription(NSAccessibilityCellRole, [self subrole]);
1097
1098    if ([axRole isEqualToString:@"AXWebArea"])
1099        return AXWebAreaText();
1100
1101    if ([axRole isEqualToString:@"AXLink"])
1102        return AXLinkText();
1103
1104    if ([axRole isEqualToString:@"AXListMarker"])
1105        return AXListMarkerText();
1106
1107    if ([axRole isEqualToString:@"AXImageMap"])
1108        return AXImageMapText();
1109
1110    if ([axRole isEqualToString:@"AXHeading"])
1111        return AXHeadingText();
1112
1113    if ([axRole isEqualToString:(NSString*)kAXMenuBarItemRole] ||
1114        [axRole isEqualToString:NSAccessibilityMenuRole])
1115        return nil;
1116
1117    if ([axRole isEqualToString:NSAccessibilityMenuButtonRole])
1118        return NSAccessibilityRoleDescription(NSAccessibilityMenuButtonRole, [self subrole]);
1119
1120    return NSAccessibilityRoleDescription(NSAccessibilityUnknownRole, nil);
1121}
1122
1123// FIXME: split up this function in a better way.
1124// suggestions: Use a hash table that maps attribute names to function calls,
1125// or maybe pointers to member functions
1126- (id)accessibilityAttributeValue:(NSString*)attributeName
1127{
1128    if (!m_object)
1129        return nil;
1130
1131    m_object->updateBackingStore();
1132
1133    if ([attributeName isEqualToString: NSAccessibilityRoleAttribute])
1134        return [self role];
1135
1136    if ([attributeName isEqualToString: NSAccessibilitySubroleAttribute])
1137        return [self subrole];
1138
1139    if ([attributeName isEqualToString: NSAccessibilityRoleDescriptionAttribute])
1140        return [self roleDescription];
1141
1142    if ([attributeName isEqualToString: NSAccessibilityParentAttribute]) {
1143        if (m_object->isAccessibilityRenderObject()) {
1144            FrameView* fv = static_cast<AccessibilityRenderObject*>(m_object)->frameViewIfRenderView();
1145            if (fv)
1146                return fv->platformWidget();
1147        }
1148
1149        return m_object->parentObjectUnignored()->wrapper();
1150    }
1151
1152    if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
1153        if (m_object->children().isEmpty()) {
1154            NSArray* children = [self renderWidgetChildren];
1155            if (children != nil)
1156                return children;
1157        }
1158        return convertToNSArray(m_object->children());
1159    }
1160
1161    if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute]) {
1162        if (m_object->isListBox()) {
1163            AccessibilityObject::AccessibilityChildrenVector selectedChildrenCopy;
1164            m_object->selectedChildren(selectedChildrenCopy);
1165            return convertToNSArray(selectedChildrenCopy);
1166        }
1167        return nil;
1168    }
1169
1170    if ([attributeName isEqualToString: NSAccessibilityVisibleChildrenAttribute]) {
1171        if (m_object->isListBox()) {
1172            AccessibilityObject::AccessibilityChildrenVector visibleChildrenCopy;
1173            m_object->visibleChildren(visibleChildrenCopy);
1174            return convertToNSArray(visibleChildrenCopy);
1175        }
1176        else if (m_object->isList())
1177            return [self accessibilityAttributeValue:NSAccessibilityChildrenAttribute];
1178
1179        return nil;
1180    }
1181
1182
1183    if (m_object->isWebArea()) {
1184        if ([attributeName isEqualToString: @"AXLinkUIElements"]) {
1185            AccessibilityObject::AccessibilityChildrenVector links;
1186            static_cast<AccessibilityRenderObject*>(m_object)->getDocumentLinks(links);
1187            return convertToNSArray(links);
1188        }
1189        if ([attributeName isEqualToString: @"AXLoaded"])
1190            return [NSNumber numberWithBool: m_object->isLoaded()];
1191        if ([attributeName isEqualToString: @"AXLayoutCount"])
1192            return [NSNumber numberWithInt: m_object->layoutCount()];
1193    }
1194
1195    if (m_object->isTextControl()) {
1196        if ([attributeName isEqualToString: NSAccessibilityNumberOfCharactersAttribute]) {
1197            int length = m_object->textLength();
1198            if (length < 0)
1199                return nil;
1200            return [NSNumber numberWithUnsignedInt:length];
1201        }
1202        if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) {
1203            String selectedText = m_object->selectedText();
1204            if (selectedText.isNull())
1205                return nil;
1206            return (NSString*)selectedText;
1207        }
1208        if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) {
1209            PlainTextRange textRange = m_object->selectedTextRange();
1210            if (textRange.isNull())
1211                return nil;
1212            return [NSValue valueWithRange:NSMakeRange(textRange.start, textRange.length)];
1213        }
1214        // TODO: Get actual visible range. <rdar://problem/4712101>
1215        if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute])
1216            return m_object->isPasswordField() ? nil : [NSValue valueWithRange: NSMakeRange(0, m_object->textLength())];
1217        if ([attributeName isEqualToString: NSAccessibilityInsertionPointLineNumberAttribute]) {
1218            // if selectionEnd > 0, then there is selected text and this question should not be answered
1219            if (m_object->isPasswordField() || m_object->selectionEnd() > 0)
1220                return nil;
1221            int lineNumber = m_object->lineForPosition(m_object->visiblePositionForIndex(m_object->selectionStart(), true));
1222            if (lineNumber < 0)
1223                return nil;
1224            return [NSNumber numberWithInt:lineNumber];
1225        }
1226    }
1227
1228    if ([attributeName isEqualToString: NSAccessibilityURLAttribute]) {
1229        KURL url = m_object->url();
1230        if (url.isNull())
1231            return nil;
1232        return (NSURL*)url;
1233    }
1234
1235    if ([attributeName isEqualToString: @"AXVisited"])
1236        return [NSNumber numberWithBool: m_object->isVisited()];
1237
1238    if ([attributeName isEqualToString: NSAccessibilityTitleAttribute]) {
1239        if (m_object->isAttachment()) {
1240            if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityTitleAttribute])
1241                return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityTitleAttribute];
1242        }
1243        return m_object->title();
1244    }
1245
1246    if ([attributeName isEqualToString: NSAccessibilityDescriptionAttribute]) {
1247        if (m_object->isAttachment()) {
1248            if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityDescriptionAttribute])
1249                return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityDescriptionAttribute];
1250        }
1251        return m_object->accessibilityDescription();
1252    }
1253
1254    if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) {
1255        if (m_object->isAttachment()) {
1256            if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityValueAttribute])
1257                return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityValueAttribute];
1258        }
1259        if (m_object->isProgressIndicator() || m_object->isSlider())
1260            return [NSNumber numberWithFloat:m_object->valueForRange()];
1261        if (m_object->hasIntValue())
1262            return [NSNumber numberWithInt:m_object->intValue()];
1263        return m_object->stringValue();
1264    }
1265
1266    if ([attributeName isEqualToString: NSAccessibilityMinValueAttribute])
1267        return [NSNumber numberWithFloat:m_object->minValueForRange()];
1268
1269    if ([attributeName isEqualToString: NSAccessibilityMaxValueAttribute])
1270        return [NSNumber numberWithFloat:m_object->maxValueForRange()];
1271
1272    if ([attributeName isEqualToString: NSAccessibilityHelpAttribute])
1273        return m_object->helpText();
1274
1275    if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute])
1276        return [NSNumber numberWithBool: m_object->isFocused()];
1277
1278    if ([attributeName isEqualToString: NSAccessibilityEnabledAttribute])
1279        return [NSNumber numberWithBool: m_object->isEnabled()];
1280
1281    if ([attributeName isEqualToString: NSAccessibilitySizeAttribute]) {
1282        IntSize s = m_object->size();
1283        return [NSValue valueWithSize: NSMakeSize(s.width(), s.height())];
1284    }
1285
1286    if ([attributeName isEqualToString: NSAccessibilityPositionAttribute])
1287        return [self position];
1288
1289    if ([attributeName isEqualToString: NSAccessibilityWindowAttribute] ||
1290        [attributeName isEqualToString: NSAccessibilityTopLevelUIElementAttribute]) {
1291        FrameView* fv = m_object->documentFrameView();
1292        if (fv)
1293            return [fv->platformWidget() window];
1294        return nil;
1295    }
1296
1297    if ([attributeName isEqualToString:NSAccessibilityAccessKeyAttribute]) {
1298        AtomicString accessKey = m_object->accessKey();
1299        if (accessKey.isNull())
1300            return nil;
1301        return accessKey;
1302    }
1303
1304    if (m_object->isDataTable()) {
1305        // TODO: distinguish between visible and non-visible rows
1306        if ([attributeName isEqualToString:NSAccessibilityRowsAttribute] ||
1307            [attributeName isEqualToString:NSAccessibilityVisibleRowsAttribute]) {
1308            return convertToNSArray(static_cast<AccessibilityTable*>(m_object)->rows());
1309        }
1310        // TODO: distinguish between visible and non-visible columns
1311        if ([attributeName isEqualToString:NSAccessibilityColumnsAttribute] ||
1312            [attributeName isEqualToString:NSAccessibilityVisibleColumnsAttribute]) {
1313            return convertToNSArray(static_cast<AccessibilityTable*>(m_object)->columns());
1314        }
1315
1316        // HTML tables don't support these
1317        if ([attributeName isEqualToString:NSAccessibilitySelectedColumnsAttribute] ||
1318            [attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute] ||
1319            [attributeName isEqualToString:NSAccessibilitySelectedCellsAttribute])
1320            return nil;
1321
1322        if ([attributeName isEqualToString:(NSString *)kAXColumnHeaderUIElementsAttribute]) {
1323            AccessibilityObject::AccessibilityChildrenVector columnHeaders;
1324            static_cast<AccessibilityTable*>(m_object)->columnHeaders(columnHeaders);
1325            return convertToNSArray(columnHeaders);
1326        }
1327
1328        if ([attributeName isEqualToString:NSAccessibilityHeaderAttribute]) {
1329            AccessibilityObject* headerContainer = static_cast<AccessibilityTable*>(m_object)->headerContainer();
1330            if (headerContainer)
1331                return headerContainer->wrapper();
1332            return nil;
1333        }
1334
1335        if ([attributeName isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) {
1336            AccessibilityObject::AccessibilityChildrenVector rowHeaders;
1337            static_cast<AccessibilityTable*>(m_object)->rowHeaders(rowHeaders);
1338            return convertToNSArray(rowHeaders);
1339        }
1340
1341        if ([attributeName isEqualToString:NSAccessibilityVisibleCellsAttribute]) {
1342            AccessibilityObject::AccessibilityChildrenVector cells;
1343            static_cast<AccessibilityTable*>(m_object)->cells(cells);
1344            return convertToNSArray(cells);
1345        }
1346    }
1347
1348    if (m_object->isTableRow()) {
1349        if ([attributeName isEqualToString:NSAccessibilityIndexAttribute])
1350            return [NSNumber numberWithInt:static_cast<AccessibilityTableRow*>(m_object)->rowIndex()];
1351    }
1352
1353    if (m_object->isTableColumn()) {
1354        if ([attributeName isEqualToString:NSAccessibilityIndexAttribute])
1355            return [NSNumber numberWithInt:static_cast<AccessibilityTableColumn*>(m_object)->columnIndex()];
1356
1357        // rows attribute for a column is the list of all the elements in that column at each row
1358        if ([attributeName isEqualToString:NSAccessibilityRowsAttribute] ||
1359            [attributeName isEqualToString:NSAccessibilityVisibleRowsAttribute]) {
1360            return convertToNSArray(static_cast<AccessibilityTableColumn*>(m_object)->children());
1361        }
1362        if ([attributeName isEqualToString:NSAccessibilityHeaderAttribute]) {
1363            AccessibilityObject* header = static_cast<AccessibilityTableColumn*>(m_object)->headerObject();
1364            if (!header)
1365                return nil;
1366            return header->wrapper();
1367        }
1368    }
1369
1370    if (m_object->isTableCell()) {
1371        if ([attributeName isEqualToString:NSAccessibilityRowIndexRangeAttribute]) {
1372            pair<int, int> rowRange;
1373            static_cast<AccessibilityTableCell*>(m_object)->rowIndexRange(rowRange);
1374            return [NSValue valueWithRange:NSMakeRange(rowRange.first, rowRange.second)];
1375        }
1376        if ([attributeName isEqualToString:NSAccessibilityColumnIndexRangeAttribute]) {
1377            pair<int, int> columnRange;
1378            static_cast<AccessibilityTableCell*>(m_object)->columnIndexRange(columnRange);
1379            return [NSValue valueWithRange:NSMakeRange(columnRange.first, columnRange.second)];
1380        }
1381    }
1382
1383    if ((m_object->isListBox() ||m_object->isList()) && [attributeName isEqualToString:NSAccessibilityOrientationAttribute])
1384        return NSAccessibilityVerticalOrientationValue;
1385
1386    if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"])
1387        return [self textMarkerRangeForSelection];
1388
1389    if (m_object->isAccessibilityRenderObject()) {
1390        RenderObject* renderer = static_cast<AccessibilityRenderObject*>(m_object)->renderer();
1391        if (!renderer)
1392            return nil;
1393
1394        if ([attributeName isEqualToString: @"AXStartTextMarker"])
1395            return textMarkerForVisiblePosition(startOfDocument(renderer->document()));
1396        if ([attributeName isEqualToString: @"AXEndTextMarker"])
1397            return textMarkerForVisiblePosition(endOfDocument(renderer->document()));
1398
1399        if ([attributeName isEqualToString:NSAccessibilityBlockQuoteLevelAttribute])
1400            return [NSNumber numberWithInt:blockquoteLevel(renderer)];
1401    } else {
1402        if ([attributeName isEqualToString:NSAccessibilityBlockQuoteLevelAttribute]) {
1403            AccessibilityObject* parent = m_object->parentObjectUnignored();
1404            if (!parent)
1405                return [NSNumber numberWithInt:0];
1406            return [parent->wrapper() accessibilityAttributeValue:NSAccessibilityBlockQuoteLevelAttribute];
1407        }
1408    }
1409
1410    if ([attributeName isEqualToString: NSAccessibilityLinkedUIElementsAttribute]) {
1411        AccessibilityObject::AccessibilityChildrenVector linkedUIElements;
1412        m_object->linkedUIElements(linkedUIElements);
1413        if (linkedUIElements.size() == 0)
1414            return nil;
1415        return convertToNSArray(linkedUIElements);
1416    }
1417
1418    if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute])
1419        return [NSNumber numberWithBool:m_object->isSelected()];
1420
1421    if ([attributeName isEqualToString: NSAccessibilityServesAsTitleForUIElementsAttribute] && m_object->isMenuButton()) {
1422        AccessibilityObject* uiElement = static_cast<AccessibilityRenderObject*>(m_object)->menuForMenuButton();
1423        if (uiElement)
1424            return [NSArray arrayWithObject:uiElement->wrapper()];
1425    }
1426
1427    if ([attributeName isEqualToString:NSAccessibilityTitleUIElementAttribute]) {
1428        AccessibilityObject* obj = m_object->titleUIElement();
1429        if (obj)
1430            return obj->wrapper();
1431        return nil;
1432    }
1433
1434    return nil;
1435}
1436
1437- (id)accessibilityFocusedUIElement
1438{
1439    m_object->updateBackingStore();
1440
1441    RefPtr<AccessibilityObject> focusedObj = m_object->focusedUIElement();
1442
1443    if (!focusedObj)
1444        return nil;
1445
1446    return focusedObj->wrapper();
1447}
1448
1449- (id)accessibilityHitTest:(NSPoint)point
1450{
1451    m_object->updateBackingStore();
1452
1453    RefPtr<AccessibilityObject> axObject = m_object->doAccessibilityHitTest(IntPoint(point));
1454    if (axObject)
1455        return NSAccessibilityUnignoredAncestor(axObject->wrapper());
1456    return NSAccessibilityUnignoredAncestor(self);
1457}
1458
1459- (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName
1460{
1461    m_object->updateBackingStore();
1462
1463    if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"])
1464        return YES;
1465
1466    if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute])
1467        return m_object->canSetFocusAttribute();
1468
1469    if ([attributeName isEqualToString: NSAccessibilityValueAttribute])
1470        return m_object->canSetValueAttribute();
1471
1472    if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute])
1473        return m_object->canSetSelectedAttribute();
1474
1475    if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute])
1476        return m_object->canSetSelectedChildrenAttribute();
1477
1478    if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute] ||
1479        [attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute] ||
1480        [attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute])
1481        return m_object->canSetTextRangeAttributes();
1482
1483    return NO;
1484}
1485
1486// accessibilityShouldUseUniqueId is an AppKit method we override so that
1487// objects will be given a unique ID, and therefore allow AppKit to know when they
1488// become obsolete (e.g. when the user navigates to a new web page, making this one
1489// unrendered but not deallocated because it is in the back/forward cache).
1490// It is important to call NSAccessibilityUnregisterUniqueIdForUIElement in the
1491// appropriate place (e.g. dealloc) to remove these non-retained references from
1492// AppKit's id mapping tables. We do this in detach by calling unregisterUniqueIdForUIElement.
1493//
1494// Registering an object is also required for observing notifications. Only registered objects can be observed.
1495- (BOOL)accessibilityIsIgnored
1496{
1497    m_object->updateBackingStore();
1498
1499    if (m_object->isAttachment())
1500        return [[self attachmentView] accessibilityIsIgnored];
1501    return m_object->accessibilityIsIgnored();
1502}
1503
1504- (NSArray* )accessibilityParameterizedAttributeNames
1505{
1506    m_object->updateBackingStore();
1507
1508    if (m_object->isAttachment())
1509        return nil;
1510
1511    static NSArray* paramAttrs = nil;
1512    static NSArray* textParamAttrs = nil;
1513    static NSArray* tableParamAttrs = nil;
1514    if (paramAttrs == nil) {
1515        paramAttrs = [[NSArray alloc] initWithObjects:
1516                      @"AXUIElementForTextMarker",
1517                      @"AXTextMarkerRangeForUIElement",
1518                      @"AXLineForTextMarker",
1519                      @"AXTextMarkerRangeForLine",
1520                      @"AXStringForTextMarkerRange",
1521                      @"AXTextMarkerForPosition",
1522                      @"AXBoundsForTextMarkerRange",
1523                      @"AXAttributedStringForTextMarkerRange",
1524                      @"AXTextMarkerRangeForUnorderedTextMarkers",
1525                      @"AXNextTextMarkerForTextMarker",
1526                      @"AXPreviousTextMarkerForTextMarker",
1527                      @"AXLeftWordTextMarkerRangeForTextMarker",
1528                      @"AXRightWordTextMarkerRangeForTextMarker",
1529                      @"AXLeftLineTextMarkerRangeForTextMarker",
1530                      @"AXRightLineTextMarkerRangeForTextMarker",
1531                      @"AXSentenceTextMarkerRangeForTextMarker",
1532                      @"AXParagraphTextMarkerRangeForTextMarker",
1533                      @"AXNextWordEndTextMarkerForTextMarker",
1534                      @"AXPreviousWordStartTextMarkerForTextMarker",
1535                      @"AXNextLineEndTextMarkerForTextMarker",
1536                      @"AXPreviousLineStartTextMarkerForTextMarker",
1537                      @"AXNextSentenceEndTextMarkerForTextMarker",
1538                      @"AXPreviousSentenceStartTextMarkerForTextMarker",
1539                      @"AXNextParagraphEndTextMarkerForTextMarker",
1540                      @"AXPreviousParagraphStartTextMarkerForTextMarker",
1541                      @"AXStyleTextMarkerRangeForTextMarker",
1542                      @"AXLengthForTextMarkerRange",
1543                      NSAccessibilityBoundsForRangeParameterizedAttribute,
1544                      nil];
1545    }
1546
1547    if (textParamAttrs == nil) {
1548        NSMutableArray* tempArray = [[NSMutableArray alloc] initWithArray:paramAttrs];
1549        [tempArray addObject:(NSString*)kAXLineForIndexParameterizedAttribute];
1550        [tempArray addObject:(NSString*)kAXRangeForLineParameterizedAttribute];
1551        [tempArray addObject:(NSString*)kAXStringForRangeParameterizedAttribute];
1552        [tempArray addObject:(NSString*)kAXRangeForPositionParameterizedAttribute];
1553        [tempArray addObject:(NSString*)kAXRangeForIndexParameterizedAttribute];
1554        [tempArray addObject:(NSString*)kAXBoundsForRangeParameterizedAttribute];
1555        [tempArray addObject:(NSString*)kAXRTFForRangeParameterizedAttribute];
1556        [tempArray addObject:(NSString*)kAXAttributedStringForRangeParameterizedAttribute];
1557        [tempArray addObject:(NSString*)kAXStyleRangeForIndexParameterizedAttribute];
1558        textParamAttrs = [[NSArray alloc] initWithArray:tempArray];
1559        [tempArray release];
1560    }
1561    if (tableParamAttrs == nil) {
1562        NSMutableArray* tempArray = [[NSMutableArray alloc] initWithArray:paramAttrs];
1563        [tempArray addObject:NSAccessibilityCellForColumnAndRowParameterizedAttribute];
1564        tableParamAttrs = [[NSArray alloc] initWithArray:tempArray];
1565        [tempArray release];
1566    }
1567
1568    if (m_object->isPasswordField())
1569        return [NSArray array];
1570
1571    if (!m_object->isAccessibilityRenderObject())
1572        return paramAttrs;
1573
1574    if (m_object->isTextControl())
1575        return textParamAttrs;
1576
1577    if (m_object->isDataTable())
1578        return tableParamAttrs;
1579
1580    if (m_object->isMenuRelated())
1581        return nil;
1582
1583    return paramAttrs;
1584}
1585
1586- (void)accessibilityPerformPressAction
1587{
1588    m_object->updateBackingStore();
1589
1590    if (m_object->isAttachment())
1591        [[self attachmentView] accessibilityPerformAction:NSAccessibilityPressAction];
1592
1593    m_object->press();
1594}
1595
1596- (void)accessibilityPerformShowMenuAction
1597{
1598    // This needs to be performed in an iteration of the run loop that did not start from an AX call.
1599    // If it's the same run loop iteration, the menu open notification won't be sent
1600    [self performSelector:@selector(accessibilityShowContextMenu) withObject:nil afterDelay:0.0];
1601}
1602
1603- (void)accessibilityShowContextMenu
1604{
1605    FrameView* frameView = m_object->documentFrameView();
1606    if (!frameView)
1607        return;
1608
1609    // simulate a click in the middle of the object
1610    IntPoint clickPoint = m_object->clickPoint();
1611    NSPoint nsClickPoint = NSMakePoint(clickPoint.x(), clickPoint.y());
1612
1613    NSView* view = nil;
1614    if (m_object->isAttachment())
1615        view = [self attachmentView];
1616    else
1617        view = frameView->documentView();
1618
1619    if (!view)
1620        return;
1621
1622    NSPoint nsScreenPoint = [view convertPoint:nsClickPoint toView:nil];
1623
1624    // Show the contextual menu for this event.
1625    NSEvent* event = [NSEvent mouseEventWithType:NSRightMouseDown location:nsScreenPoint modifierFlags:0 timestamp:0 windowNumber:[[view window] windowNumber] context:0 eventNumber:0 clickCount:1 pressure:1];
1626    NSMenu* menu = [view menuForEvent:event];
1627
1628    if (menu)
1629        [NSMenu popUpContextMenu:menu withEvent:event forView:view];
1630}
1631
1632- (void)accessibilityPerformAction:(NSString*)action
1633{
1634    m_object->updateBackingStore();
1635
1636    if ([action isEqualToString:NSAccessibilityPressAction])
1637        [self accessibilityPerformPressAction];
1638
1639    else if ([action isEqualToString:NSAccessibilityShowMenuAction])
1640        [self accessibilityPerformShowMenuAction];
1641}
1642
1643- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attributeName
1644{
1645    m_object->updateBackingStore();
1646
1647    WebCoreTextMarkerRange* textMarkerRange = nil;
1648    NSNumber*               number = nil;
1649    NSString*               string = nil;
1650    NSRange                 range = {0, 0};
1651    NSArray*                array = nil;
1652
1653    // decode the parameter
1654    if ([[WebCoreViewFactory sharedFactory] objectIsTextMarkerRange:value])
1655        textMarkerRange = (WebCoreTextMarkerRange*) value;
1656
1657    else if ([value isKindOfClass:[NSNumber self]])
1658        number = value;
1659
1660    else if ([value isKindOfClass:[NSString self]])
1661        string = value;
1662
1663    else if ([value isKindOfClass:[NSValue self]])
1664        range = [value rangeValue];
1665
1666    else if ([value isKindOfClass:[NSArray self]])
1667        array = value;
1668
1669    // handle the command
1670    if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) {
1671        ASSERT(textMarkerRange);
1672        m_object->setSelectedVisiblePositionRange([self visiblePositionRangeForTextMarkerRange:textMarkerRange]);
1673    } else if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) {
1674        ASSERT(number);
1675        m_object->setFocused([number intValue] != 0);
1676    } else if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) {
1677        if (!string)
1678            return;
1679        m_object->setValue(string);
1680    } else if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute]) {
1681        if (!number)
1682            return;
1683        m_object->setSelected([number boolValue]);
1684    } else if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute]) {
1685        if (!array || m_object->roleValue() != ListBoxRole)
1686            return;
1687        AccessibilityObject::AccessibilityChildrenVector selectedChildren;
1688        convertToVector(array, selectedChildren);
1689        static_cast<AccessibilityListBox*>(m_object)->setSelectedChildren(selectedChildren);
1690    } else if (m_object->isTextControl()) {
1691        if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) {
1692            m_object->setSelectedText(string);
1693        } else if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) {
1694            m_object->setSelectedTextRange(PlainTextRange(range.location, range.length));
1695        } else if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) {
1696            m_object->makeRangeVisible(PlainTextRange(range.location, range.length));
1697        }
1698    }
1699}
1700
1701static RenderObject* rendererForView(NSView* view)
1702{
1703    if (![view conformsToProtocol:@protocol(WebCoreFrameView)])
1704        return 0;
1705
1706    NSView<WebCoreFrameView>* frameView = (NSView<WebCoreFrameView>*)view;
1707    Frame* frame = [frameView _web_frame];
1708    if (!frame)
1709        return 0;
1710
1711    Node* node = frame->document()->ownerElement();
1712    if (!node)
1713        return 0;
1714
1715    return node->renderer();
1716}
1717
1718- (id)_accessibilityParentForSubview:(NSView*)subview
1719{
1720    RenderObject* renderer = rendererForView(subview);
1721    if (!renderer)
1722        return nil;
1723
1724    AccessibilityObject* obj = renderer->document()->axObjectCache()->get(renderer);
1725    if (obj)
1726        return obj->parentObjectUnignored()->wrapper();
1727    return nil;
1728}
1729
1730- (NSString*)accessibilityActionDescription:(NSString*)action
1731{
1732    // we have no custom actions
1733    return NSAccessibilityActionDescription(action);
1734}
1735
1736// The CFAttributedStringType representation of the text associated with this accessibility
1737// object that is specified by the given range.
1738- (NSAttributedString*)doAXAttributedStringForRange:(NSRange)range
1739{
1740    PlainTextRange textRange = PlainTextRange(range.location, range.length);
1741    VisiblePositionRange visiblePosRange = m_object->visiblePositionRangeForRange(textRange);
1742    return [self doAXAttributedStringForTextMarkerRange:textMarkerRangeFromVisiblePositions(visiblePosRange.start, visiblePosRange.end)];
1743}
1744
1745// The RTF representation of the text associated with this accessibility object that is
1746// specified by the given range.
1747- (NSData*)doAXRTFForRange:(NSRange)range
1748{
1749    NSAttributedString* attrString = [self doAXAttributedStringForRange:range];
1750    return [attrString RTFFromRange: NSMakeRange(0, [attrString length]) documentAttributes: nil];
1751}
1752
1753- (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter
1754{
1755    WebCoreTextMarker* textMarker = nil;
1756    WebCoreTextMarkerRange* textMarkerRange = nil;
1757    NSNumber* number = nil;
1758    NSArray* array = nil;
1759    RefPtr<AccessibilityObject> uiElement = 0;
1760    NSPoint point = NSZeroPoint;
1761    bool pointSet = false;
1762    NSRange range = {0, 0};
1763    bool rangeSet = false;
1764
1765    // basic parameter validation
1766    if (!m_object || !attribute || !parameter)
1767        return nil;
1768
1769    m_object->updateBackingStore();
1770
1771    // common parameter type check/casting.  Nil checks in handlers catch wrong type case.
1772    // NOTE: This assumes nil is not a valid parameter, because it is indistinguishable from
1773    // a parameter of the wrong type.
1774    if ([[WebCoreViewFactory sharedFactory] objectIsTextMarker:parameter])
1775        textMarker = (WebCoreTextMarker*) parameter;
1776
1777    else if ([[WebCoreViewFactory sharedFactory] objectIsTextMarkerRange:parameter])
1778        textMarkerRange = (WebCoreTextMarkerRange*) parameter;
1779
1780    else if ([parameter isKindOfClass:[AccessibilityObjectWrapper self]])
1781        uiElement = [(AccessibilityObjectWrapper*)parameter accessibilityObject];
1782
1783    else if ([parameter isKindOfClass:[NSNumber self]])
1784        number = parameter;
1785
1786    else if ([parameter isKindOfClass:[NSArray self]])
1787        array = parameter;
1788
1789    else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSPoint)) == 0) {
1790        pointSet = true;
1791        point = [(NSValue*)parameter pointValue];
1792
1793    } else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSRange)) == 0) {
1794        rangeSet = true;
1795        range = [(NSValue*)parameter rangeValue];
1796
1797    } else {
1798        // got a parameter of a type we never use
1799        // NOTE: No ASSERT_NOT_REACHED because this can happen accidentally
1800        // while using accesstool (e.g.), forcing you to start over
1801        return nil;
1802    }
1803
1804    // Convert values to WebCore types
1805    // FIXME: prepping all of these values as WebCore types is unnecessary in many
1806    // cases. Re-organization of this function or performing the conversion on a
1807    // need basis are possible improvements.
1808    VisiblePosition visiblePos;
1809    if (textMarker)
1810        visiblePos = visiblePositionForTextMarker(textMarker);
1811    int intNumber = [number intValue];
1812    VisiblePositionRange visiblePosRange;
1813    if (textMarkerRange)
1814        visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
1815    IntPoint webCorePoint = IntPoint(point);
1816    PlainTextRange plainTextRange = PlainTextRange(range.location, range.length);
1817
1818    // dispatch
1819    if ([attribute isEqualToString: @"AXUIElementForTextMarker"])
1820        return m_object->accessibilityObjectForPosition(visiblePos)->wrapper();
1821
1822    if ([attribute isEqualToString: @"AXTextMarkerRangeForUIElement"]) {
1823        VisiblePositionRange vpRange = uiElement.get()->visiblePositionRange();
1824        return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1825    }
1826
1827    if ([attribute isEqualToString: @"AXLineForTextMarker"])
1828        return [NSNumber numberWithUnsignedInt:m_object->lineForPosition(visiblePos)];
1829
1830    if ([attribute isEqualToString: @"AXTextMarkerRangeForLine"]) {
1831        VisiblePositionRange vpRange = m_object->visiblePositionRangeForLine(intNumber);
1832        return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1833    }
1834
1835    if ([attribute isEqualToString: @"AXStringForTextMarkerRange"])
1836        return m_object->stringForVisiblePositionRange(visiblePosRange);
1837
1838    if ([attribute isEqualToString: @"AXTextMarkerForPosition"])
1839        return pointSet ? textMarkerForVisiblePosition(m_object->visiblePositionForPoint(webCorePoint)) : nil;
1840
1841    if ([attribute isEqualToString: @"AXBoundsForTextMarkerRange"]) {
1842        NSRect rect = m_object->boundsForVisiblePositionRange(visiblePosRange);
1843        return [NSValue valueWithRect:rect];
1844    }
1845
1846    if ([attribute isEqualToString:NSAccessibilityBoundsForRangeParameterizedAttribute]) {
1847        VisiblePosition start = m_object->visiblePositionForIndex(range.location);
1848        VisiblePosition end = m_object->visiblePositionForIndex(range.location+range.length);
1849        if (start.isNull() || end.isNull())
1850            return nil;
1851        NSRect rect = m_object->boundsForVisiblePositionRange(VisiblePositionRange(start, end));
1852        return [NSValue valueWithRect:rect];
1853    }
1854
1855    if ([attribute isEqualToString: @"AXAttributedStringForTextMarkerRange"])
1856        return [self doAXAttributedStringForTextMarkerRange:textMarkerRange];
1857
1858    if ([attribute isEqualToString: @"AXTextMarkerRangeForUnorderedTextMarkers"]) {
1859        if ([array count] < 2)
1860            return nil;
1861
1862        WebCoreTextMarker* textMarker1 = (WebCoreTextMarker*) [array objectAtIndex:0];
1863        WebCoreTextMarker* textMarker2 = (WebCoreTextMarker*) [array objectAtIndex:1];
1864        if (![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker1]
1865            || ![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker2])
1866            return nil;
1867
1868        VisiblePosition visiblePos1 = visiblePositionForTextMarker(textMarker1);
1869        VisiblePosition visiblePos2 = visiblePositionForTextMarker(textMarker2);
1870        VisiblePositionRange vpRange = m_object->visiblePositionRangeForUnorderedPositions(visiblePos1, visiblePos2);
1871        return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1872    }
1873
1874    if ([attribute isEqualToString: @"AXNextTextMarkerForTextMarker"])
1875        return textMarkerForVisiblePosition(m_object->nextVisiblePosition(visiblePos));
1876
1877    if ([attribute isEqualToString: @"AXPreviousTextMarkerForTextMarker"])
1878        return textMarkerForVisiblePosition(m_object->previousVisiblePosition(visiblePos));
1879
1880    if ([attribute isEqualToString: @"AXLeftWordTextMarkerRangeForTextMarker"]) {
1881        VisiblePositionRange vpRange = m_object->positionOfLeftWord(visiblePos);
1882        return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1883    }
1884
1885    if ([attribute isEqualToString: @"AXRightWordTextMarkerRangeForTextMarker"]) {
1886        VisiblePositionRange vpRange = m_object->positionOfRightWord(visiblePos);
1887        return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1888    }
1889
1890    if ([attribute isEqualToString: @"AXLeftLineTextMarkerRangeForTextMarker"]) {
1891        VisiblePositionRange vpRange = m_object->leftLineVisiblePositionRange(visiblePos);
1892        return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1893    }
1894
1895    if ([attribute isEqualToString: @"AXRightLineTextMarkerRangeForTextMarker"]) {
1896        VisiblePositionRange vpRange = m_object->rightLineVisiblePositionRange(visiblePos);
1897        return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1898    }
1899
1900    if ([attribute isEqualToString: @"AXSentenceTextMarkerRangeForTextMarker"]) {
1901        VisiblePositionRange vpRange = m_object->sentenceForPosition(visiblePos);
1902        return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1903    }
1904
1905    if ([attribute isEqualToString: @"AXParagraphTextMarkerRangeForTextMarker"]) {
1906        VisiblePositionRange vpRange = m_object->paragraphForPosition(visiblePos);
1907        return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1908    }
1909
1910    if ([attribute isEqualToString: @"AXNextWordEndTextMarkerForTextMarker"])
1911        return textMarkerForVisiblePosition(m_object->nextWordEnd(visiblePos));
1912
1913    if ([attribute isEqualToString: @"AXPreviousWordStartTextMarkerForTextMarker"])
1914        return textMarkerForVisiblePosition(m_object->previousWordStart(visiblePos));
1915
1916    if ([attribute isEqualToString: @"AXNextLineEndTextMarkerForTextMarker"])
1917        return textMarkerForVisiblePosition(m_object->nextLineEndPosition(visiblePos));
1918
1919    if ([attribute isEqualToString: @"AXPreviousLineStartTextMarkerForTextMarker"])
1920        return textMarkerForVisiblePosition(m_object->previousLineStartPosition(visiblePos));
1921
1922    if ([attribute isEqualToString: @"AXNextSentenceEndTextMarkerForTextMarker"])
1923        return textMarkerForVisiblePosition(m_object->nextSentenceEndPosition(visiblePos));
1924
1925    if ([attribute isEqualToString: @"AXPreviousSentenceStartTextMarkerForTextMarker"])
1926        return textMarkerForVisiblePosition(m_object->previousSentenceStartPosition(visiblePos));
1927
1928    if ([attribute isEqualToString: @"AXNextParagraphEndTextMarkerForTextMarker"])
1929        return textMarkerForVisiblePosition(m_object->nextParagraphEndPosition(visiblePos));
1930
1931    if ([attribute isEqualToString: @"AXPreviousParagraphStartTextMarkerForTextMarker"])
1932        return textMarkerForVisiblePosition(m_object->previousParagraphStartPosition(visiblePos));
1933
1934    if ([attribute isEqualToString: @"AXStyleTextMarkerRangeForTextMarker"]) {
1935        VisiblePositionRange vpRange = m_object->styleRangeForPosition(visiblePos);
1936        return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1937    }
1938
1939    if ([attribute isEqualToString: @"AXLengthForTextMarkerRange"]) {
1940        int length = m_object->lengthForVisiblePositionRange(visiblePosRange);
1941        if (length < 0)
1942            return nil;
1943        return [NSNumber numberWithInt:length];
1944    }
1945
1946    if (m_object->isDataTable()) {
1947        if ([attribute isEqualToString:NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
1948            if (array == nil || [array count] != 2)
1949                return nil;
1950            AccessibilityTableCell* cell = static_cast<AccessibilityTable*>(m_object)->cellForColumnAndRow([[array objectAtIndex:0] unsignedIntValue], [[array objectAtIndex:1] unsignedIntValue]);
1951            if (!cell)
1952                return nil;
1953
1954            return cell->wrapper();
1955        }
1956    }
1957
1958    if (m_object->isTextControl()) {
1959        if ([attribute isEqualToString: (NSString *)kAXLineForIndexParameterizedAttribute]) {
1960            int lineNumber = m_object->doAXLineForIndex(intNumber);
1961            if (lineNumber < 0)
1962                return nil;
1963            return [NSNumber numberWithUnsignedInt:lineNumber];
1964        }
1965
1966        if ([attribute isEqualToString: (NSString *)kAXRangeForLineParameterizedAttribute]) {
1967            PlainTextRange textRange = m_object->doAXRangeForLine(intNumber);
1968            return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
1969        }
1970
1971        if ([attribute isEqualToString: (NSString*)kAXStringForRangeParameterizedAttribute])
1972            return rangeSet ? (id)(m_object->doAXStringForRange(plainTextRange)) : nil;
1973
1974        if ([attribute isEqualToString: (NSString*)kAXRangeForPositionParameterizedAttribute]) {
1975            if (!pointSet)
1976                return nil;
1977            PlainTextRange textRange = m_object->doAXRangeForPosition(webCorePoint);
1978            return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
1979        }
1980
1981        if ([attribute isEqualToString: (NSString*)kAXRangeForIndexParameterizedAttribute]) {
1982            PlainTextRange textRange = m_object->doAXRangeForIndex(intNumber);
1983            return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
1984        }
1985
1986        if ([attribute isEqualToString: (NSString*)kAXBoundsForRangeParameterizedAttribute]) {
1987            if (!rangeSet)
1988                return nil;
1989            NSRect rect = m_object->doAXBoundsForRange(plainTextRange);
1990            return [NSValue valueWithRect:rect];
1991        }
1992
1993        if ([attribute isEqualToString: (NSString*)kAXRTFForRangeParameterizedAttribute])
1994            return rangeSet ? [self doAXRTFForRange:range] : nil;
1995
1996        if ([attribute isEqualToString: (NSString*)kAXAttributedStringForRangeParameterizedAttribute])
1997            return rangeSet ? [self doAXAttributedStringForRange:range] : nil;
1998
1999        if ([attribute isEqualToString: (NSString*)kAXStyleRangeForIndexParameterizedAttribute]) {
2000            PlainTextRange textRange = m_object->doAXStyleRangeForIndex(intNumber);
2001            return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
2002        }
2003    }
2004
2005    return nil;
2006}
2007
2008- (BOOL)accessibilityShouldUseUniqueId
2009{
2010    return m_object->accessibilityShouldUseUniqueId();
2011}
2012
2013// API that AppKit uses for faster access
2014- (NSUInteger)accessibilityIndexOfChild:(id)child
2015{
2016    m_object->updateBackingStore();
2017
2018    const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children();
2019
2020    if (children.isEmpty())
2021        return [[self renderWidgetChildren] indexOfObject:child];
2022
2023    unsigned count = children.size();
2024    for (unsigned k = 0; k < count; ++k) {
2025        if (children[k]->wrapper() == child)
2026            return k;
2027    }
2028
2029    return NSNotFound;
2030}
2031
2032- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute
2033{
2034    m_object->updateBackingStore();
2035
2036    if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
2037        const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children();
2038        if (children.isEmpty())
2039            return [[self renderWidgetChildren] count];
2040
2041        return children.size();
2042    }
2043
2044    return [super accessibilityArrayAttributeCount:attribute];
2045}
2046
2047- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount
2048{
2049    m_object->updateBackingStore();
2050
2051    if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
2052        if (m_object->children().isEmpty()) {
2053            NSArray *children = [self renderWidgetChildren];
2054            if (!children)
2055                return nil;
2056
2057            NSUInteger childCount = [children count];
2058            if (index >= childCount)
2059                return nil;
2060
2061            NSUInteger arrayLength = min(childCount - index, maxCount);
2062            return [children subarrayWithRange:NSMakeRange(index, arrayLength)];
2063        }
2064
2065        const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children();
2066        unsigned childCount = children.size();
2067        if (index >= childCount)
2068            return nil;
2069
2070        unsigned available = min(childCount - index, maxCount);
2071
2072        NSMutableArray *subarray = [NSMutableArray arrayWithCapacity:available];
2073        for (unsigned added = 0; added < available; ++index, ++added)
2074            [subarray addObject:children[index]->wrapper()];
2075
2076        return subarray;
2077    }
2078
2079    return [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount];
2080}
2081
2082@end
2083
2084#endif // HAVE(ACCESSIBILITY)
2085