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