2008-05-13 Beth Dakin <bdakin@apple.com>
[WebKit-https.git] / WebCore / page / mac / AccessibilityObjectWrapper.mm
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 #import "AXObjectCache.h"
33 #import "AccessibilityListBox.h"
34 #import "AccessibilityRenderObject.h"
35 #import "ColorMac.h"
36 #import "Frame.h"
37 #import "HTMLAnchorElement.h"
38 #import "HTMLAreaElement.h"
39 #import "HTMLImageElement.h"
40 #import "HTMLInputElement.h"
41 #import "HTMLTextAreaElement.h"
42 #import "LocalizedStrings.h"
43 #import "RenderTextControl.h"
44 #import "RenderView.h"
45 #import "RenderWidget.h"
46 #import "SelectionController.h"
47 #import "SimpleFontData.h"
48 #import "TextIterator.h"
49 #import "WebCoreFrameView.h"
50 #import "WebCoreObjCExtras.h"
51 #import "WebCoreViewFactory.h"
52 #import "htmlediting.h"
53 #import "visible_units.h"
54
55 using namespace WebCore;
56 using namespace HTMLNames;
57
58 @implementation AccessibilityObjectWrapper
59
60 #ifndef BUILDING_ON_TIGER
61 + (void)initialize
62 {
63     WebCoreObjCFinalizeOnMainThread(self);
64 }
65 #endif
66
67 - (id)initWithAccessibilityObject:(AccessibilityObject*)axObject
68 {
69     [super init];
70
71     m_object = axObject;
72     return self;
73 }
74
75 - (void)unregisterUniqueIdForUIElement
76 {
77     [[WebCoreViewFactory sharedFactory] unregisterUniqueIdForUIElement:self];
78 }
79
80 - (void)detach
81 {
82     // Send unregisterUniqueIdForUIElement unconditionally because if it is
83     // ever accidently not done (via other bugs in our AX implementation) you
84     // end up with a crash like <rdar://problem/4273149>.  It is safe and not
85     // expensive to send even if the object is not registered.
86     [self unregisterUniqueIdForUIElement];
87     m_object = 0;
88 }
89
90 - (AccessibilityObject*)accessibilityObject
91 {
92     return m_object;
93 }
94
95 - (NSView*)attachmentView
96 {
97     ASSERT(m_object->isAttachment());
98     Widget* widget = m_object->widgetForAttachmentView();
99     if (!widget)
100         return nil;
101     return widget->getView();
102 }
103
104 static WebCoreTextMarker* textMarkerForVisiblePosition(const VisiblePosition& visiblePos)
105 {
106     if (visiblePos.isNull())
107         return nil;
108
109     Position deepPos = visiblePos.deepEquivalent();
110     Node* domNode = deepPos.node();
111     ASSERT(domNode);
112     if (!domNode)
113         return nil;
114
115     if (domNode->isHTMLElement())
116         if (static_cast<HTMLElement*>(domNode)->isPasswordField())
117             return nil;
118
119     // locate the renderer, which must exist for a visible dom node
120     RenderObject* renderer = domNode->renderer();
121     ASSERT(renderer);
122
123     // find or create an accessibility object for this renderer
124     AXObjectCache* cache = renderer->document()->axObjectCache();
125     RefPtr<AccessibilityObject> obj = cache->get(renderer);
126
127     // create a text marker, adding an ID for the AccessibilityObject if needed
128     TextMarkerData textMarkerData;
129     textMarkerData.axID = obj.get()->axObjectID();
130     textMarkerData.node = domNode;
131     textMarkerData.offset = deepPos.offset();
132     textMarkerData.affinity = visiblePos.affinity();
133     return [[WebCoreViewFactory sharedFactory] textMarkerWithBytes:&textMarkerData length:sizeof(textMarkerData)];
134 }
135
136 static VisiblePosition visiblePositionForTextMarker(WebCoreTextMarker* textMarker)
137 {
138     TextMarkerData textMarkerData;
139     
140     if (![[WebCoreViewFactory sharedFactory] getBytes:&textMarkerData fromTextMarker:textMarker length:sizeof(textMarkerData)])
141         return VisiblePosition();
142
143     VisiblePosition visiblePos = VisiblePosition(textMarkerData.node, textMarkerData.offset, textMarkerData.affinity);
144     Position deepPos = visiblePos.deepEquivalent();
145     AXObjectCache* cache = deepPos.node()->renderer()->document()->axObjectCache();
146     if (!cache->isIDinUse(textMarkerData.axID))
147         return VisiblePosition();
148
149     if (deepPos.node() != textMarkerData.node || deepPos.offset() != textMarkerData.offset)
150         return VisiblePosition();
151     
152     return visiblePos;
153 }
154
155 static VisiblePosition visiblePositionForStartOfTextMarkerRange(WebCoreTextMarkerRange* textMarkerRange)
156 {
157     return visiblePositionForTextMarker([[WebCoreViewFactory sharedFactory] startOfTextMarkerRange:textMarkerRange]);
158 }
159
160 static VisiblePosition visiblePositionForEndOfTextMarkerRange(WebCoreTextMarkerRange* textMarkerRange)
161 {
162     return visiblePositionForTextMarker([[WebCoreViewFactory sharedFactory] endOfTextMarkerRange:textMarkerRange]);
163 }
164
165 static WebCoreTextMarkerRange* textMarkerRangeFromMarkers(WebCoreTextMarker* textMarker1, WebCoreTextMarker* textMarker2)
166 {
167     return [[WebCoreViewFactory sharedFactory] textMarkerRangeWithStart:textMarker1 end:textMarker2];
168 }
169
170 static void AXAttributeStringSetFont(NSMutableAttributedString* attrString, NSString* attribute, NSFont* font, NSRange range)
171 {
172     NSDictionary* dict;
173     
174     if (font) {
175         dict = [NSDictionary dictionaryWithObjectsAndKeys:
176             [font fontName]                             , NSAccessibilityFontNameKey,
177             [font familyName]                           , NSAccessibilityFontFamilyKey,
178             [font displayName]                          , NSAccessibilityVisibleNameKey,
179             [NSNumber numberWithFloat:[font pointSize]] , NSAccessibilityFontSizeKey,
180         nil];
181
182         [attrString addAttribute:attribute value:dict range:range];
183     } else
184         [attrString removeAttribute:attribute range:range];
185     
186 }
187
188 static CGColorRef CreateCGColorIfDifferent(NSColor* nsColor, CGColorRef existingColor)
189 {
190     // get color information assuming NSDeviceRGBColorSpace 
191     NSColor* rgbColor = [nsColor colorUsingColorSpaceName:NSDeviceRGBColorSpace];
192     if (rgbColor == nil)
193         rgbColor = [NSColor blackColor];
194     CGFloat components[4];
195     [rgbColor getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]];
196     
197     // create a new CGColorRef to return
198     CGColorSpaceRef cgColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
199     CGColorRef cgColor = CGColorCreate(cgColorSpace, components);
200     CGColorSpaceRelease(cgColorSpace);
201     CFMakeCollectable(cgColor);
202     
203     // check for match with existing color
204     if (existingColor && CGColorEqualToColor(cgColor, existingColor))
205         cgColor = nil;
206
207     return cgColor;
208 }
209
210 static void AXAttributeStringSetColor(NSMutableAttributedString* attrString, NSString* attribute, NSColor* color, NSRange range)
211 {
212     if (color) {
213         CGColorRef existingColor = (CGColorRef) [attrString attribute:attribute atIndex:range.location effectiveRange:nil];
214         CGColorRef cgColor = CreateCGColorIfDifferent(color, existingColor);
215         if (cgColor) {
216             [attrString addAttribute:attribute value:(id)cgColor range:range];
217             CGColorRelease(cgColor);
218         }
219     } else
220         [attrString removeAttribute:attribute range:range];
221 }
222
223 static void AXAttributeStringSetNumber(NSMutableAttributedString* attrString, NSString* attribute, NSNumber* number, NSRange range)
224 {
225     if (number)
226         [attrString addAttribute:attribute value:number range:range];
227     else
228         [attrString removeAttribute:attribute range:range];
229 }
230
231 static void AXAttributeStringSetStyle(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
232 {
233     RenderStyle* style = renderer->style();
234
235     // set basic font info
236     AXAttributeStringSetFont(attrString, NSAccessibilityFontTextAttribute, style->font().primaryFont()->getNSFont(), range);
237
238     // set basic colors
239     AXAttributeStringSetColor(attrString, NSAccessibilityForegroundColorTextAttribute, nsColor(style->color()), range);
240     AXAttributeStringSetColor(attrString, NSAccessibilityBackgroundColorTextAttribute, nsColor(style->backgroundColor()), range);
241
242     // set super/sub scripting
243     EVerticalAlign alignment = style->verticalAlign();
244     if (alignment == SUB)
245         AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:(-1)], range);
246     else if (alignment == SUPER)
247         AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:1], range);
248     else
249         [attrString removeAttribute:NSAccessibilitySuperscriptTextAttribute range:range];
250     
251     // set shadow
252     if (style->textShadow())
253         AXAttributeStringSetNumber(attrString, NSAccessibilityShadowTextAttribute, [NSNumber numberWithBool:YES], range);
254     else
255         [attrString removeAttribute:NSAccessibilityShadowTextAttribute range:range];
256     
257     // set underline and strikethrough
258     int decor = style->textDecorationsInEffect();
259     if ((decor & UNDERLINE) == 0) {
260         [attrString removeAttribute:NSAccessibilityUnderlineTextAttribute range:range];
261         [attrString removeAttribute:NSAccessibilityUnderlineColorTextAttribute range:range];
262     }
263     
264     if ((decor & LINE_THROUGH) == 0) {
265         [attrString removeAttribute:NSAccessibilityStrikethroughTextAttribute range:range];
266         [attrString removeAttribute:NSAccessibilityStrikethroughColorTextAttribute range:range];
267     }
268
269     if ((decor & (UNDERLINE | LINE_THROUGH)) != 0) {
270         // find colors using quirk mode approach (strict mode would use current
271         // color for all but the root line box, which would use getTextDecorationColors)
272         Color underline, overline, linethrough;
273         renderer->getTextDecorationColors(decor, underline, overline, linethrough);
274         
275         if ((decor & UNDERLINE) != 0) {
276             AXAttributeStringSetNumber(attrString, NSAccessibilityUnderlineTextAttribute, [NSNumber numberWithBool:YES], range);
277             AXAttributeStringSetColor(attrString, NSAccessibilityUnderlineColorTextAttribute, nsColor(underline), range);
278         }
279
280         if ((decor & LINE_THROUGH) != 0) {
281             AXAttributeStringSetNumber(attrString, NSAccessibilityStrikethroughTextAttribute, [NSNumber numberWithBool:YES], range);
282             AXAttributeStringSetColor(attrString, NSAccessibilityStrikethroughColorTextAttribute, nsColor(linethrough), range);
283         }
284     }
285 }
286
287 static int blockquoteLevel(RenderObject* renderer)
288 {
289     int result = 0;
290     for (Node* node = renderer->element(); node; node = node->parent()) {
291         if (node->hasTagName(blockquoteTag))
292             result += 1;
293     }
294     
295     return result;
296 }
297
298 static void AXAttributeStringSetBlockquoteLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
299 {
300     int quoteLevel = blockquoteLevel(renderer);
301     
302     if (quoteLevel)
303         [attrString addAttribute:@"AXBlockQuoteLevel" value:[NSNumber numberWithInt:quoteLevel] range:range];
304     else
305         [attrString removeAttribute:@"AXBlockQuoteLevel" range:range];
306 }
307
308 static void AXAttributeStringSetSpelling(NSMutableAttributedString* attrString, Node* node, int offset, NSRange range)
309 {
310     Vector<DocumentMarker> markers = node->renderer()->document()->markersForNode(node);
311     Vector<DocumentMarker>::iterator markerIt = markers.begin();
312
313     unsigned endOffset = (unsigned)offset + range.length;
314     for ( ; markerIt != markers.end(); markerIt++) {
315         DocumentMarker marker = *markerIt;
316         
317         if (marker.type != DocumentMarker::Spelling)
318             continue;
319         
320         if (marker.endOffset <= (unsigned)offset)
321             continue;
322         
323         if (marker.startOffset > endOffset)
324             break;
325         
326         // add misspelling attribute for the intersection of the marker and the range
327         int rStart = range.location + (marker.startOffset - offset);
328         int rLength = MIN(marker.endOffset, endOffset) - marker.startOffset;
329         NSRange spellRange = NSMakeRange(rStart, rLength);
330         AXAttributeStringSetNumber(attrString, NSAccessibilityMisspelledTextAttribute, [NSNumber numberWithBool:YES], spellRange);
331         
332         if (marker.endOffset > endOffset + 1)
333             break;
334     }
335 }
336
337 static void AXAttributeStringSetHeadingLevel(AccessibilityObject* object, NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
338 {
339     int parentHeadingLevel = AccessibilityRenderObject::headingLevel(renderer->parent()->element());
340     
341     if (parentHeadingLevel)
342         [attrString addAttribute:@"AXHeadingLevel" value:[NSNumber numberWithInt:parentHeadingLevel] range:range];
343     else
344         [attrString removeAttribute:@"AXHeadingLevel" range:range];
345 }
346
347 static AccessibilityObject* AXLinkElementForNode(Node* node)
348 {
349     RenderObject* obj = node->renderer();
350     if (!obj)
351         return 0;
352
353     RefPtr<AccessibilityObject> axObj = obj->document()->axObjectCache()->get(obj);
354     HTMLAnchorElement* anchor = axObj->anchorElement();
355     if (!anchor)
356         return 0;
357
358     RenderObject* anchorRenderer = anchor->renderer();
359     if (!anchorRenderer)
360         return 0;
361     
362     return anchorRenderer->document()->axObjectCache()->get(anchorRenderer);
363 }
364
365 static void AXAttributeStringSetElement(NSMutableAttributedString* attrString, NSString* attribute, AccessibilityObject* object, NSRange range)
366 {
367     if (object && object->isAccessibilityRenderObject()) {
368         // make a serialiazable AX object
369         
370         RenderObject* renderer = static_cast<AccessibilityRenderObject*>(object)->renderer();
371         if (!renderer)
372             return;
373         
374         Document* doc = renderer->document();
375         if (!doc)
376             return;
377         
378         AXObjectCache* cache = doc->axObjectCache();
379         if (!cache)
380             return;
381
382         AXUIElementRef axElement = [[WebCoreViewFactory sharedFactory] AXUIElementForElement:object->wrapper()];
383         if (axElement) {
384             [attrString addAttribute:attribute value:(id)axElement range:range];
385             CFRelease(axElement);
386         }
387     } else
388         [attrString removeAttribute:attribute range:range];
389 }
390
391 static void AXAttributedStringAppendText(AccessibilityObject* object, NSMutableAttributedString* attrString, Node* node, int offset, const UChar* chars, int length)
392 {
393     // skip invisible text
394     if (!node->renderer())
395         return;
396
397     // easier to calculate the range before appending the string
398     NSRange attrStringRange = NSMakeRange([attrString length], length);
399     
400     // append the string from this node
401     [[attrString mutableString] appendString:[NSString stringWithCharacters:chars length:length]];
402
403     // add new attributes and remove irrelevant inherited ones
404     // NOTE: color attributes are handled specially because -[NSMutableAttributedString addAttribute: value: range:] does not merge
405     // identical colors.  Workaround is to not replace an existing color attribute if it matches what we are adding.  This also means
406     // we cannot just pre-remove all inherited attributes on the appended string, so we have to remove the irrelevant ones individually.
407
408     // remove inherited attachment from prior AXAttributedStringAppendReplaced
409     [attrString removeAttribute:NSAccessibilityAttachmentTextAttribute range:attrStringRange];
410     
411     // set new attributes
412     AXAttributeStringSetStyle(attrString, node->renderer(), attrStringRange);
413     AXAttributeStringSetHeadingLevel(object, attrString, node->renderer(), attrStringRange);
414     AXAttributeStringSetBlockquoteLevel(attrString, node->renderer(), attrStringRange);
415     AXAttributeStringSetElement(attrString, NSAccessibilityLinkTextAttribute, AXLinkElementForNode(node), attrStringRange);
416     
417     // do spelling last because it tends to break up the range
418     AXAttributeStringSetSpelling(attrString, node, offset, attrStringRange);
419 }
420
421 static NSString* nsStringForReplacedNode(Node* replacedNode)
422 {
423     // we should always be given a rendered node and a replaced node, but be safe
424     // replaced nodes are either attachments (widgets) or images
425     if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode()) {
426         ASSERT_NOT_REACHED();
427         return nil;
428     }
429
430     // create an AX object, but skip it if it is not supposed to be seen
431     RefPtr<AccessibilityObject> obj = replacedNode->renderer()->document()->axObjectCache()->get(replacedNode->renderer());
432     if (obj->accessibilityIsIgnored())
433         return nil;
434     
435     // use the attachmentCharacter to represent the replaced node
436     const UniChar attachmentChar = NSAttachmentCharacter;
437     return [NSString stringWithCharacters:&attachmentChar length:1];
438 }
439
440 - (NSAttributedString*)doAXAttributedStringForTextMarkerRange:(WebCoreTextMarkerRange*)textMarkerRange
441 {
442     // extract the start and end VisiblePosition
443     VisiblePosition startVisiblePosition = visiblePositionForStartOfTextMarkerRange(textMarkerRange);
444     if (startVisiblePosition.isNull())
445         return nil;
446
447     VisiblePosition endVisiblePosition = visiblePositionForEndOfTextMarkerRange(textMarkerRange);
448     if (endVisiblePosition.isNull())
449         return nil;
450
451     // iterate over the range to build the AX attributed string
452     NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init];
453     TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get());
454     while (!it.atEnd()) {
455         // locate the node and starting offset for this range
456         int exception = 0;
457         Node* node = it.range()->startContainer(exception);
458         ASSERT(node == it.range()->endContainer(exception));
459         int offset = it.range()->startOffset(exception);
460
461         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
462         if (it.length() != 0) {
463             AXAttributedStringAppendText(m_object, attrString, node, offset, it.characters(), it.length());
464         } else {
465             Node* replacedNode = node->childNode(offset);
466             NSString *attachmentString = nsStringForReplacedNode(replacedNode);
467             if (attachmentString) {
468                 NSRange attrStringRange = NSMakeRange([attrString length], [attachmentString length]);
469
470                 // append the placeholder string
471                 [[attrString mutableString] appendString:attachmentString];
472
473                 // remove all inherited attributes
474                 [attrString setAttributes:nil range:attrStringRange];
475
476                 // add the attachment attribute
477                 AccessibilityObject* obj = replacedNode->renderer()->document()->axObjectCache()->get(replacedNode->renderer());
478                 AXAttributeStringSetElement(attrString, NSAccessibilityAttachmentTextAttribute, obj, attrStringRange);
479             }
480         }
481         it.advance();
482     }
483
484     return [attrString autorelease];
485 }
486
487 static WebCoreTextMarkerRange* textMarkerRangeFromVisiblePositions(VisiblePosition startPosition, VisiblePosition endPosition)
488 {
489     WebCoreTextMarker* startTextMarker = textMarkerForVisiblePosition(startPosition);
490     WebCoreTextMarker* endTextMarker   = textMarkerForVisiblePosition(endPosition);
491     return textMarkerRangeFromMarkers(startTextMarker, endTextMarker);
492 }
493
494 - (NSArray*)accessibilityActionNames
495 {
496     static NSArray* actions = nil;
497     
498     if (actions == nil) {
499         if (m_object->actionElement()) 
500             actions = [[NSArray alloc] initWithObjects: NSAccessibilityPressAction, nil];
501         else if (m_object->isAttachment())
502             actions = [[[self attachmentView] accessibilityActionNames] retain];
503     }
504
505     return actions;
506 }
507
508 - (NSArray*)accessibilityAttributeNames
509 {
510     if (m_object->isAttachment())
511         return [[self attachmentView] accessibilityAttributeNames];
512
513     static NSArray* attributes = nil;
514     static NSArray* anchorAttrs = nil;
515     static NSArray* webAreaAttrs = nil;
516     static NSArray* textAttrs = nil;
517     static NSArray* listBoxAttrs = nil;
518     static NSArray* progressIndicatorAttrs = nil;
519     NSMutableArray* tempArray;
520     if (attributes == nil) {
521         attributes = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute,
522                       NSAccessibilitySubroleAttribute,
523                       NSAccessibilityRoleDescriptionAttribute,
524                       NSAccessibilityChildrenAttribute,
525                       NSAccessibilityHelpAttribute,
526                       NSAccessibilityParentAttribute,
527                       NSAccessibilityPositionAttribute,
528                       NSAccessibilitySizeAttribute,
529                       NSAccessibilityTitleAttribute,
530                       NSAccessibilityDescriptionAttribute,
531                       NSAccessibilityValueAttribute,
532                       NSAccessibilityFocusedAttribute,
533                       NSAccessibilityEnabledAttribute,
534                       NSAccessibilityWindowAttribute,
535                       @"AXSelectedTextMarkerRange",
536                       @"AXStartTextMarker",
537                       @"AXEndTextMarker",
538                       @"AXVisited",
539                       NSAccessibilityLinkedUIElementsAttribute,
540                       NSAccessibilitySelectedAttribute,
541                       @"AXBlockQuoteLevel",
542                       nil];
543     }
544     if (anchorAttrs == nil) {
545         tempArray = [[NSMutableArray alloc] initWithArray:attributes];
546         [tempArray addObject:NSAccessibilityURLAttribute];
547         anchorAttrs = [[NSArray alloc] initWithArray:tempArray];
548         [tempArray release];
549     }
550     if (webAreaAttrs == nil) {
551         tempArray = [[NSMutableArray alloc] initWithArray:attributes];
552         [tempArray addObject:@"AXLinkUIElements"];
553         [tempArray addObject:@"AXLoaded"];
554         [tempArray addObject:@"AXLayoutCount"];
555         [tempArray addObject:NSAccessibilityURLAttribute];
556         webAreaAttrs = [[NSArray alloc] initWithArray:tempArray];
557         [tempArray release];
558     }
559     if (textAttrs == nil) {
560         tempArray = [[NSMutableArray alloc] initWithArray:attributes];
561         [tempArray addObject:NSAccessibilityNumberOfCharactersAttribute];
562         [tempArray addObject:NSAccessibilitySelectedTextAttribute];
563         [tempArray addObject:NSAccessibilitySelectedTextRangeAttribute];
564         [tempArray addObject:NSAccessibilityVisibleCharacterRangeAttribute];
565         [tempArray addObject:NSAccessibilityInsertionPointLineNumberAttribute];
566         textAttrs = [[NSArray alloc] initWithArray:tempArray];
567         [tempArray release];
568     }
569     if (listBoxAttrs == nil) {
570         tempArray = [[NSMutableArray alloc] initWithArray:attributes];
571         [tempArray addObject:NSAccessibilitySelectedChildrenAttribute];
572         [tempArray addObject:NSAccessibilityVisibleChildrenAttribute];
573         [tempArray addObject:NSAccessibilityOrientationAttribute];
574         listBoxAttrs = [[NSArray alloc] initWithArray:tempArray];
575         [tempArray release];
576     }
577     if (progressIndicatorAttrs == nil) {
578         tempArray = [[NSMutableArray alloc] initWithArray:attributes];
579         [tempArray addObject:NSAccessibilityTopLevelUIElementAttribute];
580         [tempArray addObject:NSAccessibilityValueAttribute];
581         [tempArray addObject:NSAccessibilityMinValueAttribute];
582         [tempArray addObject:NSAccessibilityMaxValueAttribute];
583         progressIndicatorAttrs = [[NSArray alloc] initWithArray:tempArray];
584         [tempArray release];
585     }
586     
587     if (m_object->isPasswordField())
588         return attributes;
589
590     if (m_object->isWebArea())
591         return webAreaAttrs;
592     
593     if (m_object->isTextControl())
594         return textAttrs;
595
596     if (m_object->isAnchor() || m_object->isImage())
597         return anchorAttrs;
598
599     if (m_object->isListBox())
600         return listBoxAttrs;
601
602     if (m_object->isProgressIndicator())
603         return progressIndicatorAttrs;
604
605     return attributes;
606 }
607
608 - (VisiblePositionRange)visiblePositionRangeForTextMarkerRange:(WebCoreTextMarkerRange*) textMarkerRange
609 {
610     return VisiblePositionRange(visiblePositionForStartOfTextMarkerRange(textMarkerRange), visiblePositionForEndOfTextMarkerRange(textMarkerRange));
611 }
612
613 - (NSArray*)renderWidgetChildren
614 {
615     Widget* widget = m_object->widget();
616     if (!widget)
617         return nil;
618     return [(widget->getOuterView()) accessibilityAttributeValue: NSAccessibilityChildrenAttribute];
619 }
620
621 static NSMutableArray* convertToNSArray(const Vector<RefPtr<AccessibilityObject> >& vector)
622 {
623     unsigned length = vector.size();
624     NSMutableArray* array = [NSMutableArray arrayWithCapacity: length];
625     for (unsigned i = 0; i < length; ++i)
626         [array addObject:vector[i]->wrapper()];
627     return array;
628 }
629
630 - (WebCoreTextMarkerRange*)textMarkerRangeForSelection
631 {
632     Selection selection = m_object->selection();
633     if (selection.isNone())
634         return nil;
635     return textMarkerRangeFromVisiblePositions(selection.visibleStart(), selection.visibleEnd());
636 }
637
638 - (NSValue*)position
639 {
640     IntRect rect = m_object->elementRect();
641     
642     // The Cocoa accessibility API wants the lower-left corner.
643     NSPoint point = NSMakePoint(rect.x(), rect.bottom());
644     FrameView* frameView = m_object->documentFrameView();
645     if (frameView) {
646         NSView* view = frameView->documentView();
647         point = [[view window] convertBaseToScreen: [view convertPoint: point toView:nil]];
648     }
649
650     return [NSValue valueWithPoint: point];
651 }
652
653 typedef HashMap<int, NSString*> AccessibilityRoleMap;
654
655 static const AccessibilityRoleMap& createAccessibilityRoleMap()
656 {
657     struct RoleEntry {
658         AccessibilityRole value;
659         NSString* string;
660     };
661     
662     static const RoleEntry roles[] = {
663         { UnknownRole, NSAccessibilityUnknownRole },
664         { ButtonRole, NSAccessibilityButtonRole },
665         { RadioButtonRole, NSAccessibilityRadioButtonRole },
666         { CheckBoxRole, NSAccessibilityCheckBoxRole },
667         { SliderRole, NSAccessibilitySliderRole },
668         { TabGroupRole, NSAccessibilityTabGroupRole },
669         { TextFieldRole, NSAccessibilityTextFieldRole },
670         { StaticTextRole, NSAccessibilityStaticTextRole },
671         { TextAreaRole, NSAccessibilityTextAreaRole },
672         { ScrollAreaRole, NSAccessibilityScrollAreaRole },
673         { PopUpButtonRole, NSAccessibilityPopUpButtonRole },
674         { MenuButtonRole, NSAccessibilityMenuButtonRole },
675         { TableRole, NSAccessibilityTableRole },
676         { ApplicationRole, NSAccessibilityApplicationRole },
677         { GroupRole, NSAccessibilityGroupRole },
678         { RadioGroupRole, NSAccessibilityRadioGroupRole },
679         { ListRole, NSAccessibilityListRole },
680         { ScrollBarRole, NSAccessibilityScrollBarRole },
681         { ValueIndicatorRole, NSAccessibilityValueIndicatorRole },
682         { ImageRole, NSAccessibilityImageRole },
683         { MenuBarRole, NSAccessibilityMenuBarRole },
684         { MenuRole, NSAccessibilityMenuRole },
685         { MenuItemRole, NSAccessibilityMenuItemRole },
686         { ColumnRole, NSAccessibilityColumnRole },
687         { RowRole, NSAccessibilityRowRole },
688         { ToolbarRole, NSAccessibilityToolbarRole },
689         { BusyIndicatorRole, NSAccessibilityBusyIndicatorRole },
690         { ProgressIndicatorRole, NSAccessibilityProgressIndicatorRole },
691         { WindowRole, NSAccessibilityWindowRole },
692         { DrawerRole, NSAccessibilityDrawerRole },
693         { SystemWideRole, NSAccessibilitySystemWideRole },
694         { OutlineRole, NSAccessibilityOutlineRole },
695         { IncrementorRole, NSAccessibilityIncrementorRole },
696         { BrowserRole, NSAccessibilityBrowserRole },
697         { ComboBoxRole, NSAccessibilityComboBoxRole },
698         { SplitGroupRole, NSAccessibilitySplitGroupRole },
699         { SplitterRole, NSAccessibilitySplitterRole },
700         { ColorWellRole, NSAccessibilityColorWellRole },
701         { GrowAreaRole, NSAccessibilityGrowAreaRole },
702         { SheetRole, NSAccessibilitySheetRole },
703         { HelpTagRole, NSAccessibilityHelpTagRole },
704         { MatteRole, NSAccessibilityMatteRole }, 
705         { RulerRole, NSAccessibilityRulerRole },
706         { RulerMarkerRole, NSAccessibilityRulerMarkerRole },
707         { LinkRole, NSAccessibilityLinkRole },
708 #ifndef BUILDING_ON_TIGER        
709         { DisclosureTriangleRole, NSAccessibilityDisclosureTriangleRole },
710         { GridRole, NSAccessibilityGridRole },
711 #endif
712         { WebCoreLinkRole, @"AXLink" }, // why isn't this just NSAccessibilityLinkRole ?
713         { ImageMapRole, @"AXImageMap" },
714         { ListMarkerRole, @"AXListMarker" },
715         { WebAreaRole, @"AXWebArea" },
716         { HeadingRole, @"AXHeading" },
717         { ListBoxRole, NSAccessibilityListRole },
718         { ListBoxOptionRole, NSAccessibilityStaticTextRole }
719     };
720     AccessibilityRoleMap& roleMap = *new AccessibilityRoleMap;
721     
722     const unsigned numRoles = sizeof(roles) / sizeof(roles[0]);
723     for (unsigned i = 0; i < numRoles; ++i)
724         roleMap.set(roles[i].value, roles[i].string);
725     return roleMap;
726 }
727
728 static NSString* roleValueToNSString(AccessibilityRole value)
729 {
730     ASSERT(value);
731     static const AccessibilityRoleMap& roleMap = createAccessibilityRoleMap();
732     return roleMap.get(value);
733 }
734
735 - (NSString*)role
736 {
737     if (m_object->isAttachment())
738         return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleAttribute];
739     NSString* string = roleValueToNSString(m_object->roleValue());
740     if (string != nil)
741         return string;
742     return NSAccessibilityUnknownRole;
743 }
744
745 - (NSString*)subrole
746 {
747     if (m_object->isPasswordField())
748         return NSAccessibilitySecureTextFieldSubrole;
749     
750     if (m_object->isAttachment()) {
751         NSView* attachView = [self attachmentView];
752         if ([[attachView accessibilityAttributeNames] containsObject:NSAccessibilitySubroleAttribute]) {
753             return [attachView accessibilityAttributeValue:NSAccessibilitySubroleAttribute];
754         }
755     }
756
757     return nil;
758 }
759
760 - (NSString*)roleDescription
761 {
762     if (!m_object)
763         return nil;
764
765     // attachments have the AXImage role, but a different subrole
766     if (m_object->isAttachment())
767         return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleDescriptionAttribute];
768     
769     // FIXME 3447564: It would be better to call some AppKit API to get these strings
770     // (which would be the best way to localize them)
771     
772     NSString* axRole = [self role];
773     if ([axRole isEqualToString:NSAccessibilityButtonRole])
774         return NSAccessibilityRoleDescription(NSAccessibilityButtonRole, [self subrole]);
775     
776     if ([axRole isEqualToString:NSAccessibilityPopUpButtonRole])
777         return NSAccessibilityRoleDescription(NSAccessibilityPopUpButtonRole, [self subrole]);
778
779     if ([axRole isEqualToString:NSAccessibilityStaticTextRole])
780         return NSAccessibilityRoleDescription(NSAccessibilityStaticTextRole, [self subrole]);
781
782     if ([axRole isEqualToString:NSAccessibilityImageRole])
783         return NSAccessibilityRoleDescription(NSAccessibilityImageRole, [self subrole]);
784     
785     if ([axRole isEqualToString:NSAccessibilityGroupRole])
786         return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, [self subrole]);
787     
788     if ([axRole isEqualToString:NSAccessibilityCheckBoxRole])
789         return NSAccessibilityRoleDescription(NSAccessibilityCheckBoxRole, [self subrole]);
790
791     if ([axRole isEqualToString:NSAccessibilityRadioButtonRole])
792         return NSAccessibilityRoleDescription(NSAccessibilityRadioButtonRole, [self subrole]);
793
794     if ([axRole isEqualToString:NSAccessibilityTextFieldRole])
795         return NSAccessibilityRoleDescription(NSAccessibilityTextFieldRole, [self subrole]);
796
797     if ([axRole isEqualToString:NSAccessibilityTextAreaRole])
798         return NSAccessibilityRoleDescription(NSAccessibilityTextAreaRole, [self subrole]);
799
800     if ([axRole isEqualToString:NSAccessibilityListRole])
801         return NSAccessibilityRoleDescription(NSAccessibilityListRole, [self subrole]);
802     
803     if ([axRole isEqualToString:@"AXWebArea"])
804         return AXWebAreaText();
805     
806     if ([axRole isEqualToString:@"AXLink"])
807         return AXLinkText();
808     
809     if ([axRole isEqualToString:@"AXListMarker"])
810         return AXListMarkerText();
811     
812     if ([axRole isEqualToString:@"AXImageMap"])
813         return AXImageMapText();
814
815     if ([axRole isEqualToString:@"AXHeading"])
816         return AXHeadingText();
817     
818     return NSAccessibilityRoleDescription(NSAccessibilityUnknownRole, nil);
819 }
820
821 // FIXME: split up this function in a better way.  
822 // suggestions: Use a hash table that maps attribute names to function calls,
823 // or maybe pointers to member functions
824 - (id)accessibilityAttributeValue:(NSString*)attributeName
825 {
826     if (!m_object)
827         return nil;
828
829     if ([attributeName isEqualToString: NSAccessibilityRoleAttribute])
830         return [self role];
831
832     if ([attributeName isEqualToString: NSAccessibilitySubroleAttribute])
833         return [self subrole];
834
835     if ([attributeName isEqualToString: NSAccessibilityRoleDescriptionAttribute])
836         return [self roleDescription];
837
838     if ([attributeName isEqualToString: NSAccessibilityParentAttribute]) {
839         if (m_object->isAccessibilityRenderObject()) {
840             FrameView* fv = static_cast<AccessibilityRenderObject*>(m_object)->frameViewIfRenderView();
841             if (fv)
842                 return fv->getView();
843         }
844         
845         return m_object->parentObjectUnignored()->wrapper();
846     }
847
848     if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
849         if (m_object->children().isEmpty()) {
850             NSArray* children = [self renderWidgetChildren];
851             if (children != nil)
852                 return children;
853         }
854         return convertToNSArray(m_object->children());
855     }
856     
857     if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute] && m_object->isListBox())
858         return convertToNSArray(static_cast<AccessibilityListBox*>(m_object)->selectedChildren());
859     
860     if ([attributeName isEqualToString: NSAccessibilityVisibleChildrenAttribute] && m_object->isListBox())
861         return convertToNSArray(static_cast<AccessibilityListBox*>(m_object)->visibleChildren());
862     
863     
864     if (m_object->isWebArea()) {
865         if ([attributeName isEqualToString: @"AXLinkUIElements"]) {
866             Vector<RefPtr<AccessibilityObject> > links;
867             m_object->getDocumentLinks(links);
868             return convertToNSArray(links);
869         }
870         if ([attributeName isEqualToString: @"AXLoaded"])
871             return [NSNumber numberWithBool: m_object->isLoaded()];
872         if ([attributeName isEqualToString: @"AXLayoutCount"])
873             return [NSNumber numberWithInt: m_object->layoutCount()];
874     }
875     
876     if (m_object->isTextControl()) {
877         if ([attributeName isEqualToString: NSAccessibilityNumberOfCharactersAttribute]) {
878             int length = m_object->textLength();
879             if (length < 0)
880                 return nil;
881             return [NSNumber numberWithUnsignedInt:length];
882         }
883         if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) {
884             String selectedText = m_object->selectedText();
885             if (selectedText.isNull())
886                 return nil;
887             return (NSString*)selectedText;
888         }
889         if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) {
890             PlainTextRange textRange = m_object->selectedTextRange();
891             if (textRange.isNull())
892                 return nil;
893             return [NSValue valueWithRange:NSMakeRange(textRange.start, textRange.length)];
894         }
895         // TODO: Get actual visible range. <rdar://problem/4712101>
896         if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute])
897             return m_object->isPasswordField() ? nil : [NSValue valueWithRange: NSMakeRange(0, m_object->textLength())];
898         if ([attributeName isEqualToString: NSAccessibilityInsertionPointLineNumberAttribute]) {
899             if (m_object->isPasswordField() || m_object->selectionStart() != m_object->selectionEnd())
900                 return nil;
901             return [NSNumber numberWithInt:m_object->doAXLineForTextMarker(m_object->textMarkerForIndex(m_object->selectionStart(), true))];
902         }
903     }
904     
905     if ([attributeName isEqualToString: NSAccessibilityURLAttribute]) {
906         KURL url = m_object->url();
907         if (url.isNull())
908             return nil;
909         return (NSURL*)url;
910     }
911
912     if ([attributeName isEqualToString: @"AXVisited"])
913         return [NSNumber numberWithBool: m_object->isVisited()];
914     
915     if ([attributeName isEqualToString: NSAccessibilityTitleAttribute]) {
916         if (m_object->isAttachment()) {
917             if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityTitleAttribute]) 
918                 return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityTitleAttribute];
919         }
920         return m_object->title();
921     }
922     
923     if ([attributeName isEqualToString: NSAccessibilityDescriptionAttribute]) {
924         if (m_object->isAttachment()) {
925             if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityDescriptionAttribute])
926                 return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityDescriptionAttribute];
927         }
928         return m_object->accessibilityDescription();
929     }
930     
931     if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) {
932         if (m_object->isAttachment()) {
933             if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityValueAttribute]) 
934                 return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityValueAttribute];
935         }
936         if (m_object->isProgressIndicator())
937             return [NSNumber numberWithFloat:m_object->valueForRange()];
938         if (m_object->hasIntValue())
939             return [NSNumber numberWithInt:m_object->intValue()];
940         return m_object->stringValue();
941     }
942
943     if ([attributeName isEqualToString: NSAccessibilityMinValueAttribute])
944         return [NSNumber numberWithFloat:m_object->minValueForRange()];
945
946     if ([attributeName isEqualToString: NSAccessibilityMaxValueAttribute])
947         return [NSNumber numberWithFloat:m_object->maxValueForRange()];
948
949     if ([attributeName isEqualToString: NSAccessibilityHelpAttribute])
950         return m_object->helpText();
951
952     if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute])
953         return [NSNumber numberWithBool: m_object->isFocused()];
954
955     if ([attributeName isEqualToString: NSAccessibilityEnabledAttribute])
956         return [NSNumber numberWithBool: m_object->isEnabled()];
957
958     if ([attributeName isEqualToString: NSAccessibilitySizeAttribute]) {
959         IntSize s = m_object->size();
960         return [NSValue valueWithSize: NSMakeSize(s.width(), s.height())];
961     }
962
963     if ([attributeName isEqualToString: NSAccessibilityPositionAttribute])
964         return [self position];
965
966     if ([attributeName isEqualToString: NSAccessibilityWindowAttribute] ||
967         [attributeName isEqualToString: NSAccessibilityTopLevelUIElementAttribute]) {
968         FrameView* fv = m_object->documentFrameView();
969         if (fv)
970             return [fv->getView() window];
971         return nil;
972     }
973     
974     if (m_object->isListBox() && [attributeName isEqualToString:NSAccessibilityOrientationAttribute])
975         return NSAccessibilityVerticalOrientationValue;
976
977     if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"])
978         return [self textMarkerRangeForSelection];
979     
980     if (m_object->isAccessibilityRenderObject()) {
981         RenderObject* renderer = static_cast<AccessibilityRenderObject*>(m_object)->renderer();
982         if (!renderer)
983             return nil;
984         
985         if ([attributeName isEqualToString: @"AXStartTextMarker"])
986             return textMarkerForVisiblePosition(startOfDocument(renderer->document()));
987         if ([attributeName isEqualToString: @"AXEndTextMarker"])
988             return textMarkerForVisiblePosition(endOfDocument(renderer->document()));
989
990         if ([attributeName isEqualToString: @"AXBlockQuoteLevel"])
991             return [NSNumber numberWithInt:blockquoteLevel(renderer)];
992     }
993     
994     if ([attributeName isEqualToString: NSAccessibilityLinkedUIElementsAttribute]) {
995         AccessibilityObject* obj = m_object->linkedUIElement();
996         if (obj)
997             return (id) obj->wrapper();
998         return nil;
999     }
1000
1001     if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute])
1002         return [NSNumber numberWithBool:m_object->isSelected()];
1003     
1004     return nil;
1005 }
1006
1007 - (id)accessibilityFocusedUIElement
1008 {
1009     RefPtr<AccessibilityObject> focusedObj = m_object->focusedUIElement();
1010
1011     if (!focusedObj)
1012         return nil;
1013     
1014     return focusedObj->wrapper();
1015 }
1016
1017 - (id)accessibilityHitTest:(NSPoint)point
1018 {
1019     RefPtr<AccessibilityObject> axObject = m_object->doAccessibilityHitTest(IntPoint(point));
1020     if (axObject)
1021         return NSAccessibilityUnignoredAncestor(axObject->wrapper());
1022     return NSAccessibilityUnignoredAncestor(self);
1023 }
1024
1025 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName
1026 {
1027     if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"])
1028         return YES;
1029
1030     if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute])
1031         return m_object->canSetFocusAttribute();
1032
1033     if ([attributeName isEqualToString: NSAccessibilityValueAttribute])
1034         return m_object->canSetValueAttribute();
1035
1036     if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute] ||
1037         [attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute] ||
1038         [attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute])
1039         return m_object->canSetTextRangeAttributes();
1040     
1041     return NO;
1042 }
1043
1044 // accessibilityShouldUseUniqueId is an AppKit method we override so that
1045 // objects will be given a unique ID, and therefore allow AppKit to know when they
1046 // become obsolete (e.g. when the user navigates to a new web page, making this one
1047 // unrendered but not deallocated because it is in the back/forward cache).
1048 // It is important to call NSAccessibilityUnregisterUniqueIdForUIElement in the
1049 // appropriate place (e.g. dealloc) to remove these non-retained references from
1050 // AppKit's id mapping tables. We do this in detach by calling unregisterUniqueIdForUIElement.
1051 //
1052 // Registering an object is also required for observing notifications. Only registered objects can be observed.
1053 - (BOOL)accessibilityIsIgnored
1054 {
1055     if (m_object->isAttachment())
1056         return [[self attachmentView] accessibilityIsIgnored];
1057     return m_object->accessibilityIsIgnored();
1058 }
1059
1060 - (NSArray* )accessibilityParameterizedAttributeNames
1061 {
1062     if (m_object->isAttachment()) 
1063         return nil;
1064
1065     static NSArray* paramAttrs = nil;
1066     static NSArray* textParamAttrs = nil;
1067     if (paramAttrs == nil) {
1068         paramAttrs = [[NSArray alloc] initWithObjects:
1069                       @"AXUIElementForTextMarker",
1070                       @"AXTextMarkerRangeForUIElement",
1071                       @"AXLineForTextMarker",
1072                       @"AXTextMarkerRangeForLine",
1073                       @"AXStringForTextMarkerRange",
1074                       @"AXTextMarkerForPosition",
1075                       @"AXBoundsForTextMarkerRange",
1076                       @"AXAttributedStringForTextMarkerRange",
1077                       @"AXTextMarkerRangeForUnorderedTextMarkers",
1078                       @"AXNextTextMarkerForTextMarker",
1079                       @"AXPreviousTextMarkerForTextMarker",
1080                       @"AXLeftWordTextMarkerRangeForTextMarker",
1081                       @"AXRightWordTextMarkerRangeForTextMarker",
1082                       @"AXLeftLineTextMarkerRangeForTextMarker",
1083                       @"AXRightLineTextMarkerRangeForTextMarker",
1084                       @"AXSentenceTextMarkerRangeForTextMarker",
1085                       @"AXParagraphTextMarkerRangeForTextMarker",
1086                       @"AXNextWordEndTextMarkerForTextMarker",
1087                       @"AXPreviousWordStartTextMarkerForTextMarker",
1088                       @"AXNextLineEndTextMarkerForTextMarker",
1089                       @"AXPreviousLineStartTextMarkerForTextMarker",
1090                       @"AXNextSentenceEndTextMarkerForTextMarker",
1091                       @"AXPreviousSentenceStartTextMarkerForTextMarker",
1092                       @"AXNextParagraphEndTextMarkerForTextMarker",
1093                       @"AXPreviousParagraphStartTextMarkerForTextMarker",
1094                       @"AXStyleTextMarkerRangeForTextMarker",
1095                       @"AXLengthForTextMarkerRange",
1096                       nil];
1097     }
1098
1099     if (textParamAttrs == nil) {
1100         NSMutableArray* tempArray = [[NSMutableArray alloc] initWithArray:paramAttrs];
1101         [tempArray addObject:(NSString*)kAXLineForIndexParameterizedAttribute];
1102         [tempArray addObject:(NSString*)kAXRangeForLineParameterizedAttribute];
1103         [tempArray addObject:(NSString*)kAXStringForRangeParameterizedAttribute];
1104         [tempArray addObject:(NSString*)kAXRangeForPositionParameterizedAttribute];
1105         [tempArray addObject:(NSString*)kAXRangeForIndexParameterizedAttribute];
1106         [tempArray addObject:(NSString*)kAXBoundsForRangeParameterizedAttribute];
1107         [tempArray addObject:(NSString*)kAXRTFForRangeParameterizedAttribute];
1108         [tempArray addObject:(NSString*)kAXAttributedStringForRangeParameterizedAttribute];
1109         [tempArray addObject:(NSString*)kAXStyleRangeForIndexParameterizedAttribute];
1110         textParamAttrs = [[NSArray alloc] initWithArray:tempArray];
1111         [tempArray release];
1112     }
1113     
1114     if (m_object->isPasswordField())
1115         return [NSArray array];
1116     
1117     if (!m_object->isAccessibilityRenderObject())
1118         return paramAttrs;
1119
1120     if (m_object->isTextControl())
1121         return textParamAttrs;
1122
1123     return paramAttrs;
1124 }
1125
1126 - (void)accessibilityPerformAction:(NSString*)action
1127 {
1128     if (![action isEqualToString:NSAccessibilityPressAction])
1129         return;
1130     
1131     if (m_object->isAttachment())
1132         [[self attachmentView] accessibilityPerformAction:NSAccessibilityPressAction];
1133
1134     m_object->press();
1135 }
1136
1137 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attributeName
1138 {
1139     WebCoreTextMarkerRange* textMarkerRange = nil;
1140     NSNumber*               number = nil;
1141     NSString*               string = nil;
1142     NSRange                 range = {0, 0};
1143
1144     // decode the parameter
1145     if ([[WebCoreViewFactory sharedFactory] objectIsTextMarkerRange:value])
1146         textMarkerRange = (WebCoreTextMarkerRange*) value;
1147
1148     else if ([value isKindOfClass:[NSNumber self]])
1149         number = value;
1150
1151     else if ([value isKindOfClass:[NSString self]])
1152         string = value;
1153     
1154     else if ([value isKindOfClass:[NSValue self]])
1155         range = [value rangeValue];
1156     
1157     // handle the command
1158     if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) {
1159         ASSERT(textMarkerRange);
1160         m_object->doSetAXSelectedTextMarkerRange([self visiblePositionRangeForTextMarkerRange:textMarkerRange]);        
1161     } else if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) {
1162         ASSERT(number);
1163         m_object->setFocused([number intValue] != 0);
1164     } else if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) {
1165         if (!string)
1166             return;
1167         m_object->setValue(string);
1168     } else if (m_object->isTextControl()) {
1169         if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) {
1170             m_object->setSelectedText(string);
1171         } else if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) {
1172             m_object->setSelectedTextRange(PlainTextRange(range.location, range.length));
1173         } else if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) {
1174             m_object->makeRangeVisible(PlainTextRange(range.location, range.length));
1175         }
1176     }
1177 }
1178
1179 static RenderObject* rendererForView(NSView* view)
1180 {
1181     if (![view conformsToProtocol:@protocol(WebCoreFrameView)])
1182         return 0;
1183
1184     NSView<WebCoreFrameView>* frameView = (NSView<WebCoreFrameView>*)view;
1185     Frame* frame = [frameView _web_frame];
1186     if (!frame)
1187         return 0;
1188
1189     Node* node = frame->document()->ownerElement();
1190     if (!node)
1191         return 0;
1192
1193     return node->renderer();
1194 }
1195
1196 - (id)_accessibilityParentForSubview:(NSView*)subview
1197 {   
1198     RenderObject* renderer = rendererForView(subview);
1199     if (!renderer)
1200         return nil;
1201
1202     AccessibilityObject* obj = renderer->document()->axObjectCache()->get(renderer);
1203     if (obj)
1204         return obj->parentObjectUnignored()->wrapper();
1205     return nil;
1206 }
1207
1208 - (NSString*)accessibilityActionDescription:(NSString*)action
1209 {
1210     // we have no custom actions
1211     return NSAccessibilityActionDescription(action);
1212 }
1213
1214 // The CFAttributedStringType representation of the text associated with this accessibility
1215 // object that is specified by the given range.
1216 - (NSAttributedString*)doAXAttributedStringForRange:(NSRange)range
1217 {
1218     PlainTextRange textRange = PlainTextRange(range.location, range.length);
1219     VisiblePositionRange visiblePosRange = m_object->textMarkerRangeForRange(textRange);
1220     return [self doAXAttributedStringForTextMarkerRange:textMarkerRangeFromVisiblePositions(visiblePosRange.start, visiblePosRange.end)];
1221 }
1222
1223 // The RTF representation of the text associated with this accessibility object that is
1224 // specified by the given range.
1225 - (NSData*)doAXRTFForRange:(NSRange)range
1226 {
1227     NSAttributedString* attrString = [self doAXAttributedStringForRange:range];
1228     return [attrString RTFFromRange: NSMakeRange(0, [attrString length]) documentAttributes: nil];
1229 }
1230
1231 - (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter
1232 {
1233     WebCoreTextMarker* textMarker = nil;
1234     WebCoreTextMarkerRange* textMarkerRange = nil;
1235     NSNumber* number = nil;
1236     NSArray* array = nil;
1237     RefPtr<AccessibilityObject> uiElement = 0;
1238     NSPoint point = NSZeroPoint;
1239     bool pointSet = false;
1240     NSRange range = {0, 0};
1241     bool rangeSet = false;
1242     
1243     // basic parameter validation
1244     if (!m_object || !attribute || !parameter)
1245         return nil;
1246
1247     // common parameter type check/casting.  Nil checks in handlers catch wrong type case.
1248     // NOTE: This assumes nil is not a valid parameter, because it is indistinguishable from
1249     // a parameter of the wrong type.
1250     if ([[WebCoreViewFactory sharedFactory] objectIsTextMarker:parameter])
1251         textMarker = (WebCoreTextMarker*) parameter;
1252
1253     else if ([[WebCoreViewFactory sharedFactory] objectIsTextMarkerRange:parameter])
1254         textMarkerRange = (WebCoreTextMarkerRange*) parameter;
1255
1256     else if ([parameter isKindOfClass:[AccessibilityObjectWrapper self]])
1257         uiElement = [(AccessibilityObjectWrapper*)parameter accessibilityObject];
1258
1259     else if ([parameter isKindOfClass:[NSNumber self]])
1260         number = parameter;
1261
1262     else if ([parameter isKindOfClass:[NSArray self]])
1263         array = parameter;
1264
1265     else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSPoint)) == 0) {
1266         pointSet = true;
1267         point = [(NSValue*)parameter pointValue];
1268
1269     } else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSRange)) == 0) {
1270         rangeSet = true;
1271         range = [(NSValue*)parameter rangeValue];
1272
1273     } else {
1274         // got a parameter of a type we never use
1275         // NOTE: No ASSERT_NOT_REACHED because this can happen accidentally
1276         // while using accesstool (e.g.), forcing you to start over
1277         return nil;
1278     }
1279     
1280     // Convert values to WebCore types
1281     // FIXME: prepping all of these values as WebCore types is unnecessary in many 
1282     // cases. Re-organization of this function or performing the conversion on a 
1283     // need basis are possible improvements. 
1284     VisiblePosition visiblePos;
1285     if (textMarker)
1286         visiblePos = visiblePositionForTextMarker(textMarker);
1287     int intNumber = [number intValue];
1288     VisiblePositionRange visiblePosRange;
1289     if (textMarkerRange)
1290         visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
1291     IntPoint webCorePoint = IntPoint(point);
1292     PlainTextRange plainTextRange = PlainTextRange(range.location, range.length);
1293
1294     // dispatch
1295     if ([attribute isEqualToString: @"AXUIElementForTextMarker"])
1296         return m_object->doAXUIElementForTextMarker(visiblePos)->wrapper();
1297
1298     if ([attribute isEqualToString: @"AXTextMarkerRangeForUIElement"]) {
1299         VisiblePositionRange vpRange = uiElement.get()->visiblePositionRange();
1300         return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1301     }
1302
1303     if ([attribute isEqualToString: @"AXLineForTextMarker"])
1304         return [NSNumber numberWithUnsignedInt:m_object->doAXLineForTextMarker(visiblePos)];
1305
1306     if ([attribute isEqualToString: @"AXTextMarkerRangeForLine"]) {
1307         VisiblePositionRange vpRange = m_object->doAXTextMarkerRangeForLine(intNumber);
1308         return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1309     }
1310
1311     if ([attribute isEqualToString: @"AXStringForTextMarkerRange"])
1312         return m_object->doAXStringForTextMarkerRange(visiblePosRange);
1313
1314     if ([attribute isEqualToString: @"AXTextMarkerForPosition"])
1315         return pointSet ? textMarkerForVisiblePosition(m_object->doAXTextMarkerForPosition(webCorePoint)) : nil;
1316
1317     if ([attribute isEqualToString: @"AXBoundsForTextMarkerRange"]) {
1318         NSRect rect = m_object->doAXBoundsForTextMarkerRange(visiblePosRange);
1319         return [NSValue valueWithRect:rect];
1320     }
1321
1322     if ([attribute isEqualToString: @"AXAttributedStringForTextMarkerRange"])
1323         return [self doAXAttributedStringForTextMarkerRange:textMarkerRange];
1324
1325     if ([attribute isEqualToString: @"AXTextMarkerRangeForUnorderedTextMarkers"]) {
1326         if ([array count] < 2)
1327             return nil;
1328
1329         WebCoreTextMarker* textMarker1 = (WebCoreTextMarker*) [array objectAtIndex:0];
1330         WebCoreTextMarker* textMarker2 = (WebCoreTextMarker*) [array objectAtIndex:1];
1331         if (![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker1] 
1332             || ![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker2])
1333             return nil;
1334
1335         VisiblePosition visiblePos1 = visiblePositionForTextMarker(textMarker1);
1336         VisiblePosition visiblePos2 = visiblePositionForTextMarker(textMarker2);
1337         VisiblePositionRange vpRange = m_object->doAXTextMarkerRangeForUnorderedTextMarkers(visiblePos1, visiblePos2);
1338         return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1339     }
1340
1341     if ([attribute isEqualToString: @"AXNextTextMarkerForTextMarker"])
1342         return textMarkerForVisiblePosition(m_object->doAXNextTextMarkerForTextMarker(visiblePos));
1343
1344     if ([attribute isEqualToString: @"AXPreviousTextMarkerForTextMarker"])
1345         return textMarkerForVisiblePosition(m_object->doAXPreviousTextMarkerForTextMarker(visiblePos));
1346
1347     if ([attribute isEqualToString: @"AXLeftWordTextMarkerRangeForTextMarker"]) {
1348         VisiblePositionRange vpRange = m_object->doAXLeftWordTextMarkerRangeForTextMarker(visiblePos);
1349         return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1350     }
1351
1352     if ([attribute isEqualToString: @"AXRightWordTextMarkerRangeForTextMarker"]) {
1353         VisiblePositionRange vpRange = m_object->doAXRightWordTextMarkerRangeForTextMarker(visiblePos);
1354         return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1355     }
1356
1357     if ([attribute isEqualToString: @"AXLeftLineTextMarkerRangeForTextMarker"]) {
1358         VisiblePositionRange vpRange = m_object->doAXLeftLineTextMarkerRangeForTextMarker(visiblePos);
1359         return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1360     }
1361
1362     if ([attribute isEqualToString: @"AXRightLineTextMarkerRangeForTextMarker"]) {
1363         VisiblePositionRange vpRange = m_object->doAXRightLineTextMarkerRangeForTextMarker(visiblePos);
1364         return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1365     }
1366
1367     if ([attribute isEqualToString: @"AXSentenceTextMarkerRangeForTextMarker"]) {
1368         VisiblePositionRange vpRange = m_object->doAXSentenceTextMarkerRangeForTextMarker(visiblePos);
1369         return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1370     }
1371
1372     if ([attribute isEqualToString: @"AXParagraphTextMarkerRangeForTextMarker"]) {
1373         VisiblePositionRange vpRange = m_object->doAXParagraphTextMarkerRangeForTextMarker(visiblePos);
1374         return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1375     }
1376
1377     if ([attribute isEqualToString: @"AXNextWordEndTextMarkerForTextMarker"])
1378         return textMarkerForVisiblePosition(m_object->doAXNextWordEndTextMarkerForTextMarker(visiblePos));
1379
1380     if ([attribute isEqualToString: @"AXPreviousWordStartTextMarkerForTextMarker"])
1381         return textMarkerForVisiblePosition(m_object->doAXPreviousWordStartTextMarkerForTextMarker(visiblePos));
1382
1383     if ([attribute isEqualToString: @"AXNextLineEndTextMarkerForTextMarker"])
1384         return textMarkerForVisiblePosition(m_object->doAXNextLineEndTextMarkerForTextMarker(visiblePos));
1385
1386     if ([attribute isEqualToString: @"AXPreviousLineStartTextMarkerForTextMarker"])
1387         return textMarkerForVisiblePosition(m_object->doAXPreviousLineStartTextMarkerForTextMarker(visiblePos));
1388
1389     if ([attribute isEqualToString: @"AXNextSentenceEndTextMarkerForTextMarker"])
1390         return textMarkerForVisiblePosition(m_object->doAXNextSentenceEndTextMarkerForTextMarker(visiblePos));
1391
1392     if ([attribute isEqualToString: @"AXPreviousSentenceStartTextMarkerForTextMarker"])
1393         return textMarkerForVisiblePosition(m_object->doAXPreviousSentenceStartTextMarkerForTextMarker(visiblePos));
1394
1395     if ([attribute isEqualToString: @"AXNextParagraphEndTextMarkerForTextMarker"])
1396         return textMarkerForVisiblePosition(m_object->doAXNextParagraphEndTextMarkerForTextMarker(visiblePos));
1397
1398     if ([attribute isEqualToString: @"AXPreviousParagraphStartTextMarkerForTextMarker"])
1399         return textMarkerForVisiblePosition(m_object->doAXPreviousParagraphStartTextMarkerForTextMarker(visiblePos));
1400
1401     if ([attribute isEqualToString: @"AXStyleTextMarkerRangeForTextMarker"]) {
1402         VisiblePositionRange vpRange = m_object->doAXStyleTextMarkerRangeForTextMarker(visiblePos);
1403         return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end);
1404     }
1405
1406     if ([attribute isEqualToString: @"AXLengthForTextMarkerRange"]) {
1407         int length = m_object->doAXLengthForTextMarkerRange(visiblePosRange);
1408         if (length < 0)
1409             return nil;
1410         return [NSNumber numberWithInt:length];
1411     }
1412
1413     if (m_object->isTextControl()) {
1414         if ([attribute isEqualToString: (NSString*)kAXLineForIndexParameterizedAttribute])
1415             return [NSNumber numberWithUnsignedInt: m_object->doAXLineForIndex(intNumber)];
1416
1417         if ([attribute isEqualToString: (NSString*)kAXRangeForLineParameterizedAttribute]) {
1418             PlainTextRange textRange = m_object->doAXRangeForLine(intNumber);
1419             return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
1420         }
1421
1422         if ([attribute isEqualToString: (NSString*)kAXStringForRangeParameterizedAttribute])
1423             return rangeSet ? (id)(m_object->doAXStringForRange(plainTextRange)) : nil;
1424
1425         if ([attribute isEqualToString: (NSString*)kAXRangeForPositionParameterizedAttribute]) {
1426             if (!pointSet)
1427                 return nil;
1428             PlainTextRange textRange = m_object->doAXRangeForPosition(webCorePoint);
1429             return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
1430         }
1431
1432         if ([attribute isEqualToString: (NSString*)kAXRangeForIndexParameterizedAttribute]) {
1433             PlainTextRange textRange = m_object->doAXRangeForIndex(intNumber);
1434             return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
1435         }
1436
1437         if ([attribute isEqualToString: (NSString*)kAXBoundsForRangeParameterizedAttribute]) {
1438             if (!rangeSet)
1439                 return nil;
1440             NSRect rect = m_object->doAXBoundsForRange(plainTextRange);
1441             return [NSValue valueWithRect:rect];
1442         }
1443
1444         if ([attribute isEqualToString: (NSString*)kAXRTFForRangeParameterizedAttribute])
1445             return rangeSet ? [self doAXRTFForRange:range] : nil;
1446
1447         if ([attribute isEqualToString: (NSString*)kAXAttributedStringForRangeParameterizedAttribute])
1448             return rangeSet ? [self doAXAttributedStringForRange:range] : nil;
1449
1450         if ([attribute isEqualToString: (NSString*)kAXStyleRangeForIndexParameterizedAttribute]) {
1451             PlainTextRange textRange = m_object->doAXStyleRangeForIndex(intNumber);
1452             return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
1453         }
1454     }
1455
1456     return nil;
1457 }
1458
1459 - (BOOL)accessibilityShouldUseUniqueId
1460 {
1461     return m_object->accessibilityShouldUseUniqueId();
1462 }
1463
1464 @end