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