213ff89b1d4ba07c287ab4e40a745f0951aec314
[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     NSView *view = [self topView]->getView();
1098     RenderObject *renderer = [self topRenderer];
1099     NodeImpl *innerNode = NULL;
1100     NSPoint ourpoint;
1101     
1102     // locate the node containing the point
1103     while (1) {
1104         // ask the document layer to hitTest
1105         NSPoint windowCoord = [[view window] convertScreenToBase: point];
1106         ourpoint = [view convertPoint:windowCoord fromView:nil];
1107         RenderObject::NodeInfo nodeInfo(true, true);
1108         renderer->layer()->hitTest(nodeInfo, (int)ourpoint.x, (int)ourpoint.y);
1109         innerNode = nodeInfo.innerNode();
1110         if (!innerNode || !innerNode->renderer())
1111             return nil;
1112
1113         // done if hit something other than a widget
1114         renderer = innerNode->renderer();
1115         if (!renderer->isWidget())
1116             break;
1117
1118         // descend into widget (FRAME, IFRAME, OBJECT...)
1119         QWidget *widget = static_cast<RenderWidget *>(renderer)->widget();
1120         if (!widget || !widget->inherits("KHTMLView"))
1121             break;
1122         KHTMLPart *part = static_cast<KHTMLView *>(widget)->part();
1123         if (!part)
1124             break;
1125         DocumentImpl *document = part->xmlDocImpl();
1126         if (!document)
1127             break;
1128         renderer = document->renderer();
1129         view = static_cast<KHTMLView *>(widget)->getDocumentView();
1130     }
1131     
1132     // get position within the node
1133     VisiblePosition pos = innerNode->renderer()->positionForCoordinates ((int)ourpoint.x, (int)ourpoint.y);
1134     return (id) [self textMarkerForVisiblePosition:pos];
1135 }
1136
1137 - (id)doAXBoundsForTextMarkerRange: (AXTextMarkerRangeRef) textMarkerRange
1138 {
1139
1140     // extract the start and end VisiblePosition
1141     VisiblePosition startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange];
1142     if (startVisiblePosition.isNull())
1143         return nil;
1144     
1145     VisiblePosition endVisiblePosition = [self visiblePositionForEndOfTextMarkerRange: textMarkerRange];
1146     if (endVisiblePosition.isNull())
1147         return nil;
1148     
1149     // use the Selection class to help calculate the corresponding rectangle
1150     QRect rect1 = Selection(startVisiblePosition, startVisiblePosition).caretRect();
1151     QRect rect2 = Selection(endVisiblePosition, endVisiblePosition).caretRect();
1152     QRect ourrect = rect1.unite(rect2);
1153
1154     // try to use the document view from the selection, so that nested WebAreas work,
1155     // but fall back to the top level doc if we do not find it easily
1156     KHTMLView *docView = NULL;
1157     RenderObject * renderer = startVisiblePosition.deepEquivalent().node()->renderer();
1158     if (renderer) {
1159         DocumentImpl* doc = renderer->document();
1160         if (doc)
1161             docView = doc->view();
1162     }
1163     if (!docView)
1164         docView = [self topView];
1165     NSView * view = docView->getView();
1166
1167     // if the selection spans lines, the rectangle is to extend
1168     // across the width of the view
1169     if (rect1.bottom() != rect2.bottom()) {
1170         ourrect.setX((int)[view frame].origin.x);
1171         ourrect.setWidth((int)[view frame].size.width);
1172     }
1173  
1174     // convert our rectangle to screen coordinates
1175     NSRect rect = NSMakeRect(ourrect.left(), ourrect.top(), ourrect.width(), ourrect.height());
1176     rect = NSOffsetRect(rect, -docView->contentsX(), -docView->contentsY());
1177     rect = [view convertRect:rect toView:nil];
1178     rect.origin = [[view window] convertBaseToScreen:rect.origin];
1179    
1180     // return the converted rect
1181     return [NSValue valueWithRect:rect];
1182 }
1183
1184 static CGColorRef CreateCGColorIfDifferent(NSColor *nsColor, CGColorRef existingColor)
1185 {
1186     // get color information assuming NSDeviceRGBColorSpace 
1187     NSColor *rgbColor = [nsColor colorUsingColorSpaceName:NSDeviceRGBColorSpace];
1188     if (rgbColor == nil) {
1189         rgbColor = [NSColor blackColor];
1190     }
1191     float components[4];
1192     [rgbColor getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]];
1193     
1194     // create a new CGColorRef to return
1195     CGColorSpaceRef cgColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
1196     CGColorRef cgColor = CGColorCreate(cgColorSpace, components);
1197     CGColorSpaceRelease(cgColorSpace);
1198     CFMakeCollectable(cgColor);
1199     
1200     // check for match with existing color
1201     if (existingColor && CGColorEqualToColor(cgColor, existingColor))
1202         cgColor = nil;
1203         
1204     return cgColor;
1205 }
1206
1207 static void AXAttributeStringSetColor(NSMutableAttributedString *attrString, NSString *attribute, NSColor* color, NSRange range)
1208 {
1209     if (color != nil) {
1210         CGColorRef existingColor = (CGColorRef) [attrString attribute:attribute atIndex:range.location effectiveRange:nil];
1211         CGColorRef cgColor = CreateCGColorIfDifferent(color, existingColor);
1212         if (cgColor != NULL) {
1213             [attrString addAttribute:attribute value:(id)cgColor range:range];
1214             CGColorRelease(cgColor);
1215         }
1216     } else
1217         [attrString removeAttribute:attribute range:range];
1218 }
1219
1220 static void AXAttributeStringSetNumber(NSMutableAttributedString *attrString, NSString *attribute, NSNumber* number, NSRange range)
1221 {
1222     if (number != nil) {
1223         [attrString addAttribute:attribute value:number range:range];
1224     } else
1225         [attrString removeAttribute:attribute range:range];
1226 }
1227
1228 static void AXAttributeStringSetFont(NSMutableAttributedString *attrString, NSString *attribute, NSFont* font, NSRange range)
1229 {
1230     NSDictionary *dict;
1231     
1232     if (font != nil) {
1233         dict = [NSDictionary dictionaryWithObjectsAndKeys:
1234             [font fontName]                             , NSAccessibilityFontNameKey,
1235             [font familyName]                           , NSAccessibilityFontFamilyKey,
1236             [font displayName]                          , NSAccessibilityVisibleNameKey,
1237             [NSNumber numberWithFloat:[font pointSize]] , NSAccessibilityFontSizeKey,
1238         nil];
1239
1240         [attrString addAttribute:attribute value:dict range:range];
1241     } else
1242         [attrString removeAttribute:attribute range:range];
1243     
1244 }
1245
1246 // NOTE: This wrapper is to get the NSAccessibilityForegroundColorTextAttribute string because
1247 // AppKit changed the name from NSAccessibilityForegoundColorTextAttribute w/o binary compatibliity.
1248 // It should be removed once everyone is building on the newer AppKit per <rdar://problem/4014691>.
1249 static NSString * NSAccessibilityForegroundColorTextAttributeWrapper(void)
1250 {
1251     static NSString * axForegroundColorTextAttribute = nil;
1252     
1253     // find the symbol just once
1254     if (axForegroundColorTextAttribute == nil) {
1255         // check for the new name
1256         NSSymbol axSymbol = NSLookupAndBindSymbol("_NSAccessibilityForegroundColorTextAttribute");
1257         
1258         // fall back to the old name
1259         if (axSymbol == nil)
1260             axSymbol = NSLookupAndBindSymbol("_NSAccessibilityForegoundColorTextAttribute");
1261         ASSERT(axSymbol);
1262         
1263         // get the addreess of variable address
1264         NSString ** axSymbolAddr = (NSString**) NSAddressOfSymbol(axSymbol);
1265         
1266         // get and save the string address
1267         axForegroundColorTextAttribute = *axSymbolAddr;
1268         ASSERT(axForegroundColorTextAttribute);
1269     }
1270     
1271     // return the symbol we can use
1272     return axForegroundColorTextAttribute;
1273 }
1274
1275 static void AXAttributeStringSetStyle(NSMutableAttributedString *attrString, RenderObject *renderer, NSRange range)
1276 {
1277     RenderStyle *style = renderer->style();
1278
1279     // set basic font info
1280     AXAttributeStringSetFont(attrString, NSAccessibilityFontTextAttribute, style->font().getNSFont(), range);
1281
1282     // set basic colors
1283     AXAttributeStringSetColor(attrString, NSAccessibilityForegroundColorTextAttributeWrapper(), style->color().getNSColor(), range);
1284     AXAttributeStringSetColor(attrString, NSAccessibilityBackgroundColorTextAttribute, style->backgroundColor().getNSColor(), range);
1285
1286     // set super/sub scripting
1287     EVerticalAlign alignment = style->verticalAlign();
1288     if (alignment == khtml::SUB)
1289         AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:(-1)], range);
1290     else if (alignment == khtml::SUPER)
1291         AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:1], range);
1292     else
1293         [attrString removeAttribute:NSAccessibilitySuperscriptTextAttribute range:range];
1294     
1295     // set shadow
1296     if (style->textShadow() != nil)
1297         AXAttributeStringSetNumber(attrString, NSAccessibilityShadowTextAttribute, [NSNumber numberWithBool:YES], range);
1298     else
1299         [attrString removeAttribute:NSAccessibilityShadowTextAttribute range:range];
1300     
1301     // set underline and strikethrough
1302     int decor = style->textDecorationsInEffect();
1303     if ((decor & khtml::UNDERLINE) == 0) {
1304         [attrString removeAttribute:NSAccessibilityUnderlineTextAttribute range:range];
1305         [attrString removeAttribute:NSAccessibilityUnderlineColorTextAttribute range:range];
1306     }
1307     
1308     if ((decor & khtml::LINE_THROUGH) == 0) {
1309         [attrString removeAttribute:NSAccessibilityStrikethroughTextAttribute range:range];
1310         [attrString removeAttribute:NSAccessibilityStrikethroughColorTextAttribute range:range];
1311     }
1312
1313     if ((decor & (khtml::UNDERLINE | khtml::LINE_THROUGH)) != 0) {
1314         // find colors using quirk mode approach (strict mode would use current
1315         // color for all but the root line box, which would use getTextDecorationColors)
1316         QColor underline, overline, linethrough;
1317         renderer->getTextDecorationColors(decor, underline, overline, linethrough);
1318         
1319         if ((decor & khtml::UNDERLINE) != 0) {
1320             AXAttributeStringSetNumber(attrString, NSAccessibilityUnderlineTextAttribute, [NSNumber numberWithBool:YES], range);
1321             AXAttributeStringSetColor(attrString, NSAccessibilityUnderlineColorTextAttribute, underline.getNSColor(), range);
1322         }
1323
1324         if ((decor & khtml::LINE_THROUGH) != 0) {
1325             AXAttributeStringSetNumber(attrString, NSAccessibilityStrikethroughTextAttribute, [NSNumber numberWithBool:YES], range);
1326             AXAttributeStringSetColor(attrString, NSAccessibilityStrikethroughColorTextAttribute, linethrough.getNSColor(), range);
1327         }
1328     }
1329 }
1330
1331 static void AXAttributeStringSetElement(NSMutableAttributedString *attrString, NSString *attribute, id element, NSRange range)
1332 {
1333     if (element != nil) {
1334         // make a serialiazable AX object
1335         AXUIElementRef axElement = NSAccessibilityCreateAXUIElementRef(element);
1336         if (axElement != NULL) {
1337             [attrString addAttribute:attribute value:(id)axElement range:range];
1338             CFRelease(axElement);
1339         }
1340     } else {
1341         [attrString removeAttribute:attribute range:range];
1342     }
1343 }
1344
1345 static KWQAccObject *AXLinkElementForNode (NodeImpl *node)
1346 {
1347     RenderObject *obj = node->renderer();
1348     if (!obj)
1349         return nil;
1350
1351     KWQAccObject *axObj = obj->document()->getAccObjectCache()->accObject(obj);
1352     HTMLAnchorElementImpl* anchor = [axObj anchorElement];
1353     if (!anchor || !anchor->renderer())
1354         return nil;
1355
1356     return anchor->renderer()->document()->getAccObjectCache()->accObject(anchor->renderer());
1357 }
1358
1359 static void AXAttributedStringAppendText (NSMutableAttributedString *attrString, NodeImpl *nodeImpl, const QChar *chars, int length)
1360 {
1361     // easier to calculate the range before appending the string
1362     NSRange attrStringRange = NSMakeRange([attrString length], length);
1363     
1364     // append the string from this node
1365     [[attrString mutableString] appendString:[NSString stringWithCharacters:(const UniChar*)chars length:length]];
1366
1367     // add new attributes and remove irrelevant inherited ones
1368     // NOTE: color attributes are handled specially because -[NSMutableAttributedString addAttribute: value: range:] does not merge
1369     // identical colors.  Workaround is to not replace an existing color attribute if it matches what we are adding.  This also means
1370     // we can not just pre-remove all inherited attributes on the appended string, so we have to remove the irrelevant ones individually.
1371
1372     // remove inherited attachment from prior AXAttributedStringAppendReplaced
1373     [attrString removeAttribute:NSAccessibilityAttachmentTextAttribute range:attrStringRange];
1374     
1375     // set new attributes
1376     AXAttributeStringSetStyle(attrString, nodeImpl->renderer(), attrStringRange);
1377     AXAttributeStringSetElement(attrString, NSAccessibilityLinkTextAttribute, AXLinkElementForNode(nodeImpl), attrStringRange);
1378 }
1379
1380 static void AXAttributedStringAppendReplaced (NSMutableAttributedString *attrString, NodeImpl *replacedNode)
1381 {
1382     static const UniChar attachmentChar = NSAttachmentCharacter;
1383
1384     // we should always be given a rendered node, but be safe
1385     if (!replacedNode || !replacedNode->renderer()) {
1386         ASSERT_NOT_REACHED();
1387         return;
1388     }
1389     
1390     // we should always be given a replaced node, but be safe
1391     // replaced nodes are either attachments (widgets) or images
1392     if (!replacedNode->renderer()->isReplaced()) {
1393         ASSERT_NOT_REACHED();
1394         return;
1395     }
1396         
1397     // create an AX object, but skip it if it is not supposed to be seen
1398     KWQAccObject *obj = replacedNode->renderer()->document()->getAccObjectCache()->accObject(replacedNode->renderer());
1399     if ([obj accessibilityIsIgnored])
1400         return;
1401     
1402     // easier to calculate the range before appending the string
1403     NSRange attrStringRange = NSMakeRange([attrString length], 1);
1404     
1405     // append the placeholder string
1406     [[attrString mutableString] appendString:[NSString stringWithCharacters:&attachmentChar length:1]];
1407     
1408     // remove all inherited attributes
1409     [attrString setAttributes:nil range:attrStringRange];
1410
1411     // add the attachment attribute
1412     AXAttributeStringSetElement(attrString, NSAccessibilityAttachmentTextAttribute, obj, attrStringRange);
1413 }
1414
1415 - (id)doAXAttributedStringForTextMarkerRange: (AXTextMarkerRangeRef) textMarkerRange
1416 {
1417     // extract the start and end VisiblePosition
1418     VisiblePosition startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange];
1419     if (startVisiblePosition.isNull())
1420         return nil;
1421     
1422     VisiblePosition endVisiblePosition = [self visiblePositionForEndOfTextMarkerRange: textMarkerRange];
1423     if (endVisiblePosition.isNull())
1424         return nil;
1425     
1426     // iterate over the range to build the AX attributed string
1427     NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
1428     TextIterator it(makeRange(startVisiblePosition, endVisiblePosition));
1429     while (!it.atEnd()) {
1430         // locate the node for this range
1431         Node node = it.range().startContainer();
1432         ASSERT(node == it.range().endContainer());
1433         NodeImpl *nodeImpl = node.handle();
1434         
1435         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
1436         if (it.length() != 0) {
1437             AXAttributedStringAppendText (attrString, nodeImpl, it.characters(), it.length());
1438         } else {
1439             AXAttributedStringAppendReplaced (attrString, nodeImpl->childNode(it.range().startOffset()));
1440         }
1441         
1442         it.advance();
1443     }
1444
1445     return [attrString autorelease];
1446 }
1447
1448 - (id)doAXTextMarkerRangeForUnorderedTextMarkers: (NSArray *) markers
1449 {
1450 #if MARKERARRAY_SELF_TEST
1451     AXTextMarkerRangeRef tmr = [self getSelectedTextMarkerRange];
1452     AXTextMarkerRef tm1 = AXTextMarkerRangeCopyEndMarker(tmr);
1453     AXTextMarkerRef tm2 = AXTextMarkerRangeCopyStartMarker(tmr);
1454     markers = [NSArray arrayWithObjects: (id) tm1, (id) tm2, nil];
1455 #endif
1456     // get and validate the markers
1457     if ([markers count] < 2)
1458         return nil;
1459     
1460     AXTextMarkerRef textMarker1 = (AXTextMarkerRef) [markers objectAtIndex:0];
1461     AXTextMarkerRef textMarker2 = (AXTextMarkerRef) [markers objectAtIndex:1];
1462     if (CFGetTypeID(textMarker1) != AXTextMarkerGetTypeID() || CFGetTypeID(textMarker2) != AXTextMarkerGetTypeID())
1463         return nil;
1464     
1465     // convert to VisiblePosition
1466     VisiblePosition visiblePos1 = [self visiblePositionForTextMarker:textMarker1];
1467     VisiblePosition visiblePos2 = [self visiblePositionForTextMarker:textMarker2];
1468     if (visiblePos1.isNull() || visiblePos2.isNull())
1469         return nil;
1470     
1471     // use the Selection class to do the ordering
1472     // NOTE: Perhaps we could add a Selection method to indicate direction, based on m_baseIsStart
1473     AXTextMarkerRef startTextMarker;
1474     AXTextMarkerRef endTextMarker;
1475     Selection   sel(visiblePos1, visiblePos2);
1476     if (sel.base() == sel.start()) {
1477         startTextMarker = textMarker1;
1478         endTextMarker = textMarker2;
1479     } else {
1480         startTextMarker = textMarker2;
1481         endTextMarker = textMarker1;
1482     }
1483     
1484     // return a range based on the Selection verdict
1485     return (id) [self textMarkerRangeFromMarkers: startTextMarker andEndMarker:endTextMarker];
1486 }
1487
1488 - (id)doAXNextTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1489 {
1490     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1491     VisiblePosition nextVisiblePos = visiblePos.next();
1492     if (nextVisiblePos.isNull())
1493         return nil;
1494     
1495     return (id) [self textMarkerForVisiblePosition:nextVisiblePos];
1496 }
1497
1498 - (id)doAXPreviousTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1499 {
1500     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1501     VisiblePosition previousVisiblePos = visiblePos.previous();
1502     if (previousVisiblePos.isNull())
1503         return nil;
1504     
1505     return (id) [self textMarkerForVisiblePosition:previousVisiblePos];
1506 }
1507
1508 - (id)doAXLeftWordTextMarkerRangeForTextMarker: (AXTextMarkerRef) textMarker
1509 {
1510     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1511     VisiblePosition startPosition = startOfWord(visiblePos, khtml::LeftWordIfOnBoundary);
1512     VisiblePosition endPosition = endOfWord(startPosition);
1513
1514     return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
1515 }
1516
1517 - (id)doAXRightWordTextMarkerRangeForTextMarker: (AXTextMarkerRef) textMarker
1518 {
1519     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1520     VisiblePosition startPosition = startOfWord(visiblePos, khtml::RightWordIfOnBoundary);
1521     VisiblePosition endPosition = endOfWord(startPosition);
1522
1523     return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
1524 }
1525
1526 - (id)doAXLeftLineTextMarkerRangeForTextMarker: (AXTextMarkerRef) textMarker
1527 {
1528     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1529     if (visiblePos.isNull())
1530         return nil;
1531     
1532     // make a caret selection for the position before marker position (to make sure
1533     // we move off of a line start)
1534     VisiblePosition prevVisiblePos = visiblePos.previous();
1535     if (prevVisiblePos.isNull())
1536         return nil;
1537     
1538     VisiblePosition startPosition = startOfLine(prevVisiblePos);
1539     VisiblePosition endPosition = endOfLine(prevVisiblePos);
1540     return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
1541 }
1542
1543 - (id)doAXRightLineTextMarkerRangeForTextMarker: (AXTextMarkerRef) textMarker
1544 {
1545     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1546     if (visiblePos.isNull())
1547         return nil;
1548     
1549     // make sure we move off of a line end
1550     VisiblePosition nextVisiblePos = visiblePos.next();
1551     if (nextVisiblePos.isNull())
1552         return nil;
1553         
1554     VisiblePosition startPosition = startOfLine(nextVisiblePos);
1555     VisiblePosition endPosition = endOfLine(nextVisiblePos);
1556     return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
1557 }
1558
1559 - (id)doAXSentenceTextMarkerRangeForTextMarker: (AXTextMarkerRef) textMarker
1560 {
1561     // NOTE: BUG FO 2 IMPLEMENT (currently returns incorrect answer)
1562     // Related? <rdar://problem/3927736> Text selection broken in 8A336
1563     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1564     VisiblePosition startPosition = startOfSentence(visiblePos);
1565     VisiblePosition endPosition = endOfSentence(startPosition);
1566
1567     return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
1568 }
1569
1570 - (id)doAXParagraphTextMarkerRangeForTextMarker: (AXTextMarkerRef) textMarker
1571 {
1572     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1573     VisiblePosition startPosition = startOfParagraph(visiblePos);
1574     VisiblePosition endPosition = endOfParagraph(startPosition);
1575
1576     return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
1577 }
1578
1579 - (id)doAXNextWordEndTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1580 {
1581     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1582     if (visiblePos.isNull())
1583         return nil;
1584
1585     // make sure we move off of a word end
1586     visiblePos = visiblePos.next();
1587     if (visiblePos.isNull())
1588         return nil;
1589
1590     VisiblePosition endPosition = endOfWord(visiblePos, khtml::RightWordIfOnBoundary);
1591     return (id) [self textMarkerForVisiblePosition:endPosition];
1592 }
1593
1594 - (id)doAXPreviousWordStartTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1595 {
1596     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1597     if (visiblePos.isNull())
1598         return nil;
1599
1600     // make sure we move off of a word start
1601     visiblePos = visiblePos.previous();
1602     if (visiblePos.isNull())
1603         return nil;
1604     
1605     VisiblePosition startPosition = startOfWord(visiblePos, khtml::LeftWordIfOnBoundary);
1606     return (id) [self textMarkerForVisiblePosition:startPosition];
1607 }
1608
1609 - (id)doAXNextLineEndTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1610 {
1611     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1612     if (visiblePos.isNull())
1613         return nil;
1614     
1615     // to make sure we move off of a line end
1616     VisiblePosition nextVisiblePos = visiblePos.next();
1617     if (nextVisiblePos.isNull())
1618         return nil;
1619         
1620     VisiblePosition endPosition = endOfLine(nextVisiblePos);
1621     return (id) [self textMarkerForVisiblePosition: endPosition];
1622 }
1623
1624 - (id)doAXPreviousLineStartTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1625 {
1626     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1627     if (visiblePos.isNull())
1628         return nil;
1629     
1630     // make sure we move off of a line start
1631     VisiblePosition prevVisiblePos = visiblePos.previous();
1632     if (prevVisiblePos.isNull())
1633         return nil;
1634         
1635     VisiblePosition startPosition = startOfLine(prevVisiblePos);
1636     return (id) [self textMarkerForVisiblePosition: startPosition];
1637 }
1638
1639 - (id)doAXNextSentenceEndTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1640 {
1641     // NOTE: BUG FO 2 IMPLEMENT (currently returns incorrect answer)
1642     // Related? <rdar://problem/3927736> Text selection broken in 8A336
1643     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1644     if (visiblePos.isNull())
1645         return nil;
1646     
1647     // make sure we move off of a sentence end
1648     visiblePos = visiblePos.next();
1649     if (visiblePos.isNull())
1650         return nil;
1651
1652     VisiblePosition endPosition = endOfSentence(visiblePos);
1653     return (id) [self textMarkerForVisiblePosition: endPosition];
1654 }
1655
1656 - (id)doAXPreviousSentenceStartTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1657 {
1658     // NOTE: BUG FO 2 IMPLEMENT (currently returns incorrect answer)
1659     // Related? <rdar://problem/3927736> Text selection broken in 8A336
1660     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1661     if (visiblePos.isNull())
1662         return nil;
1663
1664     // make sure we move off of a sentence start
1665     visiblePos = visiblePos.previous();
1666     if (visiblePos.isNull())
1667         return nil;
1668
1669     VisiblePosition startPosition = startOfSentence(visiblePos);
1670     return (id) [self textMarkerForVisiblePosition: startPosition];
1671 }
1672
1673 - (id)doAXNextParagraphEndTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1674 {
1675     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1676     if (visiblePos.isNull())
1677         return nil;
1678     
1679     // make sure we move off of a paragraph end
1680     visiblePos = visiblePos.next();
1681     if (visiblePos.isNull())
1682         return nil;
1683
1684     VisiblePosition endPosition = endOfParagraph(visiblePos);
1685     return (id) [self textMarkerForVisiblePosition: endPosition];
1686 }
1687
1688 - (id)doAXPreviousParagraphStartTextMarkerForTextMarker: (AXTextMarkerRef) textMarker
1689 {
1690     VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
1691     if (visiblePos.isNull())
1692         return nil;
1693
1694     // make sure we move off of a paragraph start
1695     visiblePos = visiblePos.previous();
1696     if (visiblePos.isNull())
1697         return nil;
1698
1699     VisiblePosition startPosition = startOfParagraph(visiblePos);
1700     return (id) [self textMarkerForVisiblePosition: startPosition];
1701 }
1702
1703 - (id)doAXLengthForTextMarkerRange: (AXTextMarkerRangeRef) textMarkerRange
1704 {
1705     // NOTE: BUG Multi-byte support
1706     CFStringRef string = (CFStringRef) [self doAXStringForTextMarkerRange: textMarkerRange];
1707     if (!string)
1708         return nil;
1709
1710     return [NSNumber numberWithInt:CFStringGetLength(string)];
1711 }
1712
1713 - (id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter
1714 {
1715     AXTextMarkerRef         textMarker = nil;
1716     AXTextMarkerRangeRef    textMarkerRange = nil;
1717     NSNumber *              number = nil;
1718     NSArray *               array = nil;
1719     KWQAccObject *          uiElement = nil;
1720     NSPoint                 point = {0.0, 0.0};
1721     bool                    pointSet = false;
1722     CFTypeID                paramType;
1723     
1724     // basic parameter validation
1725     if (!m_renderer || !attribute || !parameter)
1726         return nil;
1727
1728     // common parameter type check/casting.  Nil checks in handlers catch wrong type case.
1729     // NOTE: This assumes nil is not a valid parameter, because it is indistinguishable from
1730     // a parameter of the wrong type.
1731     paramType = CFGetTypeID(parameter);
1732     if (paramType == AXTextMarkerGetTypeID())
1733         textMarker = (AXTextMarkerRef) parameter;
1734
1735     else if (paramType == AXTextMarkerRangeGetTypeID())
1736         textMarkerRange = (AXTextMarkerRangeRef) parameter;
1737
1738     else if ([parameter isKindOfClass:[KWQAccObject self]])
1739         uiElement = (KWQAccObject *) parameter;
1740
1741     else if ([parameter isKindOfClass:[NSNumber self]])
1742         number = parameter;
1743
1744     else if ([parameter isKindOfClass:[NSArray self]])
1745         array = parameter;
1746     
1747     else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue *)parameter objCType], @encode(NSPoint)) == 0) {
1748         pointSet = true;
1749         point = [(NSValue *)parameter pointValue];
1750
1751     } else {
1752         // got a parameter of a type we never use
1753         // NOTE: No ASSERT_NOT_REACHED because this can happen accidentally 
1754         // while using accesstool (e.g.), forcing you to start over
1755         return nil;
1756     }
1757   
1758     // dispatch
1759     if ([attribute isEqualToString: @"AXUIElementForTextMarker"])
1760         return [self doAXUIElementForTextMarker: textMarker];
1761
1762     if ([attribute isEqualToString: @"AXTextMarkerRangeForUIElement"])
1763         return [self doAXTextMarkerRangeForUIElement: uiElement];
1764
1765     if ([attribute isEqualToString: (NSString *) kAXLineForTextMarkerParameterizedAttribute])
1766         return [self doAXLineForTextMarker: textMarker];
1767
1768     if ([attribute isEqualToString: (NSString *) kAXTextMarkerRangeForLineParameterizedAttribute])
1769         return [self doAXTextMarkerRangeForLine: number];
1770
1771     if ([attribute isEqualToString: (NSString *) kAXStringForTextMarkerRangeParameterizedAttribute])
1772         return [self doAXStringForTextMarkerRange: textMarkerRange];
1773
1774     if ([attribute isEqualToString: (NSString *) kAXTextMarkerForPositionParameterizedAttribute])
1775         return pointSet ? [self doAXTextMarkerForPosition: point] : nil;
1776
1777     if ([attribute isEqualToString: (NSString *) kAXBoundsForTextMarkerRangeParameterizedAttribute])
1778         return [self doAXBoundsForTextMarkerRange: textMarkerRange];
1779
1780     if ([attribute isEqualToString: (NSString *) kAXAttributedStringForTextMarkerRangeParameterizedAttribute])
1781         return [self doAXAttributedStringForTextMarkerRange: textMarkerRange];
1782
1783     if ([attribute isEqualToString: (NSString *) kAXTextMarkerRangeForUnorderedTextMarkersParameterizedAttribute])
1784         return [self doAXTextMarkerRangeForUnorderedTextMarkers: array];
1785
1786     if ([attribute isEqualToString: (NSString *) kAXNextTextMarkerForTextMarkerParameterizedAttribute])
1787         return [self doAXNextTextMarkerForTextMarker: textMarker];
1788
1789     if ([attribute isEqualToString: (NSString *) kAXPreviousTextMarkerForTextMarkerParameterizedAttribute])
1790         return [self doAXPreviousTextMarkerForTextMarker: textMarker];
1791
1792     if ([attribute isEqualToString: (NSString *) kAXLeftWordTextMarkerRangeForTextMarkerParameterizedAttribute])
1793         return [self doAXLeftWordTextMarkerRangeForTextMarker: textMarker];
1794
1795     if ([attribute isEqualToString: (NSString *) kAXRightWordTextMarkerRangeForTextMarkerParameterizedAttribute])
1796         return [self doAXRightWordTextMarkerRangeForTextMarker: textMarker];
1797
1798     if ([attribute isEqualToString: (NSString *) kAXLeftLineTextMarkerRangeForTextMarkerParameterizedAttribute])
1799         return [self doAXLeftLineTextMarkerRangeForTextMarker: textMarker];
1800
1801     if ([attribute isEqualToString: (NSString *) kAXRightLineTextMarkerRangeForTextMarkerParameterizedAttribute])
1802         return [self doAXRightLineTextMarkerRangeForTextMarker: textMarker];
1803
1804     if ([attribute isEqualToString: (NSString *) kAXSentenceTextMarkerRangeForTextMarkerParameterizedAttribute])
1805         return [self doAXSentenceTextMarkerRangeForTextMarker: textMarker];
1806
1807     if ([attribute isEqualToString: (NSString *) kAXParagraphTextMarkerRangeForTextMarkerParameterizedAttribute])
1808         return [self doAXParagraphTextMarkerRangeForTextMarker: textMarker];
1809
1810     if ([attribute isEqualToString: (NSString *) kAXNextWordEndTextMarkerForTextMarkerParameterizedAttribute])
1811         return [self doAXNextWordEndTextMarkerForTextMarker: textMarker];
1812
1813     if ([attribute isEqualToString: (NSString *) kAXPreviousWordStartTextMarkerForTextMarkerParameterizedAttribute])
1814         return [self doAXPreviousWordStartTextMarkerForTextMarker: textMarker];
1815         
1816     if ([attribute isEqualToString: (NSString *) kAXNextLineEndTextMarkerForTextMarkerParameterizedAttribute])
1817         return [self doAXNextLineEndTextMarkerForTextMarker: textMarker];
1818         
1819     if ([attribute isEqualToString: (NSString *) kAXPreviousLineStartTextMarkerForTextMarkerParameterizedAttribute])
1820         return [self doAXPreviousLineStartTextMarkerForTextMarker: textMarker];
1821         
1822     if ([attribute isEqualToString: (NSString *) kAXNextSentenceEndTextMarkerForTextMarkerParameterizedAttribute])
1823         return [self doAXNextSentenceEndTextMarkerForTextMarker: textMarker];
1824         
1825     if ([attribute isEqualToString: (NSString *) kAXPreviousSentenceStartTextMarkerForTextMarkerParameterizedAttribute])
1826         return [self doAXPreviousSentenceStartTextMarkerForTextMarker: textMarker];
1827         
1828     if ([attribute isEqualToString: (NSString *) kAXNextParagraphEndTextMarkerForTextMarkerParameterizedAttribute])
1829         return [self doAXNextParagraphEndTextMarkerForTextMarker: textMarker];
1830
1831     if ([attribute isEqualToString: (NSString *) kAXPreviousParagraphStartTextMarkerForTextMarkerParameterizedAttribute])
1832         return [self doAXPreviousParagraphStartTextMarkerForTextMarker: textMarker];
1833         
1834     if ([attribute isEqualToString: (NSString *) kAXLengthForTextMarkerRangeParameterizedAttribute])
1835         return [self doAXLengthForTextMarkerRange: textMarkerRange];
1836
1837     return nil;
1838 }
1839
1840 #endif
1841
1842 - (id)accessibilityHitTest:(NSPoint)point
1843 {
1844     if (!m_renderer)
1845         return NSAccessibilityUnignoredAncestor(self);
1846     
1847     RenderObject::NodeInfo nodeInfo(true, true);
1848     m_renderer->layer()->hitTest(nodeInfo, (int)point.x, (int)point.y);
1849     if (!nodeInfo.innerNode())
1850         return NSAccessibilityUnignoredAncestor(self);
1851     RenderObject* obj = nodeInfo.innerNode()->renderer();
1852     if (!obj)
1853         return NSAccessibilityUnignoredAncestor(self);
1854     
1855     KWQAccObject * accObject = obj->document()->getAccObjectCache()->accObject(obj);
1856     return NSAccessibilityUnignoredAncestor(accObject);
1857 }
1858
1859 // _accessibilityParentForSubview is called by AppKit when moving up the tree
1860 // we override it so that we can return our KWQAccObject parent of an AppKit AX object
1861 - (id)_accessibilityParentForSubview:(NSView *)subview
1862 {   
1863     ASSERT(m_renderer && m_renderer->document());
1864     
1865     KHTMLPart* docPart = m_renderer->document()->part();
1866     if (!docPart)
1867         return nil;
1868     
1869     // check for nested WebArea (WebFrameView does not support elementForView)
1870     if (m_renderer->isCanvas() && ([subview superview] == docPart->view()->getDocumentView()))
1871         return [self accessibilityIsIgnored] == false ? self : [self parentObjectUnignored];
1872        
1873     // check for view that supports elementForView
1874     DOMElement *domElement = [KWQ(docPart)->bridge() elementForView:subview];
1875     if (!domElement)
1876         return nil;
1877         
1878     RenderObject *renderer = [domElement _elementImpl]->renderer();
1879     if (!renderer)
1880         return nil;
1881         
1882     KWQAccObject* obj = renderer->document()->getAccObjectCache()->accObject(renderer);
1883     return [obj parentObjectUnignored];
1884 }
1885
1886 - (id)accessibilityFocusedUIElement
1887 {
1888     // NOTE: BUG support nested WebAreas
1889     NodeImpl *focusNode = m_renderer->document()->focusNode();
1890     if (!focusNode || !focusNode->renderer())
1891         return nil;
1892
1893     KWQAccObject* obj = focusNode->renderer()->document()->getAccObjectCache()->accObject(focusNode->renderer());
1894     
1895     // the HTML element, for example, is focusable but has an AX object that is ignored
1896     if ([obj accessibilityIsIgnored])
1897         obj = [obj parentObjectUnignored];
1898     
1899     return obj;
1900 }
1901
1902 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName
1903 {
1904 #if OMIT_TIGER_FEATURES
1905 // no parameterized attributes in Panther... they were introduced in Tiger
1906 #else
1907     if ([attributeName isEqualToString: (NSString *) kAXSelectedTextMarkerRangeAttribute])
1908         return YES;
1909     if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) {
1910         if ([[self role] isEqualToString:@"AXLink"])
1911             return YES;
1912     }
1913 #endif
1914
1915     return NO;
1916 }
1917
1918 #if OMIT_TIGER_FEATURES
1919 // no parameterized attributes in Panther... they were introduced in Tiger
1920 #else
1921 - (void)doSetAXSelectedTextMarkerRange: (AXTextMarkerRangeRef)textMarkerRange
1922 {
1923     // extract the start and end VisiblePosition
1924     VisiblePosition startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange];
1925     if (startVisiblePosition.isNull())
1926         return;
1927     
1928     VisiblePosition endVisiblePosition = [self visiblePositionForEndOfTextMarkerRange: textMarkerRange];
1929     if (endVisiblePosition.isNull())
1930         return;
1931     
1932     // make selection and tell the document to use it
1933     // NOTE: BUG support nested WebAreas
1934     Selection sel = Selection(startVisiblePosition, endVisiblePosition);
1935     [self topDocument]->part()->setSelection(sel);
1936 }
1937
1938 - (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attributeName;
1939 {
1940     AXTextMarkerRangeRef    textMarkerRange = nil;
1941     NSNumber *              number = nil;
1942
1943     // decode the parameter
1944     if (CFGetTypeID(value) == AXTextMarkerRangeGetTypeID())
1945         textMarkerRange = (AXTextMarkerRangeRef) value;
1946
1947     else if ([value isKindOfClass:[NSNumber self]])
1948         number = value;
1949     
1950     // handle the command
1951     if ([attributeName isEqualToString: (NSString *) kAXSelectedTextMarkerRangeAttribute]) {
1952         ASSERT(textMarkerRange);
1953         [self doSetAXSelectedTextMarkerRange:textMarkerRange];
1954         
1955     } else if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) {
1956         ASSERT(number);
1957         if ([[self role] isEqualToString:@"AXLink"]) {
1958             if ([number intValue] != 0)
1959                 m_renderer->document()->setFocusNode(m_renderer->element());
1960             else
1961                 m_renderer->document()->setFocusNode(0);
1962         }
1963     }
1964 }
1965 #endif
1966
1967 - (void)childrenChanged
1968 {
1969     [self clearChildren];
1970     
1971     if ([self accessibilityIsIgnored])
1972         [[self parentObject] childrenChanged];
1973 }
1974
1975 - (void)clearChildren
1976 {
1977     [m_children release];
1978     m_children = nil;
1979 }
1980
1981 -(KWQAccObjectID)accObjectID
1982 {
1983     return m_accObjectID;
1984 }
1985
1986 -(void)setAccObjectID:(KWQAccObjectID) accObjectID
1987 {
1988     m_accObjectID = accObjectID;
1989 }
1990
1991 - (void)removeAccObjectID
1992 {
1993     m_renderer->document()->getAccObjectCache()->removeAccObjectID(self);
1994 }
1995
1996 @end