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