472bef1ff57e13f628e94870c8d7322ff210ec81
[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 "Frame.h"
34 #import "HTMLAnchorElement.h"
35 #import "HTMLImageElement.h"
36 #import "HTMLInputElement.h"
37 #import "HTMLTextAreaElement.h"
38 #import "RenderTextControl.h"
39 #import "RenderView.h"
40 #import "RenderWidget.h"
41 #import "SelectionController.h"
42 #import "TextIterator.h"
43 #import "WebCoreFrameView.h"
44 #import "WebCoreObjCExtras.h"
45 #import "WebCoreViewFactory.h"
46 #import "htmlediting.h"
47 #import "visible_units.h"
48
49 using namespace WebCore;
50 using namespace HTMLNames;
51
52 @implementation AccessibilityObjectWrapper
53
54 #ifndef BUILDING_ON_TIGER
55 + (void)initialize
56 {
57     WebCoreObjCFinalizeOnMainThread(self);
58 }
59 #endif
60
61 - (id)initWithAccessibilityObject:(AccessibilityObject*)axObject
62 {
63     [super init];
64
65     m_object = axObject;
66     return self;
67 }
68
69 - (void)unregisterUniqueIdForUIElement
70 {
71     [[WebCoreViewFactory sharedFactory] unregisterUniqueIdForUIElement:self];
72 }
73
74 - (void)detach
75 {
76     // Send unregisterUniqueIdForUIElement unconditionally because if it is
77     // ever accidently not done (via other bugs in our AX implementation) you
78     // end up with a crash like <rdar://problem/4273149>.  It is safe and not
79     // expensive to send even if the object is not registered.
80     [self unregisterUniqueIdForUIElement];
81     m_object = 0;
82 }
83
84 - (AccessibilityObject*)accessibilityObject
85 {
86     return m_object;
87 }
88
89 - (NSArray*)accessibilityActionNames
90 {
91     static NSArray* actions = nil;
92     
93     if (actions == nil) {
94         if (m_object->actionElement()) 
95             actions = [[NSArray alloc] initWithObjects: NSAccessibilityPressAction, nil];
96         else if (m_object->isAttachment())
97             actions = [[m_object->attachmentView() accessibilityActionNames] retain];
98     }
99
100     return actions;
101 }
102
103 - (NSArray*)accessibilityAttributeNames
104 {
105     if (m_object->isAttachment())
106         return [m_object->attachmentView() accessibilityAttributeNames];
107         
108     static NSArray* attributes = nil;
109     static NSArray* anchorAttrs = nil;
110     static NSArray* webAreaAttrs = nil;
111     static NSArray* textAttrs = nil;
112     NSMutableArray* tempArray;
113     if (attributes == nil) {
114         attributes = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute,
115             NSAccessibilitySubroleAttribute,
116             NSAccessibilityRoleDescriptionAttribute,
117             NSAccessibilityChildrenAttribute,
118             NSAccessibilityHelpAttribute,
119             NSAccessibilityParentAttribute,
120             NSAccessibilityPositionAttribute,
121             NSAccessibilitySizeAttribute,
122             NSAccessibilityTitleAttribute,
123             NSAccessibilityDescriptionAttribute,
124             NSAccessibilityValueAttribute,
125             NSAccessibilityFocusedAttribute,
126             NSAccessibilityEnabledAttribute,
127             NSAccessibilityWindowAttribute,
128             @"AXSelectedTextMarkerRange",
129             @"AXStartTextMarker",
130             @"AXEndTextMarker",
131             @"AXVisited",
132             NSAccessibilityLinkedUIElementsAttribute,
133             nil];
134     }
135     if (anchorAttrs == nil) {
136         tempArray = [[NSMutableArray alloc] initWithArray:attributes];
137         [tempArray addObject:NSAccessibilityURLAttribute];
138         anchorAttrs = [[NSArray alloc] initWithArray:tempArray];
139         [tempArray release];
140     }
141     if (webAreaAttrs == nil) {
142         tempArray = [[NSMutableArray alloc] initWithArray:attributes];
143         [tempArray addObject:@"AXLinkUIElements"];
144         [tempArray addObject:@"AXLoaded"];
145         [tempArray addObject:@"AXLayoutCount"];
146         webAreaAttrs = [[NSArray alloc] initWithArray:tempArray];
147         [tempArray release];
148     }
149     if (textAttrs == nil) {
150         tempArray = [[NSMutableArray alloc] initWithArray:attributes];
151         [tempArray addObject:NSAccessibilityNumberOfCharactersAttribute];
152         [tempArray addObject:NSAccessibilitySelectedTextAttribute];
153         [tempArray addObject:NSAccessibilitySelectedTextRangeAttribute];
154         [tempArray addObject:NSAccessibilityVisibleCharacterRangeAttribute];
155         [tempArray addObject:NSAccessibilityInsertionPointLineNumberAttribute];
156         textAttrs = [[NSArray alloc] initWithArray:tempArray];
157         [tempArray release];
158     }
159     
160     if (m_object->isPasswordField())
161         return attributes;
162
163     if (m_object->isWebArea())
164         return webAreaAttrs;
165     
166     if (m_object->isTextControl())
167         return textAttrs;
168
169     if (m_object->isAnchor() || m_object->isImage())
170         return anchorAttrs;
171
172     return attributes;
173 }
174
175 - (VisiblePositionRange)visiblePositionRangeForTextMarkerRange:(WebCoreTextMarkerRange*) textMarkerRange
176 {
177     return VisiblePositionRange(m_object->visiblePositionForStartOfTextMarkerRange(textMarkerRange), m_object->visiblePositionForEndOfTextMarkerRange(textMarkerRange));
178 }
179
180 - (NSArray*)renderWidgetChildren
181 {
182     Widget* widget = m_object->widget();
183     if (!widget)
184         return nil;
185     return [(widget->getOuterView()) accessibilityAttributeValue: NSAccessibilityChildrenAttribute];
186 }
187
188 static NSMutableArray* convertToNSArray(const Vector<RefPtr<AccessibilityObject> >& vector)
189 {
190     unsigned length = vector.size();
191     NSMutableArray* array = [NSMutableArray arrayWithCapacity: length];
192     for (unsigned i = 0; i < length; ++i)
193         [array addObject:vector[i]->wrapper()];
194     return array;
195 }
196
197 // FIXME: split up this function in a better way.  
198 // suggestions: Use a hash table that maps attribute names to function calls,
199 // or maybe pointers to member functions
200 - (id)accessibilityAttributeValue:(NSString*)attributeName
201 {
202     RenderObject* renderer = m_object->renderer();
203     if (!renderer)
204         return nil;
205
206     if ([attributeName isEqualToString: NSAccessibilityRoleAttribute])
207         return m_object->role();
208
209     if ([attributeName isEqualToString: NSAccessibilitySubroleAttribute])
210         return m_object->subrole();
211
212     if ([attributeName isEqualToString: NSAccessibilityRoleDescriptionAttribute])
213         return m_object->roleDescription();
214
215     if ([attributeName isEqualToString: NSAccessibilityParentAttribute]) {
216         FrameView* fv = m_object->frameViewIfRenderView();
217         if (fv)
218             return fv->getView();
219         return m_object->parentObjectUnignored()->wrapper();
220     }
221
222     if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
223         if (!m_object->hasChildren()) {
224             NSArray* children = [self renderWidgetChildren];
225             if (children != nil)
226                 return children;
227             m_object->addChildren();
228         }
229         return convertToNSArray(m_object->children());
230     }
231
232     if (m_object->isWebArea()) {
233         if ([attributeName isEqualToString: @"AXLinkUIElements"]) {
234             Vector<RefPtr<AccessibilityObject> > links;
235             m_object->documentLinks(links);
236             return convertToNSArray(links);
237         }
238         if ([attributeName isEqualToString: @"AXLoaded"])
239             return [NSNumber numberWithBool: m_object->loaded()];
240         if ([attributeName isEqualToString: @"AXLayoutCount"])
241             return [NSNumber numberWithInt: m_object->layoutCount()];
242     }
243     
244     if (m_object->isTextControl()) {
245         // FIXME: refactor code to eliminate need for this textControl
246         RenderTextControl* textControl = static_cast<RenderTextControl*>(renderer);
247         if ([attributeName isEqualToString: NSAccessibilityNumberOfCharactersAttribute]) {
248             int length = m_object->textLength();
249             if (length < 0)
250                 return nil;
251             return [NSNumber numberWithUnsignedInt:length];
252         }
253         if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) {
254             String selectedText = m_object->selectedText();
255             if (selectedText.isNull())
256                 return nil;
257             return (NSString*)selectedText;
258         }
259         if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) {
260             AccessibilityObject::PlainTextRange textRange = m_object->selectedTextRange();
261             if (textRange.isNull())
262                 return nil;
263             return [NSValue valueWithRange:NSMakeRange(textRange.start, textRange.length)];
264         }
265         // TODO: Get actual visible range. <rdar://problem/4712101>
266         if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute])
267             return m_object->isPasswordField() ? nil : [NSValue valueWithRange: NSMakeRange(0, textControl->text().length())];
268         if ([attributeName isEqualToString: NSAccessibilityInsertionPointLineNumberAttribute]) {
269             if (m_object->isPasswordField() || textControl->selectionStart() != textControl->selectionEnd())
270                 return nil;
271             return [NSNumber numberWithInt:m_object->doAXLineForTextMarker(m_object->textMarkerForIndex(textControl->selectionStart(), true))];
272         }
273     }
274     
275     if ([attributeName isEqualToString: NSAccessibilityURLAttribute]) {
276         KURL url = m_object->url();
277         if (url.isNull())
278             return nil;
279         return (NSURL*)url;
280     }
281
282     if ([attributeName isEqualToString: @"AXVisited"])
283         return [NSNumber numberWithBool: m_object->isVisited()];
284     
285     if ([attributeName isEqualToString: NSAccessibilityTitleAttribute]) {
286         if (m_object->isAttachment()) {
287             if ([[m_object->attachmentView() accessibilityAttributeNames] containsObject:NSAccessibilityTitleAttribute]) 
288                 return [m_object->attachmentView() accessibilityAttributeValue:NSAccessibilityTitleAttribute];
289         }
290         return m_object->title();
291     }
292     
293     if ([attributeName isEqualToString: NSAccessibilityDescriptionAttribute]) {
294         if (m_object->isAttachment()) {
295             if ([[m_object->attachmentView() accessibilityAttributeNames] containsObject:NSAccessibilityDescriptionAttribute])
296                 return [m_object->attachmentView() accessibilityAttributeValue:NSAccessibilityDescriptionAttribute];
297         }
298         return m_object->accessibilityDescription();
299     }
300     
301     if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) {
302         if (m_object->isAttachment()) {
303             if ([[m_object->attachmentView() accessibilityAttributeNames] containsObject:NSAccessibilityValueAttribute]) 
304                 return [m_object->attachmentView() accessibilityAttributeValue:NSAccessibilityValueAttribute];
305         }    
306         if (m_object->hasIntValue())
307             return [NSNumber numberWithInt:m_object->intValue()];
308         return m_object->stringValue();
309     }
310
311     if ([attributeName isEqualToString: NSAccessibilityHelpAttribute])
312         return m_object->helpText();
313    
314     if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute])
315         return [NSNumber numberWithBool: m_object->isFocused()];
316
317     if ([attributeName isEqualToString: NSAccessibilityEnabledAttribute])
318         return [NSNumber numberWithBool: m_object->isEnabled()];
319    
320     if ([attributeName isEqualToString: NSAccessibilitySizeAttribute]) {
321         IntSize s = m_object->size();
322         return [NSValue valueWithSize: NSMakeSize(s.width(), s.height())];
323     }
324
325     if ([attributeName isEqualToString: NSAccessibilityPositionAttribute])
326         return m_object->position();
327
328     if ([attributeName isEqualToString: NSAccessibilityWindowAttribute]) {
329         FrameView* fv = m_object->documentFrameView();
330         if (fv)
331             return [fv->getView() window];
332         return nil;
333     }
334     
335     if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"])
336         return m_object->textMarkerRangeForSelection();
337     if ([attributeName isEqualToString: @"AXStartTextMarker"])
338         return m_object->startTextMarker();
339     if ([attributeName isEqualToString: @"AXEndTextMarker"])
340         return m_object->textMarkerForVisiblePosition(endOfDocument(renderer->document()));
341
342     if ([attributeName isEqualToString: NSAccessibilityLinkedUIElementsAttribute]) {
343         AccessibilityObject* obj = m_object->linkedUIElement();
344         if (obj)
345             return (id) obj->wrapper();
346         return nil;
347     }
348
349     return nil;
350 }
351
352 - (id)accessibilityFocusedUIElement
353 {
354     RefPtr<AccessibilityObject> focusedObj = m_object->focusedUIElement();
355
356     if (!focusedObj)
357         return nil;
358     
359     return focusedObj->wrapper();
360 }
361
362 - (id)accessibilityHitTest:(NSPoint)point
363 {
364     RefPtr<AccessibilityObject> axObject = m_object->doAccessibilityHitTest(IntPoint(point));
365     if (axObject)
366         return NSAccessibilityUnignoredAncestor(axObject->wrapper());
367     return NSAccessibilityUnignoredAncestor(self);
368 }
369
370 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName
371 {
372     if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"])
373         return YES;
374         
375     if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute])
376         return m_object->canSetFocusAttribute();
377
378     if ([attributeName isEqualToString: NSAccessibilityValueAttribute])
379         return m_object->canSetValueAttribute();
380
381     if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute] ||
382         [attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute] ||
383         [attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute])
384         return m_object->canSetTextRangeAttributes();
385     
386     return NO;
387 }
388
389 // accessibilityShouldUseUniqueId is an AppKit method we override so that
390 // objects will be given a unique ID, and therefore allow AppKit to know when they
391 // become obsolete (e.g. when the user navigates to a new web page, making this one
392 // unrendered but not deallocated because it is in the back/forward cache).
393 // It is important to call NSAccessibilityUnregisterUniqueIdForUIElement in the
394 // appropriate place (e.g. dealloc) to remove these non-retained references from
395 // AppKit's id mapping tables. We do this in detach by calling unregisterUniqueIdForUIElement.
396 //
397 // Registering an object is also required for observing notifications. Only registered objects can be observed.
398 - (BOOL)accessibilityIsIgnored
399 {
400     if (m_object->isAttachment())
401         return [m_object->attachmentView() accessibilityIsIgnored];
402     return m_object->accessibilityIsIgnored();
403 }
404
405 - (NSArray* )accessibilityParameterizedAttributeNames
406 {
407     if (m_object->isAttachment()) 
408         return nil;
409         
410     static NSArray* paramAttrs = nil;
411     static NSArray* textParamAttrs = nil;
412     if (paramAttrs == nil) {
413         paramAttrs = [[NSArray alloc] initWithObjects:
414         @"AXUIElementForTextMarker",
415         @"AXTextMarkerRangeForUIElement",
416         @"AXLineForTextMarker",
417         @"AXTextMarkerRangeForLine",
418         @"AXStringForTextMarkerRange",
419         @"AXTextMarkerForPosition",
420         @"AXBoundsForTextMarkerRange",
421         @"AXAttributedStringForTextMarkerRange",
422         @"AXTextMarkerRangeForUnorderedTextMarkers",
423         @"AXNextTextMarkerForTextMarker",
424         @"AXPreviousTextMarkerForTextMarker",
425         @"AXLeftWordTextMarkerRangeForTextMarker",
426         @"AXRightWordTextMarkerRangeForTextMarker",
427         @"AXLeftLineTextMarkerRangeForTextMarker",
428         @"AXRightLineTextMarkerRangeForTextMarker",
429         @"AXSentenceTextMarkerRangeForTextMarker",
430         @"AXParagraphTextMarkerRangeForTextMarker",
431         @"AXNextWordEndTextMarkerForTextMarker",
432         @"AXPreviousWordStartTextMarkerForTextMarker",
433         @"AXNextLineEndTextMarkerForTextMarker",
434         @"AXPreviousLineStartTextMarkerForTextMarker",
435         @"AXNextSentenceEndTextMarkerForTextMarker",
436         @"AXPreviousSentenceStartTextMarkerForTextMarker",
437         @"AXNextParagraphEndTextMarkerForTextMarker",
438         @"AXPreviousParagraphStartTextMarkerForTextMarker",
439         @"AXStyleTextMarkerRangeForTextMarker",
440         @"AXLengthForTextMarkerRange",
441         nil];
442     }
443
444     if (textParamAttrs == nil) {
445         NSMutableArray* tempArray = [[NSMutableArray alloc] initWithArray:paramAttrs];
446         [tempArray addObject:(NSString*)kAXLineForIndexParameterizedAttribute];
447         [tempArray addObject:(NSString*)kAXRangeForLineParameterizedAttribute];
448         [tempArray addObject:(NSString*)kAXStringForRangeParameterizedAttribute];
449         [tempArray addObject:(NSString*)kAXRangeForPositionParameterizedAttribute];
450         [tempArray addObject:(NSString*)kAXRangeForIndexParameterizedAttribute];
451         [tempArray addObject:(NSString*)kAXBoundsForRangeParameterizedAttribute];
452         [tempArray addObject:(NSString*)kAXRTFForRangeParameterizedAttribute];
453         [tempArray addObject:(NSString*)kAXAttributedStringForRangeParameterizedAttribute];
454         [tempArray addObject:(NSString*)kAXStyleRangeForIndexParameterizedAttribute];
455         textParamAttrs = [[NSArray alloc] initWithArray:tempArray];
456         [tempArray release];
457     }
458     
459     if (m_object->isPasswordField())
460         return [NSArray array];
461     
462     if (!m_object->renderer())
463         return paramAttrs;
464
465     if (m_object->isTextControl())
466         return textParamAttrs;
467
468     return paramAttrs;
469 }
470
471 - (void)accessibilityPerformAction:(NSString*)action
472 {
473     if ([action isEqualToString:NSAccessibilityPressAction])
474         m_object->press();
475 }
476
477 - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attributeName
478 {
479     WebCoreTextMarkerRange* textMarkerRange = nil;
480     NSNumber*               number = nil;
481     NSString*               string = nil;
482     NSRange                 range = {0, 0};
483
484     // decode the parameter
485     if ([[WebCoreViewFactory sharedFactory] objectIsTextMarkerRange:value])
486         textMarkerRange = (WebCoreTextMarkerRange*) value;
487
488     else if ([value isKindOfClass:[NSNumber self]])
489         number = value;
490
491     else if ([value isKindOfClass:[NSString self]])
492         string = value;
493     
494     else if ([value isKindOfClass:[NSValue self]])
495         range = [value rangeValue];
496     
497     // handle the command
498     if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) {
499         ASSERT(textMarkerRange);
500         m_object->doSetAXSelectedTextMarkerRange([self visiblePositionRangeForTextMarkerRange:textMarkerRange]);        
501     } else if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) {
502         ASSERT(number);
503         m_object->setFocused([number intValue] != 0);
504     } else if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) {
505         if (!string)
506             return;
507         m_object->setValue(string);
508    } else if (m_object->isTextControl()) {
509         if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) {
510             m_object->setSelectedText(string);
511         } else if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) {
512             m_object->setSelectedTextRange(AccessibilityObject::PlainTextRange(range.location, range.length));
513         } else if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) {
514             m_object->makeRangeVisible(AccessibilityObject::PlainTextRange(range.location, range.length));
515         }
516     }
517 }
518
519 static RenderObject* rendererForView(NSView* view)
520 {
521     if (![view conformsToProtocol:@protocol(WebCoreFrameView)])
522         return 0;
523
524     NSView<WebCoreFrameView>* frameView = (NSView<WebCoreFrameView>*)view;
525     Frame* frame = [frameView _web_frame];
526     if (!frame)
527         return 0;
528
529     Node* node = frame->document()->ownerElement();
530     if (!node)
531         return 0;
532
533     return node->renderer();
534 }
535
536 - (id)_accessibilityParentForSubview:(NSView*)subview
537 {   
538     RenderObject* renderer = rendererForView(subview);
539     if (!renderer)
540         return nil;
541         
542     AccessibilityObject* obj = renderer->document()->axObjectCache()->get(renderer);
543     if (obj)
544         return obj->parentObjectUnignored()->wrapper();
545     return nil;
546 }
547
548 - (NSString*)accessibilityActionDescription:(NSString*)action
549 {
550     // we have no custom actions
551     return NSAccessibilityActionDescription(action);
552 }
553
554 - (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter
555 {
556     WebCoreTextMarker* textMarker = nil;
557     WebCoreTextMarkerRange* textMarkerRange = nil;
558     NSNumber* number = nil;
559     NSArray* array = nil;
560     RefPtr<AccessibilityObject> uiElement = 0;
561     NSPoint point = NSZeroPoint;
562     bool pointSet = false;
563     NSRange range = {0, 0};
564     bool rangeSet = false;
565     
566     RenderObject* renderer = m_object->renderer();
567
568     // basic parameter validation
569     if (!renderer || !attribute || !parameter)
570         return nil;
571
572     // common parameter type check/casting.  Nil checks in handlers catch wrong type case.
573     // NOTE: This assumes nil is not a valid parameter, because it is indistinguishable from
574     // a parameter of the wrong type.
575     if ([[WebCoreViewFactory sharedFactory] objectIsTextMarker:parameter])
576         textMarker = (WebCoreTextMarker*) parameter;
577
578     else if ([[WebCoreViewFactory sharedFactory] objectIsTextMarkerRange:parameter])
579         textMarkerRange = (WebCoreTextMarkerRange*) parameter;
580
581     else if ([parameter isKindOfClass:[AccessibilityObjectWrapper self]])
582         uiElement = [(AccessibilityObjectWrapper*)parameter accessibilityObject];
583
584     else if ([parameter isKindOfClass:[NSNumber self]])
585         number = parameter;
586
587     else if ([parameter isKindOfClass:[NSArray self]])
588         array = parameter;
589
590     else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSPoint)) == 0) {
591         pointSet = true;
592         point = [(NSValue*)parameter pointValue];
593
594     } else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSRange)) == 0) {
595         rangeSet = true;
596         range = [(NSValue*)parameter rangeValue];
597
598     } else {
599         // got a parameter of a type we never use
600         // NOTE: No ASSERT_NOT_REACHED because this can happen accidentally
601         // while using accesstool (e.g.), forcing you to start over
602         return nil;
603     }
604     
605     // Convert values to WebCore types
606     VisiblePosition visiblePos = m_object->visiblePositionForTextMarker(textMarker);
607     int intNumber = [number intValue];
608     VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
609     IntPoint webCorePoint = IntPoint(point);
610     AccessibilityObject::PlainTextRange plainTextRange = AccessibilityObject::PlainTextRange(range.location, range.length);
611
612     // dispatch
613     if ([attribute isEqualToString: @"AXUIElementForTextMarker"])
614         return m_object->doAXUIElementForTextMarker(visiblePos)->wrapper();
615
616     if ([attribute isEqualToString: @"AXTextMarkerRangeForUIElement"]) {
617         VisiblePositionRange vpRange = uiElement.get()->visiblePositionRange();
618         return (id)(m_object->textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end));
619     }
620
621     if ([attribute isEqualToString: @"AXLineForTextMarker"])
622         return [NSNumber numberWithUnsignedInt:m_object->doAXLineForTextMarker(visiblePos)];
623
624     if ([attribute isEqualToString: @"AXTextMarkerRangeForLine"]) {
625         VisiblePositionRange vpRange = m_object->doAXTextMarkerRangeForLine(intNumber);
626         return (id)(m_object->textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end));
627     }
628
629     if ([attribute isEqualToString: @"AXStringForTextMarkerRange"])
630         return m_object->doAXStringForTextMarkerRange(visiblePosRange);
631
632     if ([attribute isEqualToString: @"AXTextMarkerForPosition"])
633         return pointSet ? m_object->textMarkerForVisiblePosition(m_object->doAXTextMarkerForPosition(webCorePoint)) : nil;
634
635     if ([attribute isEqualToString: @"AXBoundsForTextMarkerRange"]) {
636         NSRect rect = m_object->doAXBoundsForTextMarkerRange(visiblePosRange);
637         return [NSValue valueWithRect:rect];
638     }
639
640     if ([attribute isEqualToString: @"AXAttributedStringForTextMarkerRange"])
641         return m_object->doAXAttributedStringForTextMarkerRange(textMarkerRange);
642
643     if ([attribute isEqualToString: @"AXTextMarkerRangeForUnorderedTextMarkers"]) {
644         if ([array count] < 2)
645             return nil;
646
647         WebCoreTextMarker* textMarker1 = (WebCoreTextMarker*) [array objectAtIndex:0];
648         WebCoreTextMarker* textMarker2 = (WebCoreTextMarker*) [array objectAtIndex:1];
649         if (![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker1] 
650             || ![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker2])
651             return nil;
652
653         VisiblePosition visiblePos1 = m_object->visiblePositionForTextMarker(textMarker1);
654         VisiblePosition visiblePos2 = m_object->visiblePositionForTextMarker(textMarker2);
655         VisiblePositionRange vpRange = m_object->doAXTextMarkerRangeForUnorderedTextMarkers(visiblePos1, visiblePos2);
656         return (id)(m_object->textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end));
657     }
658
659     if ([attribute isEqualToString: @"AXNextTextMarkerForTextMarker"])
660         return m_object->textMarkerForVisiblePosition(m_object->doAXNextTextMarkerForTextMarker(visiblePos));
661
662     if ([attribute isEqualToString: @"AXPreviousTextMarkerForTextMarker"])
663         return m_object->textMarkerForVisiblePosition(m_object->doAXPreviousTextMarkerForTextMarker(visiblePos));
664
665     if ([attribute isEqualToString: @"AXLeftWordTextMarkerRangeForTextMarker"]) {
666         VisiblePositionRange vpRange = m_object->doAXLeftWordTextMarkerRangeForTextMarker(visiblePos);
667         return (id)(m_object->textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end));
668     }
669
670     if ([attribute isEqualToString: @"AXRightWordTextMarkerRangeForTextMarker"]) {
671         VisiblePositionRange vpRange = m_object->doAXRightWordTextMarkerRangeForTextMarker(visiblePos);
672         return (id)(m_object->textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end));
673     }
674
675     if ([attribute isEqualToString: @"AXLeftLineTextMarkerRangeForTextMarker"]) {
676         VisiblePositionRange vpRange = m_object->doAXLeftLineTextMarkerRangeForTextMarker(visiblePos);
677         return (id)(m_object->textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end));
678     }
679
680     if ([attribute isEqualToString: @"AXRightLineTextMarkerRangeForTextMarker"]) {
681         VisiblePositionRange vpRange = m_object->doAXRightLineTextMarkerRangeForTextMarker(visiblePos);
682         return (id)(m_object->textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end));
683     }
684
685     if ([attribute isEqualToString: @"AXSentenceTextMarkerRangeForTextMarker"]) {
686         VisiblePositionRange vpRange = m_object->doAXSentenceTextMarkerRangeForTextMarker(visiblePos);
687         return (id)(m_object->textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end));
688     }
689
690     if ([attribute isEqualToString: @"AXParagraphTextMarkerRangeForTextMarker"]) {
691         VisiblePositionRange vpRange = m_object->doAXParagraphTextMarkerRangeForTextMarker(visiblePos);
692         return (id)(m_object->textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end));
693     }
694
695     if ([attribute isEqualToString: @"AXNextWordEndTextMarkerForTextMarker"])
696         return m_object->textMarkerForVisiblePosition(m_object->doAXNextWordEndTextMarkerForTextMarker(visiblePos));
697
698     if ([attribute isEqualToString: @"AXPreviousWordStartTextMarkerForTextMarker"])
699         return m_object->textMarkerForVisiblePosition(m_object->doAXPreviousWordStartTextMarkerForTextMarker(visiblePos));
700
701     if ([attribute isEqualToString: @"AXNextLineEndTextMarkerForTextMarker"])
702         return m_object->textMarkerForVisiblePosition(m_object->doAXNextLineEndTextMarkerForTextMarker(visiblePos));
703
704     if ([attribute isEqualToString: @"AXPreviousLineStartTextMarkerForTextMarker"])
705         return m_object->textMarkerForVisiblePosition(m_object->doAXPreviousLineStartTextMarkerForTextMarker(visiblePos));
706        
707     if ([attribute isEqualToString: @"AXNextSentenceEndTextMarkerForTextMarker"])
708         return m_object->textMarkerForVisiblePosition(m_object->doAXNextSentenceEndTextMarkerForTextMarker(visiblePos));
709
710     if ([attribute isEqualToString: @"AXPreviousSentenceStartTextMarkerForTextMarker"])
711         return m_object->textMarkerForVisiblePosition(m_object->doAXPreviousSentenceStartTextMarkerForTextMarker(visiblePos));
712        
713     if ([attribute isEqualToString: @"AXNextParagraphEndTextMarkerForTextMarker"])
714         return m_object->textMarkerForVisiblePosition(m_object->doAXNextParagraphEndTextMarkerForTextMarker(visiblePos));
715
716     if ([attribute isEqualToString: @"AXPreviousParagraphStartTextMarkerForTextMarker"])
717         return m_object->textMarkerForVisiblePosition(m_object->doAXPreviousParagraphStartTextMarkerForTextMarker(visiblePos));
718
719     if ([attribute isEqualToString: @"AXStyleTextMarkerRangeForTextMarker"]) {
720         VisiblePositionRange vpRange = m_object->doAXStyleTextMarkerRangeForTextMarker(visiblePos);
721         return (id)(m_object->textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end));
722     }
723
724     if ([attribute isEqualToString: @"AXLengthForTextMarkerRange"]) {
725         int length = m_object->doAXLengthForTextMarkerRange(visiblePosRange);
726         if (length < 0)
727             return nil;
728         return [NSNumber numberWithInt:length];
729     }
730
731     if (m_object->isTextControl()) {
732         if ([attribute isEqualToString: (NSString*)kAXLineForIndexParameterizedAttribute])
733             return [NSNumber numberWithUnsignedInt: m_object->doAXLineForIndex(intNumber)];
734
735         if ([attribute isEqualToString: (NSString*)kAXRangeForLineParameterizedAttribute]) {
736             AccessibilityObject::PlainTextRange textRange = m_object->doAXRangeForLine(intNumber);
737             return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
738         }
739        
740         if ([attribute isEqualToString: (NSString*)kAXStringForRangeParameterizedAttribute])
741             return rangeSet ? (id)(m_object->doAXStringForRange(plainTextRange)) : nil;
742
743         if ([attribute isEqualToString: (NSString*)kAXRangeForPositionParameterizedAttribute]) {
744             if (!pointSet)
745                 return nil;
746             AccessibilityObject::PlainTextRange textRange = m_object->doAXRangeForPosition(webCorePoint);
747             return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
748         }
749
750         if ([attribute isEqualToString: (NSString*)kAXRangeForIndexParameterizedAttribute]) {
751             AccessibilityObject::PlainTextRange textRange = m_object->doAXRangeForIndex(intNumber);
752             return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
753         }
754
755         if ([attribute isEqualToString: (NSString*)kAXBoundsForRangeParameterizedAttribute]) {
756             if (!rangeSet)
757                 return nil;
758             NSRect rect = m_object->doAXBoundsForRange(plainTextRange);
759             return [NSValue valueWithRect:rect];
760         }
761
762         if ([attribute isEqualToString: (NSString*)kAXRTFForRangeParameterizedAttribute])
763             return rangeSet ? m_object->doAXRTFForRange(range) : nil;
764
765         if ([attribute isEqualToString: (NSString*)kAXAttributedStringForRangeParameterizedAttribute])
766             return rangeSet ? m_object->doAXAttributedStringForRange(range) : nil;
767        
768         if ([attribute isEqualToString: (NSString*)kAXStyleRangeForIndexParameterizedAttribute]) {
769             AccessibilityObject::PlainTextRange textRange = m_object->doAXStyleRangeForIndex(intNumber);
770             return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
771         }
772     }
773
774     return nil;
775 }
776
777 -(BOOL) accessibilityShouldUseUniqueId
778 {
779     return m_object->accessibilityShouldUseUniqueId();
780 }
781
782 @end