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