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