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