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