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