Reviewed by Darin, Ken.
[WebKit-https.git] / WebCore / kwq / KWQAccObject.mm
1 /*
2  * Copyright (C) 2004 Apple Computer, 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 // need this until accesstool supports arrays of markers
27 #define MARKERARRAY_SELF_TEST 0
28
29 // for AXTextMarker support
30 #import <ApplicationServices/ApplicationServicesPriv.h>
31
32 #include <mach-o/dyld.h>
33 #if OMIT_TIGER_FEATURES
34 // no parameterized attributes in Panther... they were introduced in Tiger
35 #else
36 typedef AXTextMarkerRef (*TextMarkerFromTextMarkerRangeProc) (AXTextMarkerRangeRef theTextMarkerRange);
37 extern "C" AXUIElementRef NSAccessibilityCreateAXUIElementRef(id element);
38 #endif
39
40 #import "KWQAccObject.h"
41 #import "KWQAccObjectCache.h"
42 #import "KWQAssertions.h"
43 #import "KWQFoundationExtras.h"
44 #import "KWQWidget.h"
45
46 #import "dom_docimpl.h"
47 #import "dom_elementimpl.h"
48 #import "html_inlineimpl.h"
49 #import "html_imageimpl.h"
50 #import "dom_string.h"
51 #import "dom2_range.h"
52 #import "htmlattrs.h"
53 #import "htmltags.h"
54 #import "khtmlview.h"
55 #import "khtml_part.h"
56 #import "render_canvas.h"
57 #import "render_object.h"
58 #import "render_image.h"
59 #import "render_list.h"
60 #import "render_style.h"
61 #import "render_text.h"
62 #import "selection.h"
63 #import "kjs_html.h"
64 #import "text_granularity.h"
65 #import "visible_position.h"
66 #import "visible_text.h"
67 #import "visible_units.h"
68 #import "html_miscimpl.h"
69 #import "qptrstack.h"
70
71 using DOM::DocumentImpl;
72 using DOM::ElementImpl;
73 using DOM::HTMLAnchorElementImpl;
74 using DOM::HTMLMapElementImpl;
75 using DOM::HTMLAreaElementImpl;
76 using DOM::HTMLCollection;
77 using DOM::HTMLCollectionImpl;
78 using DOM::Node;
79 using DOM::NodeImpl;
80 using DOM::Position;
81 using DOM::Range;
82 using DOM::DOMString;
83
84 using khtml::plainText;
85 using khtml::EVerticalAlign;
86 using khtml::RenderObject;
87 using khtml::RenderWidget;
88 using khtml::RenderCanvas;
89 using khtml::RenderText;
90 using khtml::RenderBlock;
91 using khtml::RenderListMarker;
92 using khtml::RenderImage;
93 using khtml::RenderStyle;
94 using khtml::Selection;
95 using khtml::TextIterator;
96 using khtml::VisiblePosition;
97
98 /* NSAccessibilityDescriptionAttribute is only defined on 10.4 and newer */
99 #if BUILDING_ON_PANTHER
100 #define ACCESSIBILITY_DESCRIPTION_ATTRIBUTE @"AXDescription"
101 #else
102 #define ACCESSIBILITY_DESCRIPTION_ATTRIBUTE NSAccessibilityDescriptionAttribute
103 #endif
104
105 // FIXME: This will eventually need to really localize.
106 #define UI_STRING(string, comment) ((NSString *)[NSString stringWithUTF8String:(string)])
107
108 @implementation KWQAccObject
109
110 -(id)initWithRenderer:(RenderObject*)renderer
111 {
112     [super init];
113     m_renderer = renderer;
114     return self;
115 }
116
117 -(BOOL)detached
118 {
119     return !m_renderer;
120 }
121
122 - (BOOL)accessibilityShouldUseUniqueId {
123     return m_renderer && m_renderer->isCanvas();
124 }
125
126 extern "C" void NSAccessibilityUnregisterUniqueIdForUIElement(id element);
127 -(void)detach
128 {
129     if ([self accessibilityShouldUseUniqueId])
130         NSAccessibilityUnregisterUniqueIdForUIElement(self);
131     [m_data release];
132     m_data = 0;
133     [self removeAccObjectID];
134     m_renderer = 0;
135     [self clearChildren];
136 }
137
138 -(id)data
139 {
140     return m_data;
141 }
142
143 -(void)setData:(id)data
144 {
145     if (!m_renderer)
146         return;
147
148     [data retain];
149     [m_data release];
150     m_data = data;
151 }
152
153 -(HTMLAnchorElementImpl*)anchorElement
154 {
155     // return already-known anchor for image areas
156     if (m_areaElement)
157         return m_areaElement;
158
159     // search up the render tree for a RenderObject with a DOM node.  Defer to an earlier continuation, though.
160     RenderObject* currRenderer;
161     for (currRenderer = m_renderer; currRenderer && !currRenderer->element(); currRenderer = currRenderer->parent()) {
162         if (currRenderer->continuation())
163             return [currRenderer->document()->getAccObjectCache()->accObject(currRenderer->continuation()) anchorElement];
164     }
165     
166     // bail of none found
167     if (!currRenderer)
168         return 0;
169     
170     // search up the DOM tree for an anchor element
171     // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElementImpl
172     NodeImpl* elt = currRenderer->element();
173     for ( ; elt; elt = elt->parentNode()) {
174         if (elt->hasAnchor() && elt->renderer() && !elt->renderer()->isImage())
175             return static_cast<HTMLAnchorElementImpl*>(elt);
176     }
177   
178     return 0;
179 }
180
181 -(KWQAccObject*)firstChild
182 {
183     if (!m_renderer || !m_renderer->firstChild())
184         return nil;
185     return m_renderer->document()->getAccObjectCache()->accObject(m_renderer->firstChild());
186 }
187
188 -(KWQAccObject*)lastChild
189 {
190     if (!m_renderer || !m_renderer->lastChild())
191         return nil;
192     return m_renderer->document()->getAccObjectCache()->accObject(m_renderer->lastChild());
193 }
194
195 -(KWQAccObject*)previousSibling
196 {
197     if (!m_renderer || !m_renderer->previousSibling())
198         return nil;
199     return m_renderer->document()->getAccObjectCache()->accObject(m_renderer->previousSibling());
200 }
201
202 -(KWQAccObject*)nextSibling
203 {
204     if (!m_renderer || !m_renderer->nextSibling())
205         return nil;
206     return m_renderer->document()->getAccObjectCache()->accObject(m_renderer->nextSibling());
207 }
208
209 -(KWQAccObject*)parentObject
210 {
211     if (m_areaElement)
212         return m_renderer->document()->getAccObjectCache()->accObject(m_renderer);
213
214     if (!m_renderer || !m_renderer->parent())
215         return nil;
216     return m_renderer->document()->getAccObjectCache()->accObject(m_renderer->parent());
217 }
218
219 -(KWQAccObject*)parentObjectUnignored
220 {
221     KWQAccObject* obj = [self parentObject];
222     if ([obj accessibilityIsIgnored])
223         return [obj parentObjectUnignored];
224     else
225         return obj;
226 }
227
228 -(void)addChildrenToArray:(NSMutableArray*)array
229 {
230     // nothing to add if there is no RenderObject
231     if (!m_renderer)
232         return;
233     
234     // try to add RenderWidget's children, but fall thru if there are none
235     if (m_renderer->isWidget()) {
236         RenderWidget* renderWidget = static_cast<RenderWidget*>(m_renderer);
237         QWidget* widget = renderWidget->widget();
238         if (widget) {
239             NSArray* childArr = [(widget->getOuterView()) accessibilityAttributeValue: NSAccessibilityChildrenAttribute];
240             [array addObjectsFromArray: childArr];
241             return;
242         }
243     }
244     
245     // add all unignored acc children
246     for (KWQAccObject* obj = [self firstChild]; obj; obj = [obj nextSibling]) {
247         if ([obj accessibilityIsIgnored])
248             [obj addChildrenToArray: array];
249         else
250             [array addObject: obj];
251     }
252     
253     // for a RenderImage, add the <area> elements as individual accessibility objects
254     if (m_renderer->isImage() && !m_areaElement) {
255         HTMLMapElementImpl* map = static_cast<RenderImage*>(m_renderer)->imageMap();
256         if (map) {
257             QPtrStack<NodeImpl> nodeStack;
258             NodeImpl *current = map->firstChild();
259             while (1) {
260                 // make sure we have a node to process
261                 if (!current) {
262                     // done if there is no node and no remembered node
263                     if(nodeStack.isEmpty())
264                         break;
265                     
266                     // pop the previously processed parent
267                     current = nodeStack.pop();
268                     
269                     // move on
270                     current = current->nextSibling();
271                     continue;
272                 }
273                 
274                 // add an <area> element for this child if it has a link
275                 // NOTE: can't cache these because they all have the same renderer, which is the cache key, right?
276                 // plus there may be little reason to since they are being added to the handy array
277                 if (current->hasAnchor()) {
278                     KWQAccObject* obj = [[[KWQAccObject alloc] initWithRenderer: m_renderer] autorelease];
279                     obj->m_areaElement = static_cast<HTMLAreaElementImpl*>(current);
280                     [array addObject: obj];
281                 }
282                 
283                 // move on to children (if any) or next sibling
284                 NodeImpl *child = current->firstChild();
285                 if (child) {
286                     // switch to doing all the children, remember the parent to pop later
287                     nodeStack.push(current);
288                     current = child;
289                 }
290                 else
291                     current = current->nextSibling();
292             }
293         }
294     }
295 }
296
297 -(BOOL)isAttachment
298 {
299     // widgets are the replaced elements that we represent to AX as attachments
300     BOOL result = m_renderer->isWidget();
301     
302     // assert that a widget is a replaced element that is not an image
303     ASSERT(!result || (m_renderer->isReplaced() && !m_renderer->isImage()));
304     
305     return result;
306 }
307
308 -(NSView*)attachmentView
309 {
310     ASSERT(m_renderer->isReplaced() && m_renderer->isWidget() && !m_renderer->isImage());
311
312     RenderWidget* renderWidget = static_cast<RenderWidget*>(m_renderer);
313     QWidget* widget = renderWidget->widget();
314     if (widget)
315          return widget->getView();
316     
317     return nil;
318 }
319
320 -(NSString*)role
321 {
322     if (!m_renderer)
323         return NSAccessibilityUnknownRole;
324
325     if (m_areaElement)
326         return @"AXLink";
327     if (m_renderer->element() && m_renderer->element()->hasAnchor()) {
328         if (m_renderer->isImage())
329             return @"AXImageMap";
330         return @"AXLink";
331     }
332     if (m_renderer->isListMarker())
333         return @"AXListMarker";
334     if (m_renderer->element() && m_renderer->element()->isHTMLElement() &&
335         Node(m_renderer->element()).elementId() == ID_BUTTON)
336         return NSAccessibilityButtonRole;
337     if (m_renderer->isText())
338         return NSAccessibilityStaticTextRole;
339     if (m_renderer->isImage())
340         return NSAccessibilityImageRole;
341     if (m_renderer->isCanvas())
342         return @"AXWebArea";
343     if (m_renderer->isBlockFlow())
344         return NSAccessibilityGroupRole;
345     if ([self isAttachment])
346         return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleAttribute];
347
348     return NSAccessibilityUnknownRole;
349 }
350
351 -(NSString*)subrole
352 {
353     if ([self isAttachment])
354         return [[self attachmentView] accessibilityAttributeValue:NSAccessibilitySubroleAttribute];
355     
356     return nil;
357 }
358
359 -(NSString*)roleDescription
360 {
361 #if OMIT_TIGER_FEATURES
362     // We don't need role descriptions on Panther and we don't have the call
363     // to get at localized ones anyway. At some point we may want to conditionally
364     // compile this entire file instead, but this is OK too.
365     return nil;
366 #else
367     if (!m_renderer)
368         return nil;
369
370     // attachments have the AXImage role, but a different subrole
371     if ([self isAttachment])
372         return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleDescriptionAttribute];
373     
374     // FIXME 3517227: These need to be localized (UI_STRING here is a dummy macro)
375     // FIXME 3447564: It would be better to call some AppKit API to get these strings
376     // (which would be the best way to localize them)
377     
378     NSString *role = [self role];
379     if ([role isEqualToString:NSAccessibilityButtonRole])
380         return NSAccessibilityRoleDescription(NSAccessibilityButtonRole, nil);
381     
382     if ([role isEqualToString:NSAccessibilityStaticTextRole])
383         return NSAccessibilityRoleDescription(NSAccessibilityStaticTextRole, nil);
384
385     if ([role isEqualToString:NSAccessibilityImageRole])
386         return NSAccessibilityRoleDescription(NSAccessibilityImageRole, nil);
387     
388     if ([role isEqualToString:NSAccessibilityGroupRole])
389         return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil);
390     
391     if ([role isEqualToString:@"AXWebArea"])
392         return UI_STRING("web area", "accessibility role description for web area");
393     
394     if ([role isEqualToString:@"AXLink"])
395         return UI_STRING("link", "accessibility role description for link");
396     
397     if ([role isEqualToString:@"AXListMarker"])
398         return UI_STRING("list marker", "accessibility role description for list marker");
399     
400     if ([role isEqualToString:@"AXImageMap"])
401         return UI_STRING("image map", "accessibility role description for image map");
402     
403     return NSAccessibilityRoleDescription(NSAccessibilityUnknownRole, nil);
404 #endif
405 }
406
407 -(NSString*)helpText
408 {
409     if (!m_renderer)
410         return nil;
411
412     if (m_areaElement) {
413         QString summary = static_cast<ElementImpl*>(m_areaElement)->getAttribute(ATTR_SUMMARY).string();
414         if (!summary.isEmpty())
415             return summary.getNSString();
416         QString title = static_cast<ElementImpl*>(m_areaElement)->getAttribute(ATTR_TITLE).string();
417         if (!title.isEmpty())
418             return title.getNSString();
419     }
420
421     for (RenderObject* curr = m_renderer; curr; curr = curr->parent()) {
422         if (curr->element() && curr->element()->isHTMLElement()) {
423             QString summary = static_cast<ElementImpl*>(curr->element())->getAttribute(ATTR_SUMMARY).string();
424             if (!summary.isEmpty())
425                 return summary.getNSString();
426             QString title = static_cast<ElementImpl*>(curr->element())->getAttribute(ATTR_TITLE).string();
427             if (!title.isEmpty())
428                 return title.getNSString();
429         }
430     }
431
432     return nil;
433 }
434
435 -(NSString*)textUnderElement
436 {
437     if (!m_renderer)
438         return nil;
439
440     NodeImpl* e = m_renderer->element();
441     DocumentImpl* d = m_renderer->document();
442     if (e && d) {
443         KHTMLPart* p = d->part();
444         if (p) {
445             // catch stale KWQAccObject (see <rdar://problem/3960196>)
446             if (p->document().handle() != d)
447                 return nil;
448                 
449             Range r(p->document());
450             if (m_renderer->isText()) {
451                 r.setStartBefore(e);
452                 r.setEndAfter(e);
453                 return p->text(r).getNSString();
454             }
455             if (e->firstChild()) {
456                 r.setStartBefore(e->firstChild());
457                 r.setEndAfter(e->lastChild());
458                 return p->text(r).getNSString();
459             }
460         }
461     }
462
463     return nil;
464 }
465
466 -(NSString*)value
467 {
468     if (!m_renderer || m_areaElement)
469         return nil;
470
471     if (m_renderer->isText())
472         return [self textUnderElement];
473     
474     if (m_renderer->isListMarker())
475         return static_cast<RenderListMarker*>(m_renderer)->text().getNSString();
476
477     if (m_renderer->isCanvas()) {
478         KWQKHTMLPart *docPart = KWQ(m_renderer->document()->part());
479         if (!docPart)
480             return nil;
481         
482         VisiblePosition startVisiblePosition = VisiblePosition(m_renderer->positionForCoordinates (0, 0, nil));
483         VisiblePosition endVisiblePosition   = VisiblePosition(m_renderer->positionForCoordinates (LONG_MAX, LONG_MAX, nil));
484         QString qString   = plainText(makeRange(startVisiblePosition, endVisiblePosition));
485         
486         // transform it to a CFString and return that
487         return (id)qString.getCFString();
488     }
489     
490     if ([self isAttachment])
491         return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityValueAttribute];
492         
493     // FIXME: We might need to implement a value here for more types
494     // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
495     // this would require subclassing or making accessibilityAttributeNames do something other than return a
496     // single static array.
497     return nil;
498 }
499
500 -(NSString*)title
501 {
502     if (!m_renderer || m_areaElement || !m_renderer->element())
503         return nil;
504     
505     if (m_renderer->element()->isHTMLElement() && Node(m_renderer->element()).elementId() == ID_BUTTON)
506         return [self textUnderElement];
507     if (m_renderer->element()->hasAnchor())
508         return [self textUnderElement];
509     if ([self isAttachment])
510         return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityTitleAttribute];
511     
512     return nil;
513 }
514
515 -(NSString*)accessibilityDescription
516 {
517     if (!m_renderer || m_areaElement)
518         return nil;
519     
520     if (m_renderer->isImage()) {
521         if (m_renderer->element() && m_renderer->element()->isHTMLElement()) {
522             QString alt = static_cast<ElementImpl*>(m_renderer->element())->getAttribute(ATTR_ALT).string();
523             return !alt.isEmpty() ? alt.getNSString() : nil;
524         }
525     } else if ([self isAttachment])
526         return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityTitleAttribute];
527     
528     return nil;
529 }
530
531 static QRect boundingBoxRect(RenderObject* obj)
532 {
533     QRect rect(0,0,0,0);
534     if (obj) {
535         if (obj->isInlineContinuation())
536             obj = obj->element()->renderer();
537         QValueList<QRect> rects;
538         int x = 0, y = 0;
539         obj->absolutePosition(x, y);
540         obj->absoluteRects(rects, x, y);
541         for (QValueList<QRect>::ConstIterator it = rects.begin(); it != rects.end(); ++it) {
542             QRect r = *it;
543             if (r.isValid()) {
544                 if (rect.isEmpty())
545                     rect = r;
546                 else
547                     rect.unite(r);
548             }
549         }
550     }
551     return rect;
552 }
553
554 -(NSValue*)position
555 {
556     QRect rect = m_areaElement ? m_areaElement->getRect(m_renderer) : boundingBoxRect(m_renderer);
557     
558     // The Cocoa accessibility API wants the lower-left corner, not the upper-left, so we add in our height.
559     NSPoint point = NSMakePoint(rect.x(), rect.y() + rect.height());
560     if (m_renderer && m_renderer->canvas() && m_renderer->canvas()->view()) {
561         NSView* view = m_renderer->canvas()->view()->getDocumentView();
562         point = [[view window] convertBaseToScreen: [view convertPoint: point toView:nil]];
563     }
564     return [NSValue valueWithPoint: point];
565 }
566
567 -(NSValue*)size
568 {
569     QRect rect = m_areaElement ? m_areaElement->getRect(m_renderer) : boundingBoxRect(m_renderer);
570     return [NSValue valueWithSize: NSMakeSize(rect.width(), rect.height())];
571 }
572
573 -(BOOL)accessibilityIsIgnored
574 {
575     if (!m_renderer || m_renderer->style()->visibility() != khtml::VISIBLE)
576         return YES;
577
578     // NOTE: BRs always have text boxes now, so the text box check here can be removed
579     if (m_renderer->isText())
580         return m_renderer->isBR() || !static_cast<RenderText*>(m_renderer)->firstTextBox();
581     
582     // delegate to the attachment
583     if ([self isAttachment])
584         return [[self attachmentView] accessibilityIsIgnored];
585         
586     if (m_areaElement || (m_renderer->element() && m_renderer->element()->hasAnchor()))
587         return NO;
588
589     if (m_renderer->isBlockFlow() && m_renderer->childrenInline())
590         return !static_cast<RenderBlock*>(m_renderer)->firstLineBox();
591
592     return (!m_renderer->isListMarker() && !m_renderer->isCanvas() && 
593             !m_renderer->isImage() &&
594             !(m_renderer->element() && m_renderer->element()->isHTMLElement() &&
595               Node(m_renderer->element()).elementId() == ID_BUTTON));
596 }
597
598 - (NSArray *)accessibilityAttributeNames
599 {
600     static NSArray* attributes = nil;
601     static NSArray* anchorAttrs = nil;
602     static NSArray* webAreaAttrs = nil;
603     if (attributes == nil) {
604         attributes = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute,
605             NSAccessibilitySubroleAttribute,
606             NSAccessibilityRoleDescriptionAttribute,
607             NSAccessibilityChildrenAttribute,
608             NSAccessibilityHelpAttribute,
609             NSAccessibilityParentAttribute,
610             NSAccessibilityPositionAttribute,
611             NSAccessibilitySizeAttribute,
612             NSAccessibilityTitleAttribute,
613             ACCESSIBILITY_DESCRIPTION_ATTRIBUTE,
614             NSAccessibilityValueAttribute,
615             NSAccessibilityFocusedAttribute,
616             NSAccessibilityEnabledAttribute,
617             NSAccessibilityWindowAttribute,
618 #if OMIT_TIGER_FEATURES
619 // no parameterized attributes in Panther... they were introduced in Tiger
620 #else
621             (NSString *) kAXSelectedTextMarkerRangeAttribute,
622 //          (NSString *) kAXVisibleCharacterTextMarkerRangeAttribute,    // NOTE: <rdar://problem/3942582>
623             (NSString *) kAXStartTextMarkerAttribute,
624             (NSString *) kAXEndTextMarkerAttribute,
625 #endif
626             nil];
627     }
628     if (anchorAttrs == nil) {
629         anchorAttrs = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute,
630             NSAccessibilityRoleDescriptionAttribute,
631             NSAccessibilityChildrenAttribute,
632             NSAccessibilityHelpAttribute,
633             NSAccessibilityParentAttribute,
634             NSAccessibilityPositionAttribute,
635             NSAccessibilitySizeAttribute,
636             NSAccessibilityTitleAttribute,
637             NSAccessibilityValueAttribute,
638             NSAccessibilityFocusedAttribute,
639             NSAccessibilityEnabledAttribute,
640             NSAccessibilityWindowAttribute,
641             @"AXURL",
642 #if OMIT_TIGER_FEATURES
643 // no parameterized attributes in Panther... they were introduced in Tiger
644 #else
645             (NSString *) kAXSelectedTextMarkerRangeAttribute,
646 //          (NSString *) kAXVisibleCharacterTextMarkerRangeAttribute,     // NOTE: <rdar://problem/3942582>
647             (NSString *) kAXStartTextMarkerAttribute,
648             (NSString *) kAXEndTextMarkerAttribute,
649 #endif
650             nil];
651     }
652     if (webAreaAttrs == nil) {
653         webAreaAttrs = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute,
654             NSAccessibilityRoleDescriptionAttribute,
655             NSAccessibilityChildrenAttribute,
656             NSAccessibilityHelpAttribute,
657             NSAccessibilityParentAttribute,
658             NSAccessibilityPositionAttribute,
659             NSAccessibilitySizeAttribute,
660             NSAccessibilityTitleAttribute,
661             NSAccessibilityValueAttribute,
662             NSAccessibilityFocusedAttribute,
663             NSAccessibilityEnabledAttribute,
664             NSAccessibilityWindowAttribute,
665             @"AXLinkUIElements",
666             @"AXLoaded",
667             @"AXLayoutCount",
668 #if OMIT_TIGER_FEATURES
669 // no parameterized attributes in Panther... they were introduced in Tiger
670 #else
671             (NSString *) kAXSelectedTextMarkerRangeAttribute,
672 //          (NSString *) kAXVisibleCharacterTextMarkerRangeAttribute,     // NOTE: NOTE: <rdar://problem/3942582>
673             (NSString *) kAXStartTextMarkerAttribute,
674             (NSString *) kAXEndTextMarkerAttribute,
675 #endif
676             nil];
677     }
678     
679     if (m_renderer && m_renderer->isCanvas())
680         return webAreaAttrs;
681     if (m_areaElement || (m_renderer && !m_renderer->isImage() && m_renderer->element() && m_renderer->element()->hasAnchor()))
682         return anchorAttrs;
683     return attributes;
684 }
685
686 - (NSArray*)accessibilityActionNames
687 {
688     static NSArray* actions = nil;
689     if ([self anchorElement]) {
690         if (actions == nil)
691             actions = [[NSArray alloc] initWithObjects: NSAccessibilityPressAction, nil];
692         return actions;
693     }
694     return nil;
695 }
696
697 - (NSString *)accessibilityActionDescription:(NSString *)action
698 {
699 #if OMIT_TIGER_FEATURES
700     // We don't need action descriptions on Panther and we don't have the call
701     // to get at localized ones anyway. At some point we may want to conditionally
702     // compile this entire file instead, but this is OK too.
703     return nil;
704 #else
705     // we have no custom actions
706     return NSAccessibilityActionDescription(action);
707 #endif
708 }
709
710 - (void)accessibilityPerformAction:(NSString *)action
711 {
712     // We only have the one action (press).
713     if ([action isEqualToString:NSAccessibilityPressAction]) {
714         // Locate the anchor element. If it doesn't exist, just bail.
715         HTMLAnchorElementImpl* anchorElt = [self anchorElement];
716         if (!anchorElt)
717             return;
718
719         DocumentImpl* d = anchorElt->getDocument();
720         if (d) {
721             KHTMLPart* p = d->part();
722             if (p) {
723                 KWQ(p)->prepareForUserAction();
724             }
725         }
726
727         anchorElt->click();
728     }
729 }
730
731 #if OMIT_TIGER_FEATURES
732 // no parameterized attributes in Panther... they were introduced in Tiger
733 #else
734 - (AXTextMarkerRangeRef) textMarkerRangeFromMarkers: (AXTextMarkerRef) textMarker1 andEndMarker:(AXTextMarkerRef) textMarker2
735 {
736     AXTextMarkerRangeRef textMarkerRange;
737     
738     // create the range
739     textMarkerRange = AXTextMarkerRangeCreate (nil, textMarker1, textMarker2);
740
741     // autorelease it because we will never see it again
742     KWQCFAutorelease(textMarkerRange);
743     return textMarkerRange;
744 }
745
746 - (AXTextMarkerRef) textMarkerForVisiblePosition: (VisiblePosition)visiblePos
747 {
748     if (visiblePos.isNull())
749         return nil;
750         
751     return m_renderer->document()->getAccObjectCache()->textMarkerForVisiblePosition(visiblePos);
752 }
753
754 - (VisiblePosition) visiblePositionForTextMarker: (AXTextMarkerRef)textMarker
755 {
756     return m_renderer->document()->getAccObjectCache()->visiblePositionForTextMarker(textMarker);
757 }
758
759 - (AXTextMarkerRef) AXTextMarkerRangeCopyStartMarkerWrapper: (AXTextMarkerRangeRef)textMarkerRange
760 {
761     NSSymbol        axSymbol;
762     static TextMarkerFromTextMarkerRangeProc   axStartImpl = nil;
763     
764     if (axStartImpl == nil) {
765         axSymbol = NSLookupAndBindSymbol("_AXTextMarkerRangeCopyStartMarker");
766         if (axSymbol == nil)
767             axSymbol = NSLookupAndBindSymbol("_AXTextMarkerCopyStartMarker");
768         ASSERT(axSymbol);
769         axStartImpl = (TextMarkerFromTextMarkerRangeProc) NSAddressOfSymbol(axSymbol);
770         ASSERT(axStartImpl);
771     }
772     
773     return axStartImpl(textMarkerRange);
774 }
775
776 - (AXTextMarkerRef) AXTextMarkerRangeCopyEndMarkerWrapper: (AXTextMarkerRangeRef)textMarkerRange
777 {
778     NSSymbol        axSymbol;
779     static TextMarkerFromTextMarkerRangeProc   axEndImpl = nil;
780     
781     if (axEndImpl == nil) {
782         axSymbol = NSLookupAndBindSymbol("_AXTextMarkerRangeCopyEndMarker");
783         if (axSymbol == nil)
784             axSymbol = NSLookupAndBindSymbol("_AXTextMarkerCopyEndMarker");
785         ASSERT(axSymbol);
786         axEndImpl = (TextMarkerFromTextMarkerRangeProc) NSAddressOfSymbol(axSymbol);
787         ASSERT(axEndImpl);
788     }
789     
790     return axEndImpl(textMarkerRange);
791 }
792
793 - (VisiblePosition) visiblePositionForStartOfTextMarkerRange: (AXTextMarkerRangeRef)textMarkerRange
794 {
795     AXTextMarkerRef textMarker;
796     VisiblePosition visiblePos;
797
798     textMarker = [self AXTextMarkerRangeCopyStartMarkerWrapper:textMarkerRange];
799     visiblePos = [self visiblePositionForTextMarker:textMarker];
800     if (textMarker)
801         CFRelease(textMarker);
802     return visiblePos;
803 }
804
805 - (VisiblePosition) visiblePositionForEndOfTextMarkerRange: (AXTextMarkerRangeRef) textMarkerRange
806 {
807     AXTextMarkerRef textMarker;
808     VisiblePosition visiblePos;
809     
810     textMarker = [self AXTextMarkerRangeCopyEndMarkerWrapper:textMarkerRange];
811     visiblePos = [self visiblePositionForTextMarker:textMarker];
812     if (textMarker)
813         CFRelease(textMarker);
814     return visiblePos;
815 }
816
817 - (AXTextMarkerRangeRef) textMarkerRangeFromVisiblePositions: (VisiblePosition) startPosition andEndPos: (VisiblePosition) endPosition
818 {
819     AXTextMarkerRef startTextMarker = [self textMarkerForVisiblePosition: startPosition];
820     AXTextMarkerRef endTextMarker   = [self textMarkerForVisiblePosition: endPosition];
821     return [self textMarkerRangeFromMarkers: startTextMarker andEndMarker:endTextMarker];
822 }
823 #endif
824
825 - (DocumentImpl *)topDocument
826 {
827     return m_renderer->document()->topDocument();
828 }
829
830 - (RenderObject *)topRenderer
831 {
832     return m_renderer->document()->topDocument()->renderer();
833 }
834
835 - (KHTMLView *)topView
836 {
837     return m_renderer->document()->topDocument()->renderer()->canvas()->view();
838 }
839
840 - (id)accessibilityAttributeValue:(NSString *)attributeName
841 {
842     if (!m_renderer)
843         return nil;
844
845     if ([attributeName isEqualToString: NSAccessibilityRoleAttribute])
846         return [self role];
847
848     if ([attributeName isEqualToString: NSAccessibilitySubroleAttribute])
849         return [self subrole];
850
851     if ([attributeName isEqualToString: NSAccessibilityRoleDescriptionAttribute])
852         return [self roleDescription];
853     
854     if ([attributeName isEqualToString: NSAccessibilityParentAttribute]) {
855         if (m_renderer->isCanvas() && m_renderer->canvas() && m_renderer->canvas()->view())
856             return m_renderer->canvas()->view()->getView();
857         return [self parentObjectUnignored];
858     }
859
860     if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
861         if (!m_children) {
862             m_children = [NSMutableArray arrayWithCapacity: 8];
863             [m_children retain];
864             [self addChildrenToArray: m_children];
865         }
866         return m_children;
867     }
868
869     if (m_renderer->isCanvas()) {
870         if ([attributeName isEqualToString: @"AXLinkUIElements"]) {
871             NSMutableArray* links = [NSMutableArray arrayWithCapacity: 32];
872             HTMLCollection coll(m_renderer->document(), HTMLCollectionImpl::DOC_LINKS);
873             if (coll.isNull())
874                 return links;
875             Node curr = coll.firstItem();
876             while (!curr.isNull()) {
877                 RenderObject* obj = curr.handle()->renderer();
878                 if (obj) {
879                     KWQAccObject *axobj = obj->document()->getAccObjectCache()->accObject(obj);
880                     ASSERT([[axobj role] isEqualToString:@"AXLink"]);
881                     if (![axobj accessibilityIsIgnored])
882                         [links addObject: axobj];
883                 }
884                 curr = coll.nextItem();
885             }
886             return links;
887         }
888         else if ([attributeName isEqualToString: @"AXLoaded"])
889             return [NSNumber numberWithBool: (!m_renderer->document()->tokenizer())];
890         else if ([attributeName isEqualToString: @"AXLayoutCount"])
891             return [NSNumber numberWithInt: (static_cast<RenderCanvas*>(m_renderer)->view()->layoutCount())];
892     }
893     
894     if ([attributeName isEqualToString: @"AXURL"] && 
895         (m_areaElement || (!m_renderer->isImage() && m_renderer->element() && m_renderer->element()->hasAnchor()))) {
896         HTMLAnchorElementImpl* anchor = [self anchorElement];
897         if (anchor) {
898             QString s = anchor->getAttribute(ATTR_HREF).string();
899             if (!s.isNull()) {
900                 s = anchor->getDocument()->completeURL(s);
901                 return s.getNSString();
902             }
903         }
904     }
905     
906     if ([attributeName isEqualToString: NSAccessibilityTitleAttribute])
907         return [self title];
908     
909     if ([attributeName isEqualToString: ACCESSIBILITY_DESCRIPTION_ATTRIBUTE])
910         return [self accessibilityDescription];
911     
912     if ([attributeName isEqualToString: NSAccessibilityValueAttribute])
913         return [self value];
914
915     if ([attributeName isEqualToString: NSAccessibilityHelpAttribute])
916         return [self helpText];
917     
918     if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute])
919         return [NSNumber numberWithBool: (m_renderer->element() && m_renderer->document()->focusNode() == m_renderer->element())];
920
921     if ([attributeName isEqualToString: NSAccessibilityEnabledAttribute])
922         return [NSNumber numberWithBool: YES];
923     
924     if ([attributeName isEqualToString: NSAccessibilitySizeAttribute])
925         return [self size];
926
927     if ([attributeName isEqualToString: NSAccessibilityPositionAttribute])
928         return [self position];
929
930     if ([attributeName isEqualToString: NSAccessibilityWindowAttribute]) {
931         if (m_renderer && m_renderer->canvas() && m_renderer->canvas()->view())
932             return [m_renderer->canvas()->view()->getView() window];
933         return nil;
934     }
935     
936 #if OMIT_TIGER_FEATURES
937 // no parameterized attributes in Panther... they were introduced in Tiger
938 #else
939     if ([attributeName isEqualToString: (NSString *) kAXSelectedTextMarkerRangeAttribute]) {
940         // get the selection from the document part
941         // NOTE: BUG support nested WebAreas, like in <http://webcourses.niu.edu/>
942         // (there is a web archive of this page attached to <rdar://problem/3888973>)
943         Selection   sel = [self topView]->part()->selection();
944         if (sel.isNone()) {
945             sel = m_renderer->document()->renderer()->canvas()->view()->part()->selection();
946             if (sel.isNone())
947                 return nil;
948         }
949             
950         // return a marker range for the selection start to end
951         VisiblePosition startPosition = VisiblePosition(sel.start());
952         VisiblePosition endPosition = VisiblePosition(sel.end());
953         return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
954     }
955     
956     if ([attributeName isEqualToString: (NSString *) kAXStartTextMarkerAttribute]) {
957         return (id) [self textMarkerForVisiblePosition: VisiblePosition([self topRenderer]->positionForCoordinates (0, 0, nil))];
958     }
959
960     if ([attributeName isEqualToString: (NSString *) kAXEndTextMarkerAttribute]) {
961         return (id) [self textMarkerForVisiblePosition: VisiblePosition([self topRenderer]->positionForCoordinates (LONG_MAX, LONG_MAX, nil))];
962     }
963 #endif
964
965     return nil;
966 }
967
968 #if OMIT_TIGER_FEATURES
969 // no parameterized attributes in Panther... they were introduced in Tiger
970 #else
971 - (NSArray *)accessibilityParameterizedAttributeNames
972 {
973     static NSArray* paramAttributes = nil;
974     if (paramAttributes == nil) {
975         paramAttributes = [[NSArray alloc] initWithObjects:
976             @"AXUIElementForTextMarker",
977             kAXLineForTextMarkerParameterizedAttribute,
978             kAXTextMarkerRangeForLineParameterizedAttribute,
979             kAXStringForTextMarkerRangeParameterizedAttribute,
980             kAXTextMarkerForPositionParameterizedAttribute,
981             kAXBoundsForTextMarkerRangeParameterizedAttribute,
982 //          kAXStyleTextMarkerRangeForTextMarkerParameterizedAttribute,           // NOTE: <rdar://problem/3942606>
983             kAXAttributedStringForTextMarkerRangeParameterizedAttribute,
984             kAXTextMarkerRangeForUnorderedTextMarkersParameterizedAttribute,
985             kAXNextTextMarkerForTextMarkerParameterizedAttribute,
986             kAXPreviousTextMarkerForTextMarkerParameterizedAttribute,
987             kAXLeftWordTextMarkerRangeForTextMarkerParameterizedAttribute,
988             kAXRightWordTextMarkerRangeForTextMarkerParameterizedAttribute,
989             kAXLeftLineTextMarkerRangeForTextMarkerParameterizedAttribute,
990             kAXRightLineTextMarkerRangeForTextMarkerParameterizedAttribute,
991             kAXSentenceTextMarkerRangeForTextMarkerParameterizedAttribute,
992             kAXParagraphTextMarkerRangeForTextMarkerParameterizedAttribute,
993             kAXNextWordEndTextMarkerForTextMarkerParameterizedAttribute,
994             kAXPreviousWordStartTextMarkerForTextMarkerParameterizedAttribute,
995             kAXNextLineEndTextMarkerForTextMarkerParameterizedAttribute,
996             kAXPreviousLineStartTextMarkerForTextMarkerParameterizedAttribute,
997             kAXNextSentenceEndTextMarkerForTextMarkerParameterizedAttribute,
998             kAXPreviousSentenceStartTextMarkerForTextMarkerParameterizedAttribute,
999             kAXNextParagraphEndTextMarkerForTextMarkerParameterizedAttribute,
1000             kAXPreviousParagraphStartTextMarkerForTextMarkerParameterizedAttribute,
1001             kAXLengthForTextMarkerRangeParameterizedAttribute,
1002             nil];
1003     }
1004
1005     return paramAttributes;
1006 }
1007
1008 - (id)doAXUIElementForTextMarker: (AXTextMarkerRef) textMarker
1009 {
1010     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1011     if (visiblePos.isNull())
1012         return nil;
1013
1014     RenderObject * obj = visiblePos.position().node()->renderer();
1015     if (!obj)
1016         return nil;
1017     
1018     return obj->document()->getAccObjectCache()->accObject(obj);
1019 }
1020
1021 - (id)doAXLineForTextMarker: (AXTextMarkerRef) textMarker
1022 {
1023     unsigned int    lineCount = 0;
1024     VisiblePosition savedVisiblePos;
1025     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1026     if (visiblePos.isNull())
1027         return nil;
1028
1029     // move up until we get to the top
1030     // NOTE: BUG this is not correct in non-editable elements when the position is on the
1031     // first line, but not at the first offset, because previousLinePosition takes you to the
1032     // first offset, the same as if you had started on the second line.  In editable elements,
1033     // previousLinePosition returns nil, so the count is accurate.
1034     // NOTE: BUG This only takes us to the top of the rootEditableElement, not the top of the
1035     // top document.
1036     // NOTE: Can we use Selection::modify(EAlter alter, int verticalDistance)?
1037     while (visiblePos.isNotNull() && visiblePos != savedVisiblePos) {
1038         lineCount += 1;
1039         savedVisiblePos = visiblePos;
1040         visiblePos = previousLinePosition(visiblePos, khtml::DOWNSTREAM, 0);
1041     }
1042     
1043     return [NSNumber numberWithUnsignedInt:lineCount];
1044 }
1045
1046 - (id)doAXTextMarkerRangeForLine: (NSNumber *) lineNumber
1047 {
1048     unsigned lineCount = [lineNumber unsignedIntValue];
1049     if (lineCount == 0 || !m_renderer) return nil;
1050     
1051     // iterate over the lines
1052     // NOTE: BUG this is wrong when lineNumber is lineCount+1,  because nextLinePosition takes you to the
1053     // last offset of the last line
1054     VisiblePosition visiblePos = VisiblePosition([self topRenderer]->positionForCoordinates (0, 0, nil));
1055     VisiblePosition savedVisiblePos;
1056     while (--lineCount != 0) {
1057         savedVisiblePos = visiblePos;
1058         visiblePos = nextLinePosition(visiblePos, khtml::DOWNSTREAM, 0);
1059         if (visiblePos.isNull() || visiblePos == savedVisiblePos)
1060             return nil;
1061     }
1062     
1063     // make a caret selection for the marker position, then extend it to the line
1064     // NOTE: ignores results of sel.modify because it returns false when
1065     // starting at an empty line.  The resulting selection in that case
1066     // will be a caret at visiblePos. 
1067     Selection sel = Selection(visiblePos, visiblePos);
1068     (void)sel.modify(Selection::EXTEND, Selection::RIGHT, khtml::LINE_BOUNDARY);
1069
1070     // return a marker range for the selection start to end
1071     VisiblePosition startPosition = VisiblePosition(sel.start());
1072     VisiblePosition endPosition = VisiblePosition(sel.end());
1073     return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
1074 }
1075
1076 - (id)doAXStringForTextMarkerRange: (AXTextMarkerRangeRef) textMarkerRange
1077 {
1078     // extract the start and end VisiblePosition
1079     VisiblePosition startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange];
1080     if (startVisiblePosition.isNull())
1081         return nil;
1082     
1083     VisiblePosition endVisiblePosition = [self visiblePositionForEndOfTextMarkerRange: textMarkerRange];
1084     if (endVisiblePosition.isNull())
1085         return nil;
1086     
1087     // get the visible text in the range
1088     QString qString = plainText(makeRange(startVisiblePosition, endVisiblePosition));
1089     
1090     // transform it to a CFString and return that
1091     return (id)qString.getCFString();
1092 }
1093
1094 - (id)doAXTextMarkerForPosition: (CGPoint) point
1095 {
1096     NSPoint screenpoint = NSMakePoint(point.x, point.y);
1097     NSView * view = [self topView]->getView();
1098     NSPoint windowpoint = [[view window] convertScreenToBase: screenpoint];
1099     NSPoint ourpoint = [view convertPoint:windowpoint fromView:nil];
1100
1101     VisiblePosition visiblePos = VisiblePosition([self topRenderer]->positionForCoordinates ((int)ourpoint.x, (int)ourpoint.y, nil));
1102     return (id) [self textMarkerForVisiblePosition:visiblePos];
1103 }
1104
1105 - (id)doAXBoundsForTextMarkerRange: (AXTextMarkerRangeRef) textMarkerRange
1106 {
1107
1108     // extract the start and end VisiblePosition
1109     VisiblePosition startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange];
1110     if (startVisiblePosition.isNull())
1111         return nil;
1112     
1113     VisiblePosition endVisiblePosition = [self visiblePositionForEndOfTextMarkerRange: textMarkerRange];
1114     if (endVisiblePosition.isNull())
1115         return nil;
1116     
1117     // use the Selection class to help calculate the corresponding rectangle
1118     // NOTE: If the selection spans lines, the rectangle is to extend across
1119     // the width of the view
1120     NSView * view = [self topView]->getView();
1121     QRect rect1 = Selection(startVisiblePosition, startVisiblePosition).caretRect();
1122     QRect rect2 = Selection(endVisiblePosition, endVisiblePosition).caretRect();
1123     QRect ourrect = rect1.unite(rect2);
1124     if (rect1.bottom() != rect2.bottom()) {
1125         ourrect.setX((int)[view frame].origin.x);
1126         ourrect.setWidth((int)[view frame].size.width);
1127     }
1128  
1129     // convert our rectangle to screen coordinates
1130     NSRect rect = NSMakeRect(ourrect.left(), ourrect.top(), ourrect.width(), ourrect.height());
1131     rect = [view convertRect:rect toView:nil];
1132     rect.origin = [[view window] convertBaseToScreen:rect.origin];
1133     
1134     // return the converted rect
1135     return [NSValue valueWithRect:rect];
1136 }
1137
1138 static CGColorRef CreateCGColorIfDifferent(NSColor *nsColor, CGColorRef existingColor)
1139 {
1140     // get color information assuming NSDeviceRGBColorSpace 
1141     NSColor *rgbColor = [nsColor colorUsingColorSpaceName:NSDeviceRGBColorSpace];
1142     if (rgbColor == nil) {
1143         rgbColor = [NSColor blackColor];
1144     }
1145     float components[4];
1146     [rgbColor getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]];
1147     
1148     // create a new CGColorRef to return
1149     CGColorSpaceRef cgColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
1150     CGColorRef cgColor = CGColorCreate(cgColorSpace, components);
1151     CGColorSpaceRelease(cgColorSpace);
1152     CFMakeCollectable(cgColor);
1153     
1154     // check for match with existing color
1155     if (existingColor && CGColorEqualToColor(cgColor, existingColor))
1156         cgColor = nil;
1157         
1158     return cgColor;
1159 }
1160
1161 static void AXAttributeStringSetColor(NSMutableAttributedString *attrString, NSString *attribute, NSColor* color, NSRange range)
1162 {
1163     if (color != nil) {
1164         CGColorRef existingColor = (CGColorRef) [attrString attribute:attribute atIndex:range.location effectiveRange:nil];
1165         CGColorRef cgColor = CreateCGColorIfDifferent(color, existingColor);
1166         if (cgColor != NULL) {
1167             [attrString addAttribute:attribute value:(id)cgColor range:range];
1168             CGColorRelease(cgColor);
1169         }
1170     } else
1171         [attrString removeAttribute:attribute range:range];
1172 }
1173
1174 static void AXAttributeStringSetNumber(NSMutableAttributedString *attrString, NSString *attribute, NSNumber* number, NSRange range)
1175 {
1176     if (number != nil) {
1177         [attrString addAttribute:attribute value:number range:range];
1178     } else
1179         [attrString removeAttribute:attribute range:range];
1180 }
1181
1182 static void AXAttributeStringSetFont(NSMutableAttributedString *attrString, NSString *attribute, NSFont* font, NSRange range)
1183 {
1184     NSDictionary *dict;
1185     
1186     if (font != nil) {
1187         dict = [NSDictionary dictionaryWithObjectsAndKeys:
1188             [font fontName]                             , NSAccessibilityFontNameKey,
1189             [font familyName]                           , NSAccessibilityFontFamilyKey,
1190             [font displayName]                          , NSAccessibilityVisibleNameKey,
1191             [NSNumber numberWithFloat:[font pointSize]] , NSAccessibilityFontSizeKey,
1192         nil];
1193
1194         [attrString addAttribute:attribute value:dict range:range];
1195     } else
1196         [attrString removeAttribute:attribute range:range];
1197     
1198 }
1199
1200 static void AXAttributeStringSetStyle(NSMutableAttributedString *attrString, RenderObject *renderer, NSRange range)
1201 {
1202     RenderStyle *style = renderer->style();
1203
1204     // set basic font info
1205     AXAttributeStringSetFont(attrString, NSAccessibilityFontTextAttribute, style->font().getNSFont(), range);
1206
1207     // set basic colors
1208     AXAttributeStringSetColor(attrString, NSAccessibilityForegoundColorTextAttribute, style->color().getNSColor(), range);
1209     AXAttributeStringSetColor(attrString, NSAccessibilityBackgroundColorTextAttribute, style->backgroundColor().getNSColor(), range);
1210
1211     // set super/sub scripting
1212     EVerticalAlign alignment = style->verticalAlign();
1213     if (alignment == khtml::SUB)
1214         AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:(-1)], range);
1215     else if (alignment == khtml::SUPER)
1216         AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:1], range);
1217     else
1218         [attrString removeAttribute:NSAccessibilitySuperscriptTextAttribute range:range];
1219     
1220     // set shadow
1221     if (style->textShadow() != nil)
1222         AXAttributeStringSetNumber(attrString, NSAccessibilityShadowTextAttribute, [NSNumber numberWithBool:YES], range);
1223     else
1224         [attrString removeAttribute:NSAccessibilityShadowTextAttribute range:range];
1225     
1226     // set underline and strikethrough
1227     int decor = style->textDecorationsInEffect();
1228     if ((decor & khtml::UNDERLINE) == 0) {
1229         [attrString removeAttribute:NSAccessibilityUnderlineTextAttribute range:range];
1230         [attrString removeAttribute:NSAccessibilityUnderlineColorTextAttribute range:range];
1231     }
1232     
1233     if ((decor & khtml::LINE_THROUGH) == 0) {
1234         [attrString removeAttribute:NSAccessibilityStrikethroughTextAttribute range:range];
1235         [attrString removeAttribute:NSAccessibilityStrikethroughColorTextAttribute range:range];
1236     }
1237
1238     if ((decor & (khtml::UNDERLINE | khtml::LINE_THROUGH)) != 0) {
1239         // find colors using quirk mode approach (strict mode would use current
1240         // color for all but the root line box, which would use getTextDecorationColors)
1241         QColor underline, overline, linethrough;
1242         renderer->getTextDecorationColors(decor, underline, overline, linethrough);
1243         
1244         if ((decor & khtml::UNDERLINE) != 0) {
1245             AXAttributeStringSetNumber(attrString, NSAccessibilityUnderlineTextAttribute, [NSNumber numberWithBool:YES], range);
1246             AXAttributeStringSetColor(attrString, NSAccessibilityUnderlineColorTextAttribute, underline.getNSColor(), range);
1247         }
1248
1249         if ((decor & khtml::LINE_THROUGH) != 0) {
1250             AXAttributeStringSetNumber(attrString, NSAccessibilityStrikethroughTextAttribute, [NSNumber numberWithBool:YES], range);
1251             AXAttributeStringSetColor(attrString, NSAccessibilityStrikethroughColorTextAttribute, linethrough.getNSColor(), range);
1252         }
1253     }
1254 }
1255
1256 static void AXAttributeStringSetElement(NSMutableAttributedString *attrString, NSString *attribute, id element, NSRange range)
1257 {
1258     if (element != nil) {
1259         // make a serialiazable AX object
1260         AXUIElementRef axElement = NSAccessibilityCreateAXUIElementRef(element);
1261         if (axElement != NULL) {
1262             [attrString addAttribute:attribute value:(id)axElement range:range];
1263             CFRelease(axElement);
1264         }
1265     } else {
1266         [attrString removeAttribute:attribute range:range];
1267     }
1268 }
1269
1270 static KWQAccObject *AXLinkElementForNode (NodeImpl *node)
1271 {
1272     RenderObject *obj = node->renderer();
1273     if (!obj)
1274         return nil;
1275
1276     KWQAccObject *axObj = obj->document()->getAccObjectCache()->accObject(obj);
1277     HTMLAnchorElementImpl* anchor = [axObj anchorElement];
1278     if (!anchor || !anchor->renderer())
1279         return nil;
1280
1281     return anchor->renderer()->document()->getAccObjectCache()->accObject(anchor->renderer());
1282 }
1283
1284 static void AXAttributedStringAppendText (NSMutableAttributedString *attrString, NodeImpl *nodeImpl, const QChar *chars, int length)
1285 {
1286     // easier to calculate the range before appending the string
1287     NSRange attrStringRange = NSMakeRange([attrString length], length);
1288     
1289     // append the string from this node
1290     [[attrString mutableString] appendString:[NSString stringWithCharacters:(const UniChar*)chars length:length]];
1291
1292     // add new attributes and remove irrelevant inherited ones
1293     // NOTE: color attributes are handled specially because -[NSMutableAttributedString addAttribute: value: range:] does not merge
1294     // identical colors.  Workaround is to not replace an existing color attribute if it matches what we are adding.  This also means
1295     // we can not just pre-remove all inherited attributes on the appended string, so we have to remove the irrelevant ones individually.
1296
1297     // remove inherited attachment from prior AXAttributedStringAppendReplaced
1298     [attrString removeAttribute:NSAccessibilityAttachmentTextAttribute range:attrStringRange];
1299     
1300     // set new attributes
1301     AXAttributeStringSetStyle(attrString, nodeImpl->renderer(), attrStringRange);
1302     AXAttributeStringSetElement(attrString, NSAccessibilityLinkTextAttribute, AXLinkElementForNode(nodeImpl), attrStringRange);
1303 }
1304
1305 static void AXAttributedStringAppendReplaced (NSMutableAttributedString *attrString, NodeImpl *replacedNode)
1306 {
1307     static const UniChar attachmentChar = NSAttachmentCharacter;
1308
1309     // we should always be given a rendered node, but be safe
1310     if (!replacedNode || !replacedNode->renderer()) {
1311         ASSERT_NOT_REACHED();
1312         return;
1313     }
1314     
1315     // we should always be given a replaced node, but be safe
1316     // replaced nodes are either attachments (widgets) or images
1317     if (!replacedNode->renderer()->isReplaced()) {
1318         ASSERT_NOT_REACHED();
1319         return;
1320     }
1321         
1322     // create an AX object, but skip it if it is not supposed to be seen
1323     KWQAccObject *obj = replacedNode->renderer()->document()->getAccObjectCache()->accObject(replacedNode->renderer());
1324     if ([obj accessibilityIsIgnored])
1325         return;
1326     
1327     // easier to calculate the range before appending the string
1328     NSRange attrStringRange = NSMakeRange([attrString length], 1);
1329     
1330     // append the placeholder string
1331     [[attrString mutableString] appendString:[NSString stringWithCharacters:&attachmentChar length:1]];
1332     
1333     // remove all inherited attributes
1334     [attrString setAttributes:nil range:attrStringRange];
1335
1336     // add the attachment attribute
1337     AXAttributeStringSetElement(attrString, NSAccessibilityAttachmentTextAttribute, obj, attrStringRange);
1338 }
1339
1340 - (id)doAXAttributedStringForTextMarkerRange: (AXTextMarkerRangeRef) textMarkerRange
1341 {
1342     // extract the start and end VisiblePosition
1343     VisiblePosition startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange];
1344     if (startVisiblePosition.isNull())
1345         return nil;
1346     
1347     VisiblePosition endVisiblePosition = [self visiblePositionForEndOfTextMarkerRange: textMarkerRange];
1348     if (endVisiblePosition.isNull())
1349         return nil;
1350     
1351     // iterate over the range to build the AX attributed string
1352     NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
1353     TextIterator it(makeRange(startVisiblePosition, endVisiblePosition));
1354     while (!it.atEnd()) {
1355         // locate the node for this range
1356         Node node = it.range().startContainer();
1357         ASSERT(node == it.range().endContainer());
1358         NodeImpl *nodeImpl = node.handle();
1359         
1360         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
1361         if (it.length() != 0) {
1362             AXAttributedStringAppendText (attrString, nodeImpl, it.characters(), it.length());
1363         } else {
1364             AXAttributedStringAppendReplaced (attrString, nodeImpl->childNode(it.range().startOffset()));
1365         }
1366         
1367         it.advance();
1368     }
1369
1370     return [attrString autorelease];
1371 }
1372
1373 - (id)doAXTextMarkerRangeForUnorderedTextMarkers: (NSArray *) markers
1374 {
1375 #if MARKERARRAY_SELF_TEST
1376     AXTextMarkerRangeRef tmr = [self getSelectedTextMarkerRange];
1377     AXTextMarkerRef tm1 = [self AXTextMarkerRangeCopyEndMarkerWrapper:tmr];
1378     AXTextMarkerRef tm2 = [self AXTextMarkerRangeCopyStartMarkerWrapper:tmr];
1379     markers = [NSArray arrayWithObjects: (id) tm1, (id) tm2, nil];
1380 #endif
1381     // get and validate the markers
1382     if ([markers count] < 2)
1383         return nil;
1384     
1385     AXTextMarkerRef textMarker1 = (AXTextMarkerRef) [markers objectAtIndex:0];
1386     AXTextMarkerRef textMarker2 = (AXTextMarkerRef) [markers objectAtIndex:1];
1387     if (CFGetTypeID(textMarker1) != AXTextMarkerGetTypeID() || CFGetTypeID(textMarker2) != AXTextMarkerGetTypeID())
1388         return nil;
1389     
1390     // convert to VisiblePosition
1391     VisiblePosition visiblePos1 = [self visiblePositionForTextMarker:textMarker1];
1392     VisiblePosition visiblePos2 = [self visiblePositionForTextMarker:textMarker2];
1393     if (visiblePos1.isNull() || visiblePos2.isNull())
1394         return nil;
1395     
1396     // use the Selection class to do the ordering
1397     // NOTE: Perhaps we could add a Selection method to indicate direction, based on m_baseIsStart
1398     AXTextMarkerRef startTextMarker;
1399     AXTextMarkerRef endTextMarker;
1400     Selection   sel(visiblePos1, visiblePos2);
1401     if (sel.base() == sel.start()) {
1402         startTextMarker = textMarker1;
1403         endTextMarker = textMarker2;
1404     } else {
1405         startTextMarker = textMarker2;
1406         endTextMarker = textMarker1;
1407     }
1408     
1409     // return a range based on the Selection verdict
1410     return (id) [self textMarkerRangeFromMarkers: startTextMarker andEndMarker:endTextMarker];
1411 }
1412
1413 - (id)doAXNextTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1414 {
1415     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1416     VisiblePosition nextVisiblePos = visiblePos.next();
1417     if (nextVisiblePos.isNull())
1418         return nil;
1419     
1420     return (id) [self textMarkerForVisiblePosition:nextVisiblePos];
1421 }
1422
1423 - (id)doAXPreviousTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1424 {
1425     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1426     VisiblePosition previousVisiblePos = visiblePos.previous();
1427     if (previousVisiblePos.isNull())
1428         return nil;
1429     
1430     return (id) [self textMarkerForVisiblePosition:previousVisiblePos];
1431 }
1432
1433 - (id)doAXLeftWordTextMarkerRangeForTextMarker: (AXTextMarkerRef) textMarker
1434 {
1435     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1436     VisiblePosition startPosition = startOfWord(visiblePos, khtml::LeftWordIfOnBoundary);
1437     VisiblePosition endPosition = endOfWord(startPosition);
1438
1439     return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
1440 }
1441
1442 - (id)doAXRightWordTextMarkerRangeForTextMarker: (AXTextMarkerRef) textMarker
1443 {
1444     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1445     VisiblePosition startPosition = startOfWord(visiblePos, khtml::RightWordIfOnBoundary);
1446     VisiblePosition endPosition = endOfWord(startPosition);
1447
1448     return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
1449 }
1450
1451 - (id)doAXLeftLineTextMarkerRangeForTextMarker: (AXTextMarkerRef) textMarker
1452 {
1453     // use Selection class instead of visible_units because startOfLine and endOfLine
1454     // are declared but not defined
1455     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1456     if (visiblePos.isNull())
1457         return nil;
1458     
1459     // make a caret selection for the position before marker position (to make sure
1460     // we move off of a line start)
1461     VisiblePosition prevVisiblePos = visiblePos.previous();
1462     if (prevVisiblePos.isNull())
1463         return nil;
1464     
1465     // extend selection to the line
1466     // NOTE: ignores results of sel.modify because it returns false when
1467     // starting at an empty line.  The resulting selection in that case
1468     // will be a caret at prevVisiblePos. 
1469     Selection sel = Selection(prevVisiblePos, prevVisiblePos);
1470     (void)sel.modify(Selection::MOVE, Selection::LEFT, khtml::LINE_BOUNDARY);
1471     (void)sel.modify(Selection::EXTEND, Selection::RIGHT, khtml::LINE_BOUNDARY);
1472
1473     // return a marker range for the selection start to end
1474     VisiblePosition startPosition = VisiblePosition(sel.start());
1475     VisiblePosition endPosition = VisiblePosition(sel.end());
1476     return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
1477 }
1478
1479 - (id)doAXRightLineTextMarkerRangeForTextMarker: (AXTextMarkerRef) textMarker
1480 {
1481     // use Selection class instead of visible_units because startOfLine and endOfLine
1482     // are declared but not defined
1483     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1484     if (visiblePos.isNull())
1485         return nil;
1486     
1487     // make sure we move off of a line end
1488     VisiblePosition nextVisiblePos = visiblePos.next();
1489     if (nextVisiblePos.isNull())
1490         return nil;
1491         
1492     // make a caret selection and extend it to the line
1493     // NOTE: ignores results of sel.modify because it returns false when
1494     // starting at an empty line.  The resulting selection in that case
1495     // will be a caret at nextVisiblePos. 
1496     Selection sel = Selection(nextVisiblePos, nextVisiblePos);
1497     (void)sel.modify(Selection::MOVE, Selection::RIGHT, khtml::LINE_BOUNDARY);
1498     (void)sel.modify(Selection::EXTEND, Selection::LEFT, khtml::LINE_BOUNDARY);
1499
1500     // return a marker range for the selection start to end
1501     VisiblePosition startPosition = VisiblePosition(sel.start());
1502     VisiblePosition endPosition = VisiblePosition(sel.end());
1503     return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
1504 }
1505
1506 - (id)doAXSentenceTextMarkerRangeForTextMarker: (AXTextMarkerRef) textMarker
1507 {
1508     // NOTE: BUG FO 2 IMPLEMENT (currently returns incorrect answer)
1509     // Related? <rdar://problem/3927736> Text selection broken in 8A336
1510     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1511     VisiblePosition startPosition = startOfSentence(visiblePos);
1512     VisiblePosition endPosition = endOfSentence(startPosition);
1513
1514     return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
1515 }
1516
1517 - (id)doAXParagraphTextMarkerRangeForTextMarker: (AXTextMarkerRef) textMarker
1518 {
1519     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1520     VisiblePosition startPosition = startOfParagraph(visiblePos);
1521     VisiblePosition endPosition = endOfParagraph(startPosition);
1522
1523     return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
1524 }
1525
1526 - (id)doAXNextWordEndTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1527 {
1528     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1529     if (visiblePos.isNull())
1530         return nil;
1531
1532     // make sure we move off of a word end
1533     visiblePos = visiblePos.next();
1534     if (visiblePos.isNull())
1535         return nil;
1536
1537     VisiblePosition endPosition = endOfWord(visiblePos, khtml::RightWordIfOnBoundary);
1538     return (id) [self textMarkerForVisiblePosition: endPosition];
1539 }
1540
1541 - (id)doAXPreviousWordStartTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1542 {
1543     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1544     if (visiblePos.isNull())
1545         return nil;
1546
1547     // make sure we move off of a word start
1548     visiblePos = visiblePos.previous();
1549     if (visiblePos.isNull())
1550         return nil;
1551     
1552     VisiblePosition startPosition = startOfWord(visiblePos, khtml::LeftWordIfOnBoundary);
1553     return (id) [self textMarkerForVisiblePosition: startPosition];
1554 }
1555
1556 - (id)doAXNextLineEndTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1557 {
1558     // use Selection class instead of visible_units because startOfLine and endOfLine
1559     // are declared but not defined
1560     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1561     if (visiblePos.isNull())
1562         return nil;
1563     
1564     // to make sure we move off of a line end
1565     VisiblePosition nextVisiblePos = visiblePos.next();
1566     if (nextVisiblePos.isNull())
1567         return nil;
1568         
1569     // make caret selection and extend it to the line
1570     // NOTE: ignores results of sel.modify because it returns false when
1571     // starting at an empty line.  The resulting selection in that case
1572     // will be a caret at nextVisiblePos. 
1573     Selection sel = Selection(nextVisiblePos, nextVisiblePos);
1574     (void)sel.modify(Selection::MOVE, Selection::RIGHT, khtml::LINE_BOUNDARY);
1575
1576     // return a marker for the selection end
1577     VisiblePosition endPosition = VisiblePosition(sel.end());
1578     return (id) [self textMarkerForVisiblePosition: endPosition];
1579 }
1580
1581 - (id)doAXPreviousLineStartTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1582 {
1583     // use Selection class instead of visible_units because startOfLine and endOfLine
1584     // are declared but not defined
1585     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1586     if (visiblePos.isNull())
1587         return nil;
1588     
1589     // make sure we move off of a line start
1590     VisiblePosition prevVisiblePos = visiblePos.previous();
1591     if (prevVisiblePos.isNull())
1592         return nil;
1593         
1594     // make a caret selection and extend it to the line
1595     // NOTE: ignores results of sel.modify because it returns false when
1596     // starting at an empty line.  The resulting selection in that case
1597     // will be a caret at prevVisiblePos. 
1598     Selection sel = Selection(prevVisiblePos, prevVisiblePos);
1599     (void)sel.modify(Selection::MOVE, Selection::LEFT, khtml::LINE_BOUNDARY);
1600
1601     // return a marker for the selection start
1602     VisiblePosition startPosition = VisiblePosition(sel.start());
1603     return (id) [self textMarkerForVisiblePosition: startPosition];
1604 }
1605
1606 - (id)doAXNextSentenceEndTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1607 {
1608     // NOTE: BUG FO 2 IMPLEMENT (currently returns incorrect answer)
1609     // Related? <rdar://problem/3927736> Text selection broken in 8A336
1610     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1611     if (visiblePos.isNull())
1612         return nil;
1613     
1614     // make sure we move off of a sentence end
1615     visiblePos = visiblePos.next();
1616     if (visiblePos.isNull())
1617         return nil;
1618
1619     VisiblePosition endPosition = endOfSentence(visiblePos);
1620     return (id) [self textMarkerForVisiblePosition: endPosition];
1621 }
1622
1623 - (id)doAXPreviousSentenceStartTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1624 {
1625     // NOTE: BUG FO 2 IMPLEMENT (currently returns incorrect answer)
1626     // Related? <rdar://problem/3927736> Text selection broken in 8A336
1627     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1628     if (visiblePos.isNull())
1629         return nil;
1630
1631     // make sure we move off of a sentence start
1632     visiblePos = visiblePos.previous();
1633     if (visiblePos.isNull())
1634         return nil;
1635
1636     VisiblePosition startPosition = startOfSentence(visiblePos);
1637     return (id) [self textMarkerForVisiblePosition: startPosition];
1638 }
1639
1640 - (id)doAXNextParagraphEndTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1641 {
1642     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1643     if (visiblePos.isNull())
1644         return nil;
1645     
1646     // make sure we move off of a paragraph end
1647     visiblePos = visiblePos.next();
1648     if (visiblePos.isNull())
1649         return nil;
1650
1651     VisiblePosition endPosition = endOfParagraph(visiblePos);
1652     return (id) [self textMarkerForVisiblePosition: endPosition];
1653 }
1654
1655 - (id)doAXPreviousParagraphStartTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1656 {
1657     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1658     if (visiblePos.isNull())
1659         return nil;
1660
1661     // make sure we move off of a paragraph start
1662     visiblePos = visiblePos.previous();
1663     if (visiblePos.isNull())
1664         return nil;
1665
1666     VisiblePosition startPosition = startOfParagraph(visiblePos);
1667     return (id) [self textMarkerForVisiblePosition: startPosition];
1668 }
1669
1670 - (id)doAXLengthForTextMarkerRange: (AXTextMarkerRangeRef) textMarkerRange
1671 {
1672     // NOTE: BUG Multi-byte support
1673     CFStringRef string = (CFStringRef) [self doAXStringForTextMarkerRange: textMarkerRange];
1674     if (!string)
1675         return nil;
1676
1677     return [NSNumber numberWithInt:CFStringGetLength(string)];
1678 }
1679
1680 - (id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter
1681 {
1682     AXTextMarkerRef         textMarker = nil;
1683     AXTextMarkerRangeRef    textMarkerRange = nil;
1684     AXValueRef              value = nil;
1685     NSNumber *              number = nil;
1686     NSArray *               array = nil;
1687     CGPoint                 point;
1688     CGSize                  size;
1689     CGRect                  rect;
1690     CFRange                 range;
1691     AXError                 error;
1692     
1693     // basic parameter validation
1694     if (!m_renderer || !attribute || !parameter)
1695         return nil;
1696
1697     // common parameter type check/casting.  Nil checks in handlers catch wrong type case.
1698     // NOTE: This assumes nil is not a valid parameter, because it is indistinguishable from
1699     // a parameter of the wrong type.
1700     if (CFGetTypeID(parameter) == AXTextMarkerGetTypeID())
1701         textMarker = (AXTextMarkerRef) parameter;
1702
1703     else if (CFGetTypeID(parameter) == AXTextMarkerRangeGetTypeID())
1704         textMarkerRange = (AXTextMarkerRangeRef) parameter;
1705
1706     else if ([parameter isKindOfClass:[NSNumber self]])
1707         number = parameter;
1708
1709     else if ([parameter isKindOfClass:[NSArray self]])
1710         array = parameter;
1711
1712     else if (CFGetTypeID(parameter) == AXValueGetTypeID()) {
1713         value = (AXValueRef) parameter;
1714         switch (AXValueGetType(value)) {
1715             case kAXValueCGPointType:
1716                 AXValueGetValue(value, kAXValueCGPointType, &point);
1717                 break;
1718             case kAXValueCGSizeType:
1719                 AXValueGetValue(value, kAXValueCGSizeType, &size);
1720                 break;
1721             case kAXValueCGRectType:
1722                 AXValueGetValue(value, kAXValueCGRectType, &rect);
1723                 break;
1724             case kAXValueCFRangeType:
1725                 AXValueGetValue(value, kAXValueCFRangeType, &range);
1726                 break;
1727             case kAXValueAXErrorType:
1728                 AXValueGetValue(value, kAXValueAXErrorType, &error);
1729                 break;
1730             default:
1731                 break;
1732         }
1733     }
1734   
1735     // dispatch
1736     if ([attribute isEqualToString: @"AXUIElementForTextMarker"])
1737         return [self doAXUIElementForTextMarker: textMarker];
1738
1739     if ([attribute isEqualToString: (NSString *) kAXLineForTextMarkerParameterizedAttribute])
1740         return [self doAXLineForTextMarker: textMarker];
1741
1742     if ([attribute isEqualToString: (NSString *) kAXTextMarkerRangeForLineParameterizedAttribute])
1743         return [self doAXTextMarkerRangeForLine: number];
1744
1745     if ([attribute isEqualToString: (NSString *) kAXStringForTextMarkerRangeParameterizedAttribute])
1746         return [self doAXStringForTextMarkerRange: textMarkerRange];
1747
1748     if ([attribute isEqualToString: (NSString *) kAXTextMarkerForPositionParameterizedAttribute])
1749         return [self doAXTextMarkerForPosition: point];
1750
1751     if ([attribute isEqualToString: (NSString *) kAXBoundsForTextMarkerRangeParameterizedAttribute])
1752         return [self doAXBoundsForTextMarkerRange: textMarkerRange];
1753
1754     if ([attribute isEqualToString: (NSString *) kAXAttributedStringForTextMarkerRangeParameterizedAttribute])
1755         return [self doAXAttributedStringForTextMarkerRange: textMarkerRange];
1756
1757     if ([attribute isEqualToString: (NSString *) kAXTextMarkerRangeForUnorderedTextMarkersParameterizedAttribute])
1758         return [self doAXTextMarkerRangeForUnorderedTextMarkers: array];
1759
1760     if ([attribute isEqualToString: (NSString *) kAXNextTextMarkerForTextMarkerParameterizedAttribute])
1761         return [self doAXNextTextMarkerForTextMarker: textMarker];
1762
1763     if ([attribute isEqualToString: (NSString *) kAXPreviousTextMarkerForTextMarkerParameterizedAttribute])
1764         return [self doAXPreviousTextMarkerForTextMarker: textMarker];
1765
1766     if ([attribute isEqualToString: (NSString *) kAXLeftWordTextMarkerRangeForTextMarkerParameterizedAttribute])
1767         return [self doAXLeftWordTextMarkerRangeForTextMarker: textMarker];
1768
1769     if ([attribute isEqualToString: (NSString *) kAXRightWordTextMarkerRangeForTextMarkerParameterizedAttribute])
1770         return [self doAXRightWordTextMarkerRangeForTextMarker: textMarker];
1771
1772     if ([attribute isEqualToString: (NSString *) kAXLeftLineTextMarkerRangeForTextMarkerParameterizedAttribute])
1773         return [self doAXLeftLineTextMarkerRangeForTextMarker: textMarker];
1774
1775     if ([attribute isEqualToString: (NSString *) kAXRightLineTextMarkerRangeForTextMarkerParameterizedAttribute])
1776         return [self doAXRightLineTextMarkerRangeForTextMarker: textMarker];
1777
1778     if ([attribute isEqualToString: (NSString *) kAXSentenceTextMarkerRangeForTextMarkerParameterizedAttribute])
1779         return [self doAXSentenceTextMarkerRangeForTextMarker: textMarker];
1780
1781     if ([attribute isEqualToString: (NSString *) kAXParagraphTextMarkerRangeForTextMarkerParameterizedAttribute])
1782         return [self doAXParagraphTextMarkerRangeForTextMarker: textMarker];
1783
1784     if ([attribute isEqualToString: (NSString *) kAXNextWordEndTextMarkerForTextMarkerParameterizedAttribute])
1785         return [self doAXNextWordEndTextMarkerForTextMarker: textMarker];
1786
1787     if ([attribute isEqualToString: (NSString *) kAXPreviousWordStartTextMarkerForTextMarkerParameterizedAttribute])
1788         return [self doAXPreviousWordStartTextMarkerForTextMarker: textMarker];
1789         
1790     if ([attribute isEqualToString: (NSString *) kAXNextLineEndTextMarkerForTextMarkerParameterizedAttribute])
1791         return [self doAXNextLineEndTextMarkerForTextMarker: textMarker];
1792         
1793     if ([attribute isEqualToString: (NSString *) kAXPreviousLineStartTextMarkerForTextMarkerParameterizedAttribute])
1794         return [self doAXPreviousLineStartTextMarkerForTextMarker: textMarker];
1795         
1796     if ([attribute isEqualToString: (NSString *) kAXNextSentenceEndTextMarkerForTextMarkerParameterizedAttribute])
1797         return [self doAXNextSentenceEndTextMarkerForTextMarker: textMarker];
1798         
1799     if ([attribute isEqualToString: (NSString *) kAXPreviousSentenceStartTextMarkerForTextMarkerParameterizedAttribute])
1800         return [self doAXPreviousSentenceStartTextMarkerForTextMarker: textMarker];
1801         
1802     if ([attribute isEqualToString: (NSString *) kAXNextParagraphEndTextMarkerForTextMarkerParameterizedAttribute])
1803         return [self doAXNextParagraphEndTextMarkerForTextMarker: textMarker];
1804
1805     if ([attribute isEqualToString: (NSString *) kAXPreviousParagraphStartTextMarkerForTextMarkerParameterizedAttribute])
1806         return [self doAXPreviousParagraphStartTextMarkerForTextMarker: textMarker];
1807         
1808     if ([attribute isEqualToString: (NSString *) kAXLengthForTextMarkerRangeParameterizedAttribute])
1809         return [self doAXLengthForTextMarkerRange: textMarkerRange];
1810
1811     return nil;
1812 }
1813
1814 #endif
1815
1816 - (id)accessibilityHitTest:(NSPoint)point
1817 {
1818     if (!m_renderer)
1819         return self;
1820     
1821     RenderObject::NodeInfo nodeInfo(true, true);
1822     m_renderer->layer()->hitTest(nodeInfo, (int)point.x, (int)point.y);
1823     if (!nodeInfo.innerNode())
1824         return self;
1825     RenderObject* obj = nodeInfo.innerNode()->renderer();
1826     if (!obj)
1827         return self;
1828     return obj->document()->getAccObjectCache()->accObject(obj);
1829 }
1830
1831 - (id)accessibilityFocusedUIElement
1832 {
1833     // NOTE: BUG support nested WebAreas
1834     NodeImpl *focusNode = m_renderer->document()->focusNode();
1835     if (!focusNode || !focusNode->renderer())
1836         return nil;
1837
1838     KWQAccObject* obj = focusNode->renderer()->document()->getAccObjectCache()->accObject(focusNode->renderer());
1839     
1840     // the HTML element, for example, is focusable but has an AX object that is ignored
1841     if ([obj accessibilityIsIgnored])
1842         obj = [obj parentObjectUnignored];
1843     
1844     return obj;
1845 }
1846
1847 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName
1848 {
1849 #if OMIT_TIGER_FEATURES
1850 // no parameterized attributes in Panther... they were introduced in Tiger
1851 #else
1852     if ([attributeName isEqualToString: (NSString *) kAXSelectedTextMarkerRangeAttribute])
1853         return YES;
1854 #endif
1855
1856     return NO;
1857 }
1858
1859 #if OMIT_TIGER_FEATURES
1860 // no parameterized attributes in Panther... they were introduced in Tiger
1861 #else
1862 - (void)doSetAXSelectedTextMarkerRange: (AXTextMarkerRangeRef)textMarkerRange
1863 {
1864     VisiblePosition startVisiblePosition, endVisiblePosition;
1865     
1866     // extract the start and end VisiblePosition
1867     startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange];
1868     if (startVisiblePosition.isNull())
1869         return;
1870     
1871     endVisiblePosition = [self visiblePositionForEndOfTextMarkerRange: textMarkerRange];
1872     if (endVisiblePosition.isNull())
1873         return;
1874     
1875     // make selection and tell the document to use it
1876     // NOTE: BUG support nested WebAreas
1877     Selection sel = Selection(startVisiblePosition, endVisiblePosition);
1878     [self topDocument]->part()->setSelection(sel);
1879 }
1880
1881 - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attributeName;
1882 {
1883     AXTextMarkerRangeRef    textMarkerRange = nil;
1884
1885     if (CFGetTypeID(value) == AXTextMarkerRangeGetTypeID())
1886         textMarkerRange = (AXTextMarkerRangeRef) value;
1887         
1888     if ([attributeName isEqualToString: (NSString *) kAXSelectedTextMarkerRangeAttribute]) {
1889         [self doSetAXSelectedTextMarkerRange:textMarkerRange];
1890     }
1891 }
1892 #endif
1893
1894 - (void)childrenChanged
1895 {
1896     [self clearChildren];
1897     
1898     if ([self accessibilityIsIgnored])
1899         [[self parentObject] childrenChanged];
1900 }
1901
1902 - (void)clearChildren
1903 {
1904     [m_children release];
1905     m_children = nil;
1906 }
1907
1908 -(KWQAccObjectID)accObjectID
1909 {
1910     return m_accObjectID;
1911 }
1912
1913 -(void)setAccObjectID:(KWQAccObjectID) accObjectID
1914 {
1915     m_accObjectID = accObjectID;
1916 }
1917
1918 - (void)removeAccObjectID
1919 {
1920     m_renderer->document()->getAccObjectCache()->removeAccObjectID(self);
1921 }
1922
1923 @end