AX: Make SVG Group containers accessible elements
[WebKit-https.git] / Source / WebCore / accessibility / ios / WebAccessibilityObjectWrapperIOS.mm
1 /*
2  * Copyright (C) 2008, Apple 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 #import "config.h"
27 #import "WebAccessibilityObjectWrapperIOS.h"
28
29 #if HAVE(ACCESSIBILITY) && PLATFORM(IOS)
30
31 #import "AccessibilityRenderObject.h"
32 #import "AccessibilityTable.h"
33 #import "AccessibilityTableCell.h"
34 #import "Font.h"
35 #import "Frame.h"
36 #import "FrameSelection.h"
37 #import "FrameView.h"
38 #import "HitTestResult.h"
39 #import "HTMLFrameOwnerElement.h"
40 #import "HTMLInputElement.h"
41 #import "HTMLNames.h"
42 #import "IntRect.h"
43 #import "IntSize.h"
44 #import "Range.h"
45 #import "RenderView.h"
46 #import "RuntimeApplicationChecksIOS.h"
47 #import "SVGNames.h"
48 #import "TextIterator.h"
49 #import "WAKScrollView.h"
50 #import "WAKView.h"
51 #import "WAKWindow.h"
52 #import "WebCoreThread.h"
53 #import "visible_units.h"
54
55 #import <GraphicsServices/GraphicsServices.h>
56
57 @interface NSObject (AccessibilityPrivate)
58 - (void)_accessibilityUnregister;
59 - (NSString *)accessibilityLabel;
60 - (NSString *)accessibilityValue;
61 - (BOOL)isAccessibilityElement;
62 - (NSInteger)accessibilityElementCount;
63 - (id)accessibilityElementAtIndex:(NSInteger)index;
64 - (NSInteger)indexOfAccessibilityElement:(id)element;
65 @end
66
67 @interface WebAccessibilityObjectWrapper (AccessibilityPrivate)
68 - (id)_accessibilityWebDocumentView;
69 - (id)accessibilityContainer;
70 - (void)setAccessibilityLabel:(NSString *)label;
71 - (void)setAccessibilityValue:(NSString *)value;
72 - (BOOL)containsUnnaturallySegmentedChildren;
73 - (NSInteger)positionForTextMarker:(id)marker;
74 @end
75
76 @interface WAKView (iOSAccessibility)
77 - (BOOL)accessibilityIsIgnored;
78 @end
79
80 using namespace WebCore;
81 using namespace HTMLNames;
82
83 // These are tokens accessibility uses to denote attributes. 
84 static NSString * const UIAccessibilityTokenBlockquoteLevel = @"UIAccessibilityTokenBlockquoteLevel";
85 static NSString * const UIAccessibilityTokenHeadingLevel = @"UIAccessibilityTokenHeadingLevel";
86 static NSString * const UIAccessibilityTokenFontName = @"UIAccessibilityTokenFontName";
87 static NSString * const UIAccessibilityTokenFontFamily = @"UIAccessibilityTokenFontFamily";
88 static NSString * const UIAccessibilityTokenFontSize = @"UIAccessibilityTokenFontSize";
89 static NSString * const UIAccessibilityTokenBold = @"UIAccessibilityTokenBold";
90 static NSString * const UIAccessibilityTokenItalic = @"UIAccessibilityTokenItalic";
91 static NSString * const UIAccessibilityTokenUnderline = @"UIAccessibilityTokenUnderline";
92
93 static AccessibilityObjectWrapper* AccessibilityUnignoredAncestor(AccessibilityObjectWrapper *wrapper)
94 {
95     while (wrapper && ![wrapper isAccessibilityElement]) {
96         AccessibilityObject* object = [wrapper accessibilityObject];
97         if (!object)
98             break;
99         
100         if ([wrapper isAttachment] && ![[wrapper attachmentView] accessibilityIsIgnored])
101             break;
102             
103         AccessibilityObject* parentObject = object->parentObjectUnignored();
104         if (!parentObject)
105             break;
106
107         wrapper = parentObject->wrapper();
108     }
109     return wrapper;
110 }
111
112 #pragma mark Accessibility Text Marker
113
114 @interface WebAccessibilityTextMarker : NSObject
115 {
116     AXObjectCache* _cache;
117     TextMarkerData _textMarkerData;
118 }
119
120 + (WebAccessibilityTextMarker *)textMarkerWithVisiblePosition:(VisiblePosition&)visiblePos cache:(AXObjectCache*)cache;
121
122 @end
123
124 @implementation WebAccessibilityTextMarker
125
126 - (id)initWithTextMarker:(TextMarkerData *)data cache:(AXObjectCache*)cache
127 {
128     if (!(self = [super init]))
129         return nil;
130     
131     _cache = cache;
132     memcpy(&_textMarkerData, data, sizeof(TextMarkerData));
133     return self;
134 }
135
136 - (id)initWithData:(NSData *)data cache:(AXObjectCache*)cache
137 {
138     if (!(self = [super init]))
139         return nil;
140     
141     _cache = cache;
142     [data getBytes:&_textMarkerData length:sizeof(TextMarkerData)];
143     
144     return self;
145 }
146
147 // This is needed for external clients to be able to create a text marker without having a pointer to the cache.
148 - (id)initWithData:(NSData *)data accessibilityObject:(AccessibilityObjectWrapper *)wrapper
149 {
150     WebCore::AccessibilityObject* axObject = [wrapper accessibilityObject]; 
151     if (!axObject)
152         return nil;
153     
154     return [self initWithData:data cache:axObject->axObjectCache()];
155 }
156
157 + (WebAccessibilityTextMarker *)textMarkerWithVisiblePosition:(VisiblePosition&)visiblePos cache:(AXObjectCache*)cache
158 {
159     TextMarkerData textMarkerData;
160     cache->textMarkerDataForVisiblePosition(textMarkerData, visiblePos);
161     
162     return [[[WebAccessibilityTextMarker alloc] initWithTextMarker:&textMarkerData cache:cache] autorelease];
163 }
164
165 - (NSData *)dataRepresentation
166 {
167     return [NSData dataWithBytes:&_textMarkerData length:sizeof(TextMarkerData)];
168 }
169
170 - (VisiblePosition)visiblePosition
171 {
172     return _cache->visiblePositionForTextMarkerData(_textMarkerData);
173 }
174
175 - (NSString *)description
176 {
177     return [NSString stringWithFormat:@"[AXTextMarker %p] = node: %p offset: %d", self, _textMarkerData.node, _textMarkerData.offset];
178 }
179
180 @end
181
182 @implementation WebAccessibilityObjectWrapper
183
184 - (id)initWithAccessibilityObject:(AccessibilityObject*)axObject
185 {
186     self = [super initWithAccessibilityObject:axObject];
187     if (!self)
188         return nil;
189     
190     // Initialize to a sentinel value.
191     m_accessibilityTraitsFromAncestor = ULLONG_MAX;
192     m_isAccessibilityElement = -1;
193     
194     return self;
195 }
196
197 - (void)detach
198 {
199     // rdar://8798960 Make sure the object is gone early, so that anything _accessibilityUnregister 
200     // does can't call back into the render tree.
201     m_object = 0;
202
203     if ([self respondsToSelector:@selector(_accessibilityUnregister)])
204         [self _accessibilityUnregister];
205 }
206
207 - (void)dealloc
208 {
209     // We should have been detached before deallocated.
210     ASSERT(!m_object);
211     [super dealloc];
212 }
213
214 - (BOOL)_prepareAccessibilityCall
215 {
216     // rdar://7980318 if we start a call, then block in WebThreadLock(), then we're dealloced on another, thread, we could
217     // crash, so we should retain ourself for the duration of usage here.
218     [[self retain] autorelease];
219
220     WebThreadLock();
221     
222     // If we came back from our thread lock and we were detached, we will no longer have an m_object.
223     if (!m_object)
224         return NO;
225     
226     m_object->updateBackingStore();
227     if (!m_object)
228         return NO;
229     
230     return YES;
231 }
232
233 // These are here so that we don't have to import AXRuntime.
234 // The methods will be swizzled when the accessibility bundle is loaded.
235
236 - (uint64_t)_axLinkTrait { return (1 << 0); }
237 - (uint64_t)_axVisitedTrait { return (1 << 1); }
238 - (uint64_t)_axHeaderTrait { return (1 << 2); }
239 - (uint64_t)_axContainedByListTrait { return (1 << 3); }
240 - (uint64_t)_axContainedByTableTrait { return (1 << 4); }
241 - (uint64_t)_axContainedByLandmarkTrait { return (1 << 5); }
242 - (uint64_t)_axWebContentTrait { return (1 << 6); }
243 - (uint64_t)_axSecureTextFieldTrait { return (1 << 7); }
244 - (uint64_t)_axTextEntryTrait { return (1 << 8); }
245 - (uint64_t)_axHasTextCursorTrait { return (1 << 9); }
246 - (uint64_t)_axTextOperationsAvailableTrait { return (1 << 10); }
247 - (uint64_t)_axImageTrait { return (1 << 11); }
248 - (uint64_t)_axTabButtonTrait { return (1 << 12); }
249 - (uint64_t)_axButtonTrait { return (1 << 13); }
250 - (uint64_t)_axToggleTrait { return (1 << 14); }
251 - (uint64_t)_axPopupButtonTrait { return (1 << 15); }
252 - (uint64_t)_axStaticTextTrait { return (1 << 16); }
253 - (uint64_t)_axAdjustableTrait { return (1 << 17); }
254 - (uint64_t)_axMenuItemTrait { return (1 << 18); }
255 - (uint64_t)_axSelectedTrait { return (1 << 19); }
256 - (uint64_t)_axNotEnabledTrait { return (1 << 20); }
257 - (uint64_t)_axRadioButtonTrait { return (1 << 21); }
258
259 - (BOOL)accessibilityCanFuzzyHitTest
260 {
261     if (![self _prepareAccessibilityCall])
262         return nil;
263     
264     AccessibilityRole role = m_object->roleValue();
265     // Elements that can be returned when performing fuzzy hit testing.
266     switch (role) {
267     case ButtonRole:
268     case CheckBoxRole:
269     case ComboBoxRole:
270     case DisclosureTriangleRole:
271     case HeadingRole:
272     case ImageMapLinkRole:
273     case ImageRole:
274     case LinkRole:
275     case ListBoxRole:
276     case ListBoxOptionRole:
277     case MenuButtonRole:
278     case MenuItemRole:
279     case PopUpButtonRole:
280     case RadioButtonRole:
281     case ScrollBarRole:
282     case SliderRole:
283     case StaticTextRole:
284     case TabRole:
285     case TextFieldRole:
286         return !m_object->accessibilityIsIgnored();
287     default:
288         return false;
289     }
290 }
291
292 - (AccessibilityObjectWrapper *)accessibilityPostProcessHitTest:(CGPoint)point
293 {
294     UNUSED_PARAM(point);
295     // The UIKit accessibility wrapper will override this and perform the post process hit test.
296     return nil;
297 }
298
299 - (id)accessibilityHitTest:(CGPoint)point
300 {
301     if (![self _prepareAccessibilityCall])
302         return nil;
303     
304     // Try a fuzzy hit test first to find an accessible element.
305     RefPtr<AccessibilityObject> axObject = m_object->accessibilityHitTest(IntPoint(point));
306     if (!axObject)
307         return nil;
308     
309     // If this is a good accessible object to return, no extra work is required.
310     if ([axObject->wrapper() accessibilityCanFuzzyHitTest])
311         return AccessibilityUnignoredAncestor(axObject->wrapper());
312     
313     // Check to see if we can post-process this hit test to find a better candidate.
314     AccessibilityObjectWrapper* wrapper = [axObject->wrapper() accessibilityPostProcessHitTest:point];
315     if (wrapper)
316         return AccessibilityUnignoredAncestor(wrapper);
317     
318     // Fall back to default behavior.
319     return AccessibilityUnignoredAncestor(axObject->wrapper());    
320 }
321
322 - (NSInteger)accessibilityElementCount
323 {
324     if (![self _prepareAccessibilityCall])
325         return nil;
326
327     if ([self isAttachment])
328         return [[self attachmentView] accessibilityElementCount];
329     
330     return m_object->children().size();
331 }
332
333 - (id)accessibilityElementAtIndex:(NSInteger)index
334 {
335     if (![self _prepareAccessibilityCall])
336         return nil;
337
338     if ([self isAttachment])
339         return [[self attachmentView] accessibilityElementAtIndex:index];
340     
341     AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
342     if (static_cast<unsigned>(index) >= children.size())
343         return nil;
344     
345     AccessibilityObjectWrapper* wrapper = children[index]->wrapper();
346     if (children[index]->isAttachment())
347         return [wrapper attachmentView];
348
349     return wrapper;
350 }
351
352 - (NSInteger)indexOfAccessibilityElement:(id)element
353 {
354     if (![self _prepareAccessibilityCall])
355         return NSNotFound;
356     
357     if ([self isAttachment])
358         return [[self attachmentView] indexOfAccessibilityElement:element];
359     
360     AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
361     unsigned count = children.size();
362     for (unsigned k = 0; k < count; ++k) {
363         AccessibilityObjectWrapper* wrapper = children[k]->wrapper();
364         if (wrapper == element || (children[k]->isAttachment() && [wrapper attachmentView] == element))
365             return k;
366     }
367     
368     return NSNotFound;
369 }
370
371 - (NSString *)accessibilityLanguage
372 {
373     if (![self _prepareAccessibilityCall])
374         return nil;
375     
376     return m_object->language();
377 }
378
379 - (BOOL)_accessibilityIsLandmarkRole:(AccessibilityRole)role
380 {
381     switch (role) {
382         case LandmarkApplicationRole:
383         case LandmarkBannerRole:
384         case LandmarkComplementaryRole:
385         case LandmarkContentInfoRole:
386         case LandmarkMainRole:
387         case LandmarkNavigationRole:
388         case LandmarkSearchRole:
389             return YES;
390         default:
391             return NO;
392     }    
393 }
394
395 - (AccessibilityObjectWrapper*)_accessibilityListAncestor
396 {
397     for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
398         AccessibilityRole role = parent->roleValue();
399         if (role == ListRole || role == ListBoxRole)
400             return parent->wrapper();
401     }
402     
403     return nil;
404 }
405
406 - (AccessibilityObjectWrapper*)_accessibilityLandmarkAncestor
407 {
408     for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
409         if ([self _accessibilityIsLandmarkRole:parent->roleValue()])
410             return parent->wrapper();
411     }
412
413     return nil;
414 }
415
416 - (AccessibilityObjectWrapper*)_accessibilityTableAncestor
417 {
418     for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
419         if (parent->roleValue() == TableRole)
420             return parent->wrapper();   
421     }
422     
423     return nil;
424 }
425
426 - (uint64_t)_accessibilityTraitsFromAncestors
427 {
428     uint64_t traits = 0;
429     AccessibilityRole role = m_object->roleValue();
430     
431     // Trait information also needs to be gathered from the parents above the object.
432     // The parentObject is needed instead of the unignoredParentObject, because a table might be ignored, but information still needs to be gathered from it.    
433     for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
434         AccessibilityRole parentRole = parent->roleValue();
435         if (parentRole == WebAreaRole)
436             break;
437         
438         switch (parentRole) {
439             case LinkRole:
440             case WebCoreLinkRole:
441                 traits |= [self _axLinkTrait];
442                 if (parent->isVisited())
443                     traits |= [self _axVisitedTrait];
444                 break;
445             case HeadingRole:
446             {
447                 traits |= [self _axHeaderTrait];
448                 // If this object has the header trait, we should set the value
449                 // to the heading level. If it was a static text element, we need to store
450                 // the value as the label, because the heading level needs to the value.
451                 AccessibilityObjectWrapper* wrapper = parent->wrapper();
452                 if (role == StaticTextRole) 
453                     [self setAccessibilityLabel:m_object->stringValue()];                
454                 [self setAccessibilityValue:[wrapper accessibilityValue]];
455                 break;
456             }
457             case ListBoxRole:
458             case ListRole:
459                 traits |= [self _axContainedByListTrait];
460                 break;
461             case TableRole:
462                 traits |= [self _axContainedByTableTrait];
463                 break;
464             default:
465                 if ([self _accessibilityIsLandmarkRole:parentRole])
466                     traits |= [self _axContainedByLandmarkTrait];
467                 break;
468         }
469     }
470     
471     return traits;
472 }
473
474 - (uint64_t)accessibilityTraits
475 {
476     if (![self _prepareAccessibilityCall])
477         return 0;
478     
479     AccessibilityRole role = m_object->roleValue();
480     uint64_t traits = [self _axWebContentTrait];
481     switch (role) {
482         case LinkRole:
483         case WebCoreLinkRole:
484             traits |= [self _axLinkTrait];
485             if (m_object->isVisited())
486                 traits |= [self _axVisitedTrait];
487             break;
488         // TextFieldRole is intended to fall through to TextAreaRole, in order to pick up the text entry and text cursor traits.
489         case TextFieldRole:
490             if (m_object->isPasswordField())
491                 traits |= [self _axSecureTextFieldTrait];
492         case TextAreaRole:
493             traits |= [self _axTextEntryTrait];            
494             if (m_object->isFocused())
495                 traits |= ([self _axHasTextCursorTrait] | [self _axTextOperationsAvailableTrait]);
496             break;
497         case ImageRole:
498             traits |= [self _axImageTrait];
499             break;
500         case TabRole:
501             traits |= [self _axTabButtonTrait];
502             break;
503         case ButtonRole:
504             traits |= [self _axButtonTrait];
505             if (m_object->isPressed())
506                 traits |= [self _axToggleTrait];
507             break;
508         case PopUpButtonRole:
509             traits |= [self _axPopupButtonTrait];
510             break;
511         case RadioButtonRole:
512             traits |= [self _axRadioButtonTrait] | [self _axToggleTrait];
513             break;
514         case CheckBoxRole:
515             traits |= ([self _axButtonTrait] | [self _axToggleTrait]);
516             break;
517         case HeadingRole:
518             traits |= [self _axHeaderTrait];
519             break;
520         case StaticTextRole:
521             traits |= [self _axStaticTextTrait];
522             break;
523         case SliderRole:
524             traits |= [self _axAdjustableTrait];
525             break;
526         case MenuButtonRole:
527         case MenuItemRole:
528             traits |= [self _axMenuItemTrait];
529             break;
530         default:
531             break;
532     }
533
534     if (m_object->isSelected())
535         traits |= [self _axSelectedTrait];
536
537     if (!m_object->isEnabled())
538         traits |= [self _axNotEnabledTrait];
539     
540     if (m_accessibilityTraitsFromAncestor == ULLONG_MAX)
541         m_accessibilityTraitsFromAncestor = [self _accessibilityTraitsFromAncestors];
542     
543     traits |= m_accessibilityTraitsFromAncestor;
544
545     return traits;
546 }
547
548 - (BOOL)isSVGGroupElement
549 {
550     // If an SVG group element has a title, it should be an accessible element on iOS.
551 #if ENABLE(SVG)
552     Node* node = m_object->node();
553     if (node && node->hasTagName(SVGNames::gTag) && [[self accessibilityLabel] length] > 0)
554         return YES;
555 #endif
556     
557     return NO;
558 }
559
560 - (BOOL)determineIsAccessibilityElement
561 {
562     if (!m_object)
563         return false;
564     
565     // Honor when something explicitly makes this an element (super will contain that logic) 
566     if ([super isAccessibilityElement])
567         return YES;
568     
569     m_object->updateBackingStore();
570     
571     switch (m_object->roleValue()) {
572         case TextFieldRole:
573         case TextAreaRole:
574         case ButtonRole:
575         case PopUpButtonRole:
576         case CheckBoxRole:
577         case RadioButtonRole:
578         case SliderRole:
579         case MenuButtonRole:
580         case ValueIndicatorRole:
581         case ImageRole:
582         case MenuItemRole:
583         case IncrementorRole:
584         case ComboBoxRole:
585         case DisclosureTriangleRole:
586         case ImageMapRole:
587         case ListMarkerRole:
588         case ListBoxOptionRole:
589         case TabRole:
590         case DocumentMathRole:
591             return true;
592         case StaticTextRole:
593         {
594             // Many text elements only contain a space.
595             if (![[[self accessibilityLabel] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length])
596                 return false;
597
598             // Text elements that are just pieces of links or headers should not be exposed.
599             if ([AccessibilityUnignoredAncestor([self accessibilityContainer]) containsUnnaturallySegmentedChildren])
600                 return false;
601             return true;
602         }
603             
604         // Don't expose headers as elements; instead expose their children as elements, with the header trait (unless they have no children)
605         case HeadingRole:
606             if (![self accessibilityElementCount])
607                 return true;
608             return false;
609             
610         // Links can sometimes be elements (when they only contain static text or don't contain anything).
611         // They should not be elements when containing text and other types.
612         case WebCoreLinkRole:
613         case LinkRole:
614             if ([self containsUnnaturallySegmentedChildren] || ![self accessibilityElementCount])
615                 return true;
616             return false;
617         case GroupRole:
618             if ([self isSVGGroupElement])
619                 return true;
620         // All other elements are ignored on the iphone.
621         default:
622         case UnknownRole:
623         case TabGroupRole:
624         case ScrollAreaRole:
625         case TableRole:
626         case ApplicationRole:
627         case RadioGroupRole:
628         case ListRole:
629         case ListBoxRole:
630         case ScrollBarRole:
631         case MenuBarRole:
632         case MenuRole:
633         case ColumnRole:
634         case RowRole:
635         case ToolbarRole:
636         case BusyIndicatorRole:
637         case ProgressIndicatorRole:
638         case WindowRole:
639         case DrawerRole:
640         case SystemWideRole:
641         case OutlineRole:
642         case BrowserRole:
643         case SplitGroupRole:
644         case SplitterRole:
645         case ColorWellRole:
646         case GrowAreaRole:
647         case SheetRole:
648         case HelpTagRole:
649         case MatteRole:
650         case RulerRole:
651         case RulerMarkerRole:
652         case GridRole:
653         case WebAreaRole:
654             return false;
655     }
656 }
657
658 - (BOOL)isAccessibilityElement
659 {
660     if (![self _prepareAccessibilityCall])
661         return NO;
662     
663     if (m_isAccessibilityElement == -1)
664         m_isAccessibilityElement = [self determineIsAccessibilityElement];
665     
666     return m_isAccessibilityElement;
667 }
668
669 - (BOOL)stringValueShouldBeUsedInLabel
670 {
671     if (m_object->isTextControl())
672         return NO;
673     if (m_object->roleValue() == PopUpButtonRole)
674         return NO;
675     if (m_object->isFileUploadButton())
676         return NO;
677
678     return YES;
679 }
680
681 - (BOOL)fileUploadButtonReturnsValueInTitle
682 {
683     return NO;
684 }
685
686 - (NSString *)accessibilityLabel
687 {
688     if (![self _prepareAccessibilityCall])
689         return nil;
690
691     // check if the label was overriden
692     NSString *label = [super accessibilityLabel];
693     if (label)
694         return label;
695
696     // iOS doesn't distinguish between a title and description field,
697     // so concatentation will yield the best result.
698     NSString *axTitle = [self accessibilityTitle];
699     NSString *axDescription = [self accessibilityDescription];
700     NSUInteger axTitleLength = [axTitle length];
701     NSUInteger axDescriptionLength = [axDescription length];
702     
703     if (axTitleLength && axDescriptionLength)
704         return [axTitle stringByAppendingFormat:@", %@", axDescription];
705     else if (axTitleLength)
706         return axTitle;
707     else if (axDescriptionLength)
708         return axDescription;
709     else if ([self stringValueShouldBeUsedInLabel])
710         return m_object->stringValue();
711     
712     return nil;
713 }
714
715 - (AccessibilityTableCell*)tableCellParent
716 {
717     // Find if this element is in a table cell.
718     AccessibilityObject* cell = 0;
719     for (cell = m_object; cell && !cell->isTableCell(); cell = cell->parentObject()) 
720     { }
721     
722     if (!cell)
723         return 0;
724
725     return static_cast<AccessibilityTableCell*>(cell);
726 }
727
728 - (AccessibilityTable*)tableParent
729 {
730     // Find if the parent table for the table cell.
731     AccessibilityObject* parentTable = 0;
732     for (parentTable = m_object; parentTable && !parentTable->isDataTable(); parentTable = parentTable->parentObject()) 
733     { }
734     
735     if (!parentTable)
736         return 0;
737     
738     return static_cast<AccessibilityTable*>(parentTable);
739 }
740
741 - (id)accessibilityTitleElement
742 {
743     if (![self _prepareAccessibilityCall])
744         return nil;
745
746     AccessibilityObject* titleElement = m_object->titleUIElement();
747     if (titleElement)
748         return titleElement->wrapper();
749
750     return nil;
751 }
752
753 // Meant to return row or column headers (or other things as the future permits).
754 - (NSArray *)accessibilityHeaderElements
755 {
756     if (![self _prepareAccessibilityCall])
757         return nil;
758
759     AccessibilityTableCell* tableCell = [self tableCellParent];
760     if (!tableCell)
761         return nil;
762     
763     AccessibilityTable* table = [self tableParent];
764     if (!table)
765         return nil;
766     
767     // Get the row and column range, so we can use them to find the headers.
768     pair<unsigned, unsigned> rowRange;
769     pair<unsigned, unsigned> columnRange;
770     tableCell->rowIndexRange(rowRange);
771     tableCell->columnIndexRange(columnRange);
772     
773     AccessibilityObject::AccessibilityChildrenVector rowHeaders;
774     AccessibilityObject::AccessibilityChildrenVector columnHeaders;
775     table->rowHeaders(rowHeaders);
776     table->columnHeaders(columnHeaders);
777     
778     NSMutableArray *headers = [NSMutableArray array];
779     
780     unsigned columnRangeIndex = static_cast<unsigned>(columnRange.first);
781     if (columnRangeIndex < columnHeaders.size()) {
782         RefPtr<AccessibilityObject> columnHeader = columnHeaders[columnRange.first];
783         AccessibilityObjectWrapper* wrapper = columnHeader->wrapper();
784         if (wrapper)
785             [headers addObject:wrapper];
786     }
787
788     unsigned rowRangeIndex = static_cast<unsigned>(rowRange.first);
789     if (rowRangeIndex < rowHeaders.size()) {
790         RefPtr<AccessibilityObject> rowHeader = rowHeaders[rowRange.first];
791         AccessibilityObjectWrapper* wrapper = rowHeader->wrapper();
792         if (wrapper)
793             [headers addObject:wrapper];
794     }
795         
796     return headers;
797 }
798
799 - (id)accessibilityElementForRow:(NSInteger)row andColumn:(NSInteger)column
800 {
801     if (![self _prepareAccessibilityCall])
802         return nil;
803
804     AccessibilityTable* table = [self tableParent];
805     if (!table)
806         return nil;
807
808     AccessibilityTableCell* cell = table->cellForColumnAndRow(column, row);
809     if (!cell)
810         return nil;
811     return cell->wrapper();
812 }
813
814 - (NSRange)accessibilityRowRange
815 {
816     if (![self _prepareAccessibilityCall])
817         return NSMakeRange(NSNotFound, 0);
818
819     if (m_object->isRadioButton()) {
820         AccessibilityObject::AccessibilityChildrenVector radioButtonSiblings;
821         m_object->linkedUIElements(radioButtonSiblings);
822         if (radioButtonSiblings.size() <= 1)
823             return NSMakeRange(NSNotFound, 0);
824         
825         return NSMakeRange(radioButtonSiblings.find(m_object), radioButtonSiblings.size());
826     }
827     
828     AccessibilityTableCell* tableCell = [self tableCellParent];
829     if (!tableCell)
830         return NSMakeRange(NSNotFound, 0);
831     
832     pair<unsigned, unsigned> rowRange;
833     tableCell->rowIndexRange(rowRange);
834     return NSMakeRange(rowRange.first, rowRange.second);
835 }
836
837 - (NSRange)accessibilityColumnRange
838 {
839     if (![self _prepareAccessibilityCall])
840         return NSMakeRange(NSNotFound, 0);
841
842     AccessibilityTableCell* tableCell = [self tableCellParent];
843     if (!tableCell)
844         return NSMakeRange(NSNotFound, 0);
845     
846     pair<unsigned, unsigned> columnRange;
847     tableCell->columnIndexRange(columnRange);
848     return NSMakeRange(columnRange.first, columnRange.second);
849 }
850
851 - (NSString *)accessibilityPlaceholderValue
852 {
853     if (![self _prepareAccessibilityCall])
854         return nil;
855
856     return m_object->placeholderValue();
857 }
858
859 - (NSString *)accessibilityValue
860 {
861     if (![self _prepareAccessibilityCall])
862         return nil;
863     
864     // check if the value was overriden
865     NSString *value = [super accessibilityValue];
866     if (value)
867         return value;
868     
869     if (m_object->isCheckboxOrRadio()) {
870         switch (m_object->checkboxOrRadioValue()) {
871         case ButtonStateOff:
872             return [NSString stringWithFormat:@"%d", 0];
873         case ButtonStateOn:
874             return [NSString stringWithFormat:@"%d", 1];
875         case ButtonStateMixed:
876             return [NSString stringWithFormat:@"%d", 2];
877         }
878         ASSERT_NOT_REACHED();
879         return [NSString stringWithFormat:@"%d", 0];
880     }
881     
882     if (m_object->isButton() && m_object->isPressed())
883         return [NSString stringWithFormat:@"%d", 1];
884
885     // rdar://8131388 WebKit should expose the same info as UIKit for its password fields.
886     if (m_object->isPasswordField()) {
887         int passwordLength = m_object->accessibilityPasswordFieldLength();
888         NSMutableString* string = [NSMutableString string];
889         for (int k = 0; k < passwordLength; ++k)
890             [string appendString:@"•"];
891         return string;
892     }
893     
894     // A text control should return its text data as the axValue (per iPhone AX API).
895     if (![self stringValueShouldBeUsedInLabel])
896         return m_object->stringValue();
897     
898     if (m_object->isProgressIndicator() || m_object->isSlider()) {
899         // Prefer a valueDescription if provided by the author (through aria-valuetext).
900         String valueDescription = m_object->valueDescription();
901         if (!valueDescription.isEmpty())
902             return valueDescription;
903
904         return [NSString stringWithFormat:@"%.2f", m_object->valueForRange()];
905     }
906
907     if (m_object->isHeading())
908         return [NSString stringWithFormat:@"%d", m_object->headingLevel()];
909     
910     return nil;
911 }
912
913 - (BOOL)accessibilityIsComboBox
914 {
915     if (![self _prepareAccessibilityCall])
916         return NO;
917
918     return m_object->roleValue() == ComboBoxRole;
919 }
920
921 - (NSString *)accessibilityHint
922 {
923     if (![self _prepareAccessibilityCall])
924         return nil;
925
926     return [self accessibilityHelpText];
927 }
928
929 - (NSURL *)accessibilityURL
930 {
931     if (![self _prepareAccessibilityCall])
932         return nil;
933     
934     KURL url = m_object->url();
935     if (url.isNull())
936         return nil;
937     return (NSURL*)url;
938 }
939
940 - (CGRect)_convertIntRectToScreenCoordinates:(IntRect)rect
941 {
942     if (!m_object)
943         return CGRectZero;
944     
945     CGSize size = CGSizeMake(rect.size().width(), rect.size().height());
946     CGPoint point = CGPointMake(rect.x(), rect.y());
947     
948     CGRect frame = CGRectMake(point.x, point.y, size.width, size.height);
949     
950     FrameView* frameView = m_object->documentFrameView();
951     if (frameView) {
952         WAKView* view = frameView->documentView();
953         frame = [view convertRect:frame toView:nil];
954     }
955     
956     // we need the web document view to give us our final screen coordinates
957     // because that can take account of the scroller
958     id webDocument = [self _accessibilityWebDocumentView];
959     if (webDocument)
960         frame = [webDocument convertRect:frame toView:nil];
961     
962     return frame;
963 }
964
965 // Used by UIKit accessibility bundle to help determine distance during a hit-test.
966 - (CGRect)accessibilityElementRect
967 {
968     if (![self _prepareAccessibilityCall])
969         return CGRectZero;
970     
971     LayoutRect rect = m_object->elementRect();
972     return CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
973 }
974
975 // The "center point" is where VoiceOver will "press" an object. This may not be the actual
976 // center of the accessibilityFrame
977 - (CGPoint)accessibilityActivationPoint
978 {
979     if (![self _prepareAccessibilityCall])
980         return CGPointZero;
981     
982     IntRect rect = pixelSnappedIntRect(m_object->boundingBoxRect());
983     CGRect cgRect = [self _convertIntRectToScreenCoordinates:rect];
984     return CGPointMake(CGRectGetMidX(cgRect), CGRectGetMidY(cgRect));
985 }
986
987 - (CGRect)accessibilityFrame
988 {
989     if (![self _prepareAccessibilityCall])
990         return CGRectZero;
991     
992     IntRect rect = pixelSnappedIntRect(m_object->elementRect());
993     return [self _convertIntRectToScreenCoordinates:rect];
994 }
995
996 // Checks whether a link contains only static text and images (and has been divided unnaturally by <spans> and other nefarious mechanisms).
997 - (BOOL)containsUnnaturallySegmentedChildren
998 {
999     if (!m_object)
1000         return NO;
1001     
1002     AccessibilityRole role = m_object->roleValue();
1003     if (role != LinkRole && role != WebCoreLinkRole)
1004         return NO;
1005     
1006     AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
1007     unsigned childrenSize = children.size();
1008
1009     // If there's only one child, then it doesn't have segmented children. 
1010     if (childrenSize == 1)
1011         return NO;
1012     
1013     for (unsigned i = 0; i < childrenSize; ++i) {
1014         AccessibilityRole role = children[i]->roleValue();
1015         if (role != StaticTextRole && role != ImageRole && role != GroupRole)
1016             return NO;
1017     }
1018     
1019     return YES;
1020 }
1021
1022 - (id)accessibilityContainer
1023 {
1024     if (![self _prepareAccessibilityCall])
1025         return nil;
1026     
1027     // As long as there's a parent wrapper, that's the correct chain to climb.
1028     AccessibilityObject* parent = m_object->parentObjectUnignored(); 
1029     if (parent)
1030         return parent->wrapper();
1031
1032     // The only object without a parent wrapper should be a scroll view.
1033     ASSERT(m_object->isAccessibilityScrollView());
1034     
1035     // Verify this is the top document. If not, we might need to go through the platform widget.
1036     FrameView* frameView = m_object->documentFrameView();
1037     Document* document = m_object->document();
1038     if (document && frameView && document != document->topDocument())
1039         return frameView->platformWidget();
1040     
1041     // The top scroll view's parent is the web document view.
1042     return [self _accessibilityWebDocumentView];
1043 }
1044
1045 - (id)accessibilityFocusedUIElement
1046 {
1047     if (![self _prepareAccessibilityCall])
1048         return nil;
1049     
1050     AccessibilityObject* focusedObj = m_object->focusedUIElement();
1051     
1052     if (!focusedObj)
1053         return nil;
1054     
1055     return focusedObj->wrapper();
1056 }
1057
1058 - (id)_accessibilityWebDocumentView
1059 {
1060     if (![self _prepareAccessibilityCall])
1061         return nil;
1062
1063     // This method performs the crucial task of connecting to the UIWebDocumentView.
1064     // This is needed to correctly calculate the screen position of the AX object.
1065     static Class webViewClass = nil;
1066     if (!webViewClass)
1067         webViewClass = NSClassFromString(@"WebView");
1068
1069     if (!webViewClass)
1070         return nil;
1071     
1072     FrameView* frameView = m_object->documentFrameView();
1073
1074     if (!frameView)
1075         return nil;
1076     
1077     // If this is the top level frame, the UIWebDocumentView should be returned.
1078     id parentView = frameView->documentView();
1079     while (parentView && ![parentView isKindOfClass:webViewClass])
1080         parentView = [parentView superview];
1081     
1082     // The parentView should have an accessibilityContainer, if the UIKit accessibility bundle was loaded. 
1083     // The exception is DRT, which tests accessibility without the entire system turning accessibility on. Hence,
1084     // this check should be valid for everything except DRT.
1085     ASSERT([parentView accessibilityContainer] || applicationIsDumpRenderTree());
1086     
1087     return [parentView accessibilityContainer];
1088 }
1089
1090 - (NSArray *)_accessibilityNextElementsWithCount:(UInt32)count
1091 {    
1092     if (![self _prepareAccessibilityCall])
1093         return nil;
1094     
1095     return [[self _accessibilityWebDocumentView] _accessibilityNextElementsWithCount:count];
1096 }
1097
1098 - (NSArray *)_accessibilityPreviousElementsWithCount:(UInt32)count
1099 {
1100     if (![self _prepareAccessibilityCall])
1101         return nil;
1102     
1103     return [[self _accessibilityWebDocumentView] _accessibilityPreviousElementsWithCount:count];
1104 }
1105
1106 - (BOOL)accessibilityRequired
1107 {
1108     if (![self _prepareAccessibilityCall])
1109         return NO;
1110
1111     return m_object->isRequired();
1112 }
1113
1114 - (NSArray *)accessibilityFlowToElements
1115 {
1116     if (![self _prepareAccessibilityCall])
1117         return nil;
1118     
1119     AccessibilityObject::AccessibilityChildrenVector children;
1120     m_object->ariaFlowToElements(children);
1121     
1122     unsigned length = children.size();
1123     NSMutableArray* array = [NSMutableArray arrayWithCapacity:length];
1124     for (unsigned i = 0; i < length; ++i) {
1125         AccessibilityObjectWrapper* wrapper = children[i]->wrapper();
1126         ASSERT(wrapper);
1127         if (!wrapper)
1128             continue;
1129
1130         if (children[i]->isAttachment() && [wrapper attachmentView])
1131             [array addObject:[wrapper attachmentView]];
1132         else
1133             [array addObject:wrapper];
1134     }
1135     return array;
1136 }
1137
1138 - (id)accessibilityLinkedElement
1139 {
1140     if (![self _prepareAccessibilityCall])
1141         return nil;
1142     
1143     // If this static text inside of a link, it should use its parent's linked element.
1144     AccessibilityObject* element = m_object;
1145     if (m_object->roleValue() == StaticTextRole && m_object->parentObjectUnignored()->isLink())
1146         element = m_object->parentObjectUnignored();
1147     
1148     AccessibilityObject::AccessibilityChildrenVector children;
1149     element->linkedUIElements(children);
1150     if (children.size() == 0)
1151         return nil;
1152     
1153     return children[0]->wrapper();
1154 }
1155
1156
1157 - (BOOL)isAttachment
1158 {
1159     if (!m_object)
1160         return NO;
1161     
1162     return m_object->isAttachment();
1163 }
1164
1165 - (void)_accessibilityActivate
1166 {
1167     if (![self _prepareAccessibilityCall])
1168         return;
1169
1170     m_object->press();
1171 }
1172
1173 - (id)attachmentView
1174 {
1175     if (![self _prepareAccessibilityCall])
1176         return nil;
1177
1178     ASSERT([self isAttachment]);
1179     Widget* widget = m_object->widgetForAttachmentView();
1180     if (!widget)
1181         return nil;
1182     return widget->platformWidget();    
1183 }
1184
1185 static RenderObject* rendererForView(WAKView* view)
1186 {
1187     if (![view conformsToProtocol:@protocol(WebCoreFrameView)])
1188         return 0;
1189     
1190     WAKView<WebCoreFrameView>* frameView = (WAKView<WebCoreFrameView>*)view;
1191     Frame* frame = [frameView _web_frame];
1192     if (!frame)
1193         return 0;
1194     
1195     Node* node = frame->document()->ownerElement();
1196     if (!node)
1197         return 0;
1198     
1199     return node->renderer();
1200 }
1201
1202 - (id)_accessibilityParentForSubview:(id)subview
1203 {   
1204     RenderObject* renderer = rendererForView(subview);
1205     if (!renderer)
1206         return nil;
1207     
1208     AccessibilityObject* obj = renderer->document()->axObjectCache()->getOrCreate(renderer);
1209     if (obj)
1210         return obj->parentObjectUnignored()->wrapper();
1211     return nil;
1212 }
1213
1214 - (void)postFocusChangeNotification
1215 {
1216     // The UIKit accessibility wrapper will override and post appropriate notification.
1217 }
1218
1219 - (void)postSelectedTextChangeNotification
1220 {
1221     // The UIKit accessibility wrapper will override and post appropriate notification.    
1222 }
1223
1224 - (void)postLayoutChangeNotification
1225 {
1226     // The UIKit accessibility wrapper will override and post appropriate notification.        
1227 }
1228
1229 - (void)postLiveRegionChangeNotification
1230 {
1231     // The UIKit accessibility wrapper will override and post appropriate notification.    
1232 }
1233
1234 - (void)postLoadCompleteNotification
1235 {
1236     // The UIKit accessibility wrapper will override and post appropriate notification.    
1237 }
1238
1239 - (void)postChildrenChangedNotification
1240 {
1241     // The UIKit accessibility wrapper will override and post appropriate notification.    
1242 }
1243
1244 - (void)postInvalidStatusChangedNotification
1245 {
1246     // The UIKit accessibility wrapper will override and post appropriate notification.        
1247 }
1248
1249 - (void)accessibilityElementDidBecomeFocused
1250 {
1251     if (![self _prepareAccessibilityCall])
1252         return;
1253     
1254     // The focused VoiceOver element might be the text inside a link.
1255     // In those cases we should focus on the link itself.
1256     for (AccessibilityObject* object = m_object; object != nil; object = object->parentObject()) {
1257         if (object->roleValue() == WebAreaRole)
1258             break;
1259
1260         if (object->canSetFocusAttribute()) {
1261             object->setFocused(true);
1262             break;
1263         }
1264     }
1265 }
1266
1267 - (void)accessibilityModifySelection:(TextGranularity)granularity increase:(BOOL)increase
1268 {
1269     if (![self _prepareAccessibilityCall])
1270         return;
1271     
1272     FrameSelection* frameSelection = m_object->document()->frame()->selection();
1273     VisibleSelection selection = m_object->selection();
1274     VisiblePositionRange range = m_object->visiblePositionRange();
1275     
1276     // Before a selection with length exists, the cursor position needs to move to the right starting place.
1277     // That should be the beginning of this element (range.start). However, if the cursor is already within the 
1278     // range of this element (the cursor is represented by selection), then the cursor does not need to move.
1279     if (frameSelection->isNone() && (selection.visibleStart() < range.start || selection.visibleEnd() > range.end))
1280         frameSelection->moveTo(range.start, UserTriggered);
1281     
1282     frameSelection->modify(FrameSelection::AlterationExtend, (increase) ? DirectionRight : DirectionLeft, granularity, UserTriggered);
1283 }
1284
1285 - (void)accessibilityIncreaseSelection:(TextGranularity)granularity
1286 {
1287     [self accessibilityModifySelection:granularity increase:YES];
1288 }
1289
1290 - (void)accessibilityDecreaseSelection:(TextGranularity)granularity
1291 {
1292     [self accessibilityModifySelection:granularity increase:NO];
1293 }
1294
1295 - (void)accessibilityMoveSelectionToMarker:(WebAccessibilityTextMarker *)marker
1296 {
1297     if (![self _prepareAccessibilityCall])
1298         return;
1299     
1300     VisiblePosition visiblePosition = [marker visiblePosition];
1301     if (visiblePosition.isNull())
1302         return;
1303
1304     FrameSelection* frameSelection = m_object->document()->frame()->selection();
1305     frameSelection->moveTo(visiblePosition, UserTriggered);
1306 }
1307
1308 - (void)accessibilityIncrement
1309 {
1310     if (![self _prepareAccessibilityCall])
1311         return;
1312
1313     m_object->increment();
1314 }
1315
1316 - (void)accessibilityDecrement
1317 {
1318     if (![self _prepareAccessibilityCall])
1319         return;
1320
1321     m_object->decrement();
1322 }
1323
1324 #pragma mark Accessibility Text Marker Handlers
1325
1326 - (BOOL)_addAccessibilityObject:(AccessibilityObject*)axObject toTextMarkerArray:(NSMutableArray *)array
1327 {
1328     if (!axObject)
1329         return NO;
1330     
1331     AccessibilityObjectWrapper* wrapper = axObject->wrapper();
1332     if (!wrapper)
1333         return NO;
1334
1335     // Don't add the same object twice, but since this has already been added, we should return
1336     // YES because we want to inform that it's in the array
1337     if ([array containsObject:wrapper])
1338         return YES;
1339     
1340     // Explicity set that this is now an element (in case other logic tries to override).
1341     [wrapper setValue:[NSNumber numberWithBool:YES] forKey:@"isAccessibilityElement"];    
1342     [array addObject:wrapper];
1343     return YES;
1344 }
1345
1346 - (NSString *)stringForTextMarkers:(NSArray *)markers
1347 {
1348     if (![self _prepareAccessibilityCall])
1349         return nil;
1350     
1351     if ([markers count] != 2)
1352         return nil;
1353     
1354     WebAccessibilityTextMarker* startMarker = [markers objectAtIndex:0];
1355     WebAccessibilityTextMarker* endMarker = [markers objectAtIndex:1];
1356     if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
1357         return nil;
1358     
1359     // extract the start and end VisiblePosition
1360     VisiblePosition startVisiblePosition = [startMarker visiblePosition];
1361     if (startVisiblePosition.isNull())
1362         return nil;
1363     
1364     VisiblePosition endVisiblePosition = [endMarker visiblePosition];
1365     if (endVisiblePosition.isNull())
1366         return nil;
1367
1368     VisiblePositionRange visiblePosRange = VisiblePositionRange(startVisiblePosition, endVisiblePosition);
1369     return m_object->stringForVisiblePositionRange(visiblePosRange);
1370 }
1371
1372 static int blockquoteLevel(RenderObject* renderer)
1373 {
1374     if (!renderer)
1375         return 0;
1376     
1377     int result = 0;
1378     for (Node* node = renderer->node(); node; node = node->parentNode()) {
1379         if (node->hasTagName(blockquoteTag))
1380             result += 1;
1381     }
1382     
1383     return result;
1384 }
1385
1386 static void AXAttributeStringSetBlockquoteLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1387 {
1388     int quoteLevel = blockquoteLevel(renderer);
1389     
1390     if (quoteLevel)
1391         [attrString addAttribute:UIAccessibilityTokenBlockquoteLevel value:[NSNumber numberWithInt:quoteLevel] range:range];
1392     else
1393         [attrString removeAttribute:UIAccessibilityTokenBlockquoteLevel range:range];
1394 }
1395
1396 static void AXAttributeStringSetHeadingLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1397 {
1398     if (!renderer)
1399         return;
1400     
1401     AccessibilityObject* parentObject = renderer->document()->axObjectCache()->getOrCreate(renderer->parent());
1402     int parentHeadingLevel = parentObject->headingLevel();
1403     
1404     if (parentHeadingLevel)
1405         [attrString addAttribute:UIAccessibilityTokenHeadingLevel value:[NSNumber numberWithInt:parentHeadingLevel] range:range];
1406     else
1407         [attrString removeAttribute:UIAccessibilityTokenHeadingLevel range:range];
1408 }
1409
1410 static void AXAttributeStringSetFont(NSMutableAttributedString* attrString, GSFontRef font, NSRange range)
1411 {
1412     if (!font)
1413         return;
1414     
1415     const char* nameCStr = GSFontGetFullName(font);
1416     const char* familyCStr = GSFontGetFamilyName(font);
1417     NSNumber* size = [NSNumber numberWithFloat:GSFontGetSize(font)];
1418     NSNumber* bold = [NSNumber numberWithBool:GSFontIsBold(font)];
1419     GSFontTraitMask traits = GSFontGetTraits(font);
1420     if (nameCStr)
1421         [attrString addAttribute:UIAccessibilityTokenFontName value:[NSString stringWithUTF8String:nameCStr] range:range];
1422     if (familyCStr)
1423         [attrString addAttribute:UIAccessibilityTokenFontFamily value:[NSString stringWithUTF8String:familyCStr] range:range];
1424     if ([size boolValue])
1425         [attrString addAttribute:UIAccessibilityTokenFontSize value:size range:range];
1426     if ([bold boolValue] || (traits & GSBoldFontMask))
1427         [attrString addAttribute:UIAccessibilityTokenBold value:[NSNumber numberWithBool:YES] range:range];
1428     if (traits & GSItalicFontMask)
1429         [attrString addAttribute:UIAccessibilityTokenItalic value:[NSNumber numberWithBool:YES] range:range];
1430
1431 }
1432
1433 static void AXAttributeStringSetNumber(NSMutableAttributedString* attrString, NSString* attribute, NSNumber* number, NSRange range)
1434 {
1435     if (number)
1436         [attrString addAttribute:attribute value:number range:range];
1437     else
1438         [attrString removeAttribute:attribute range:range];
1439 }
1440
1441 static void AXAttributeStringSetStyle(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1442 {
1443     RenderStyle* style = renderer->style();
1444     
1445     // set basic font info
1446     AXAttributeStringSetFont(attrString, style->font().primaryFont()->getGSFont(), range);
1447                 
1448     int decor = style->textDecorationsInEffect();
1449     if ((decor & (UNDERLINE | LINE_THROUGH)) != 0) {
1450         // find colors using quirk mode approach (strict mode would use current
1451         // color for all but the root line box, which would use getTextDecorationColors)
1452         Color underline, overline, linethrough;
1453         renderer->getTextDecorationColors(decor, underline, overline, linethrough);
1454         
1455         if (decor & UNDERLINE)
1456             AXAttributeStringSetNumber(attrString, UIAccessibilityTokenUnderline, [NSNumber numberWithBool:YES], range);
1457     }
1458 }
1459
1460 static void AXAttributedStringAppendText(NSMutableAttributedString* attrString, Node* node, const UChar* chars, int length)
1461 {
1462     // skip invisible text
1463     if (!node->renderer())
1464         return;
1465     
1466     // easier to calculate the range before appending the string
1467     NSRange attrStringRange = NSMakeRange([attrString length], length);
1468     
1469     // append the string from this node
1470     [[attrString mutableString] appendString:[NSString stringWithCharacters:chars length:length]];
1471     
1472     // set new attributes
1473     AXAttributeStringSetStyle(attrString, node->renderer(), attrStringRange);
1474     AXAttributeStringSetHeadingLevel(attrString, node->renderer(), attrStringRange);
1475     AXAttributeStringSetBlockquoteLevel(attrString, node->renderer(), attrStringRange);    
1476 }
1477
1478
1479 // This method is intended to return an array of strings and accessibility elements that 
1480 // represent the objects on one line of rendered web content. The array of markers sent
1481 // in should be ordered and contain only a start and end marker.
1482 - (NSArray *)arrayOfTextForTextMarkers:(NSArray *)markers attributed:(BOOL)attributed
1483 {
1484     if (![self _prepareAccessibilityCall])
1485         return nil;
1486
1487     if ([markers count] != 2)
1488         return nil;
1489     
1490     WebAccessibilityTextMarker* startMarker = [markers objectAtIndex:0];
1491     WebAccessibilityTextMarker* endMarker = [markers objectAtIndex:1];
1492     if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
1493         return nil;
1494     
1495     // extract the start and end VisiblePosition
1496     VisiblePosition startVisiblePosition = [startMarker visiblePosition];
1497     if (startVisiblePosition.isNull())
1498         return nil;
1499     
1500     VisiblePosition endVisiblePosition = [endMarker visiblePosition];
1501     if (endVisiblePosition.isNull())
1502         return nil;
1503     
1504     // iterate over the range to build the AX attributed string
1505     NSMutableArray* array = [[NSMutableArray alloc] init];
1506     TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get());
1507     for (; !it.atEnd(); it.advance()) {
1508         // locate the node and starting offset for this range
1509         int exception = 0;
1510         Node* node = it.range()->startContainer(exception);
1511         ASSERT(node == it.range()->endContainer(exception));
1512         int offset = it.range()->startOffset(exception);
1513         
1514         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
1515         if (it.length() != 0) {
1516             if (!attributed) {
1517                 // First check if this is represented by a link.
1518                 AccessibilityObject* linkObject = AccessibilityObject::anchorElementForNode(node);
1519                 if ([self _addAccessibilityObject:linkObject toTextMarkerArray:array])
1520                     continue;
1521                 
1522                 // Next check if this region is represented by a heading.
1523                 AccessibilityObject* headingObject = AccessibilityObject::headingElementForNode(node);
1524                 if ([self _addAccessibilityObject:headingObject toTextMarkerArray:array])
1525                     continue;
1526                 
1527                 String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition())); 
1528                 
1529                 if (!listMarkerText.isEmpty()) 
1530                     [array addObject:[NSString stringWithCharacters:listMarkerText.characters() length:listMarkerText.length()]];
1531                 // There was not an element representation, so just return the text.
1532                 [array addObject:[NSString stringWithCharacters:it.characters() length:it.length()]];
1533             }
1534             else
1535             {
1536                 String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition())); 
1537
1538                 if (!listMarkerText.isEmpty()) {
1539                     NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init];
1540                     AXAttributedStringAppendText(attrString, node, listMarkerText.characters(), listMarkerText.length());
1541                     [array addObject:attrString];
1542                     [attrString release];
1543                 }
1544                 
1545                 NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
1546                 AXAttributedStringAppendText(attrString, node, it.characters(), it.length());
1547                 [array addObject:attrString];
1548                 [attrString release];
1549             }
1550         } else {
1551             Node* replacedNode = node->childNode(offset);
1552             if (replacedNode) {
1553                 AccessibilityObject* obj = m_object->axObjectCache()->getOrCreate(replacedNode->renderer());
1554                 if (obj && !obj->accessibilityIsIgnored())
1555                     [self _addAccessibilityObject:obj toTextMarkerArray:array];
1556             }
1557         }
1558     }
1559     
1560     return [array autorelease];
1561 }
1562
1563 - (NSRange)_convertToNSRange:(Range *)range
1564 {
1565     if (!range || !range->startContainer())
1566         return NSMakeRange(NSNotFound, 0);
1567     
1568     Document* document = m_object->document();
1569     FrameSelection* frameSelection = document->frame()->selection();
1570     
1571     Element* selectionRoot = frameSelection->rootEditableElement();
1572     Element* scope = selectionRoot ? selectionRoot : document->documentElement();
1573     
1574     // Mouse events may cause TSM to attempt to create an NSRange for a portion of the view
1575     // that is not inside the current editable region.  These checks ensure we don't produce
1576     // potentially invalid data when responding to such requests.
1577     if (range->startContainer() != scope && !range->startContainer()->isDescendantOf(scope))
1578         return NSMakeRange(NSNotFound, 0);
1579     if (range->endContainer() != scope && !range->endContainer()->isDescendantOf(scope))
1580         return NSMakeRange(NSNotFound, 0);
1581     
1582     RefPtr<Range> testRange = Range::create(scope->document(), scope, 0, range->startContainer(), range->startOffset());
1583     ASSERT(testRange->startContainer() == scope);
1584     int startPosition = TextIterator::rangeLength(testRange.get());
1585     
1586     ExceptionCode ec;
1587     testRange->setEnd(range->endContainer(), range->endOffset(), ec);
1588     ASSERT(testRange->startContainer() == scope);
1589     int endPosition = TextIterator::rangeLength(testRange.get());
1590     return NSMakeRange(startPosition, endPosition - startPosition);
1591 }
1592
1593 - (PassRefPtr<Range>)_convertToDOMRange:(NSRange)nsrange
1594 {
1595     if (nsrange.location > INT_MAX)
1596         return 0;
1597     if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
1598         nsrange.length = INT_MAX - nsrange.location;
1599         
1600     // our critical assumption is that we are only called by input methods that
1601     // concentrate on a given area containing the selection
1602     // We have to do this because of text fields and textareas. The DOM for those is not
1603     // directly in the document DOM, so serialization is problematic. Our solution is
1604     // to use the root editable element of the selection start as the positional base.
1605     // That fits with AppKit's idea of an input context.
1606     Document* document = m_object->document();
1607     FrameSelection* frameSelection = document->frame()->selection();
1608     Element* selectionRoot = frameSelection->rootEditableElement();
1609     Element* scope = selectionRoot ? selectionRoot : document->documentElement();
1610     return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length);
1611 }
1612
1613 // This method is intended to take a text marker representing a VisiblePosition and convert it
1614 // into a normalized location within the document.
1615 - (NSInteger)positionForTextMarker:(WebAccessibilityTextMarker *)marker
1616 {
1617     if (![self _prepareAccessibilityCall])
1618         return NSNotFound;
1619
1620     if (!marker)
1621         return NSNotFound;    
1622
1623     VisibleSelection selection([marker visiblePosition]);
1624     RefPtr<Range> range = selection.toNormalizedRange();
1625     NSRange nsRange = [self _convertToNSRange:range.get()];
1626     return nsRange.location;
1627 }
1628
1629 - (NSArray *)textMarkerRange
1630 {
1631     if (![self _prepareAccessibilityCall])
1632         return nil;
1633     
1634     VisiblePositionRange range = m_object->visiblePositionRange();
1635     VisiblePosition startPosition = range.start;
1636     VisiblePosition endPosition = range.end;
1637     WebAccessibilityTextMarker* start = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:startPosition cache:m_object->axObjectCache()];
1638     WebAccessibilityTextMarker* end = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:endPosition cache:m_object->axObjectCache()];
1639     
1640     return [NSArray arrayWithObjects:start, end, nil];
1641 }
1642
1643 // A method to get the normalized text cursor range of an element. Used in DumpRenderTree.
1644 - (NSRange)elementTextRange
1645 {
1646     if (![self _prepareAccessibilityCall])
1647         return NSMakeRange(NSNotFound, 0);
1648
1649     NSArray *markers = [self textMarkerRange];
1650     if ([markers count] != 2)
1651         return NSMakeRange(NSNotFound, 0);
1652     
1653     WebAccessibilityTextMarker *startMarker = [markers objectAtIndex:0];
1654     WebAccessibilityTextMarker *endMarker = [markers objectAtIndex:1];
1655     
1656     NSInteger startPosition = [self positionForTextMarker:startMarker];
1657     NSInteger endPosition = [self positionForTextMarker:endMarker];
1658     
1659     return NSMakeRange(startPosition, endPosition - startPosition);
1660 }
1661
1662 - (AccessibilityObjectWrapper *)accessibilityObjectForTextMarker:(WebAccessibilityTextMarker *)marker
1663 {
1664     if (![self _prepareAccessibilityCall])
1665         return nil;
1666
1667     if (!marker)
1668         return nil;
1669     
1670     VisiblePosition visiblePosition = [marker visiblePosition];
1671     AccessibilityObject* obj = m_object->accessibilityObjectForPosition(visiblePosition);
1672     if (!obj)
1673         return nil;
1674     
1675     return AccessibilityUnignoredAncestor(obj->wrapper());
1676 }
1677
1678 - (NSArray *)textMarkerRangeForSelection
1679 {
1680     if (![self _prepareAccessibilityCall])
1681         return nil;
1682     
1683     VisibleSelection selection = m_object->selection();
1684     if (selection.isNone())
1685         return nil;
1686     VisiblePosition startPosition = selection.visibleStart();
1687     VisiblePosition endPosition = selection.visibleEnd();
1688
1689     WebAccessibilityTextMarker* startMarker = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:startPosition cache:m_object->axObjectCache()];
1690     WebAccessibilityTextMarker* endMarker = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:endPosition cache:m_object->axObjectCache()];
1691     if (!startMarker || !endMarker)
1692         return nil;
1693     
1694     return [NSArray arrayWithObjects:startMarker, endMarker, nil];
1695 }
1696
1697 - (WebAccessibilityTextMarker *)textMarkerForPosition:(NSInteger)position
1698 {
1699     if (![self _prepareAccessibilityCall])
1700         return nil;
1701
1702     PassRefPtr<Range> range = [self _convertToDOMRange:NSMakeRange(position, 0)];
1703     if (!range)
1704         return nil;
1705     
1706     VisibleSelection selection = VisibleSelection(range.get(), DOWNSTREAM);
1707
1708     VisiblePosition visiblePosition = selection.visibleStart();
1709     return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:visiblePosition cache:m_object->axObjectCache()];
1710 }
1711
1712 - (id)_stringForRange:(NSRange)range attributed:(BOOL)attributed
1713 {
1714     if (![self _prepareAccessibilityCall])
1715         return nil;
1716     
1717     WebAccessibilityTextMarker* startMarker = [self textMarkerForPosition:range.location];
1718     WebAccessibilityTextMarker* endMarker = [self textMarkerForPosition:NSMaxRange(range)];
1719     
1720     // Clients don't always know the exact range, rather than force them to compute it,
1721     // allow clients to overshoot and use the max text marker range.
1722     if (!startMarker || !endMarker) {
1723         NSArray *markers = [self textMarkerRange];
1724         if ([markers count] != 2)
1725             return nil;
1726         if (!startMarker)
1727             startMarker = [markers objectAtIndex:0];
1728         if (!endMarker)
1729             endMarker = [markers objectAtIndex:1];
1730     }
1731     
1732     NSArray* array = [self arrayOfTextForTextMarkers:[NSArray arrayWithObjects:startMarker, endMarker, nil] attributed:attributed];
1733     Class returnClass = attributed ? [NSMutableAttributedString class] : [NSMutableString class];
1734     id returnValue = [[[returnClass alloc] init] autorelease];
1735     
1736     NSInteger count = [array count];
1737     for (NSInteger k = 0; k < count; ++k) {
1738         id object = [array objectAtIndex:k];
1739
1740         if (![object isKindOfClass:returnClass])
1741             continue;
1742         
1743         if (attributed)
1744             [(NSMutableAttributedString *)returnValue appendAttributedString:object];
1745         else
1746             [(NSMutableString *)returnValue appendString:object];
1747     }
1748     return returnValue;
1749 }
1750
1751
1752 // A convenience method for getting the text of a NSRange. Currently used only by DRT.
1753 - (NSString *)stringForRange:(NSRange)range
1754 {
1755     return [self _stringForRange:range attributed:NO];
1756 }
1757
1758 - (NSAttributedString *)attributedStringForRange:(NSRange)range
1759 {
1760     return [self _stringForRange:range attributed:YES];
1761 }
1762
1763 // A convenience method for getting the accessibility objects of a NSRange. Currently used only by DRT.
1764 - (NSArray *)elementsForRange:(NSRange)range
1765 {
1766     if (![self _prepareAccessibilityCall])
1767         return nil;
1768     
1769     WebAccessibilityTextMarker* startMarker = [self textMarkerForPosition:range.location];
1770     WebAccessibilityTextMarker* endMarker = [self textMarkerForPosition:NSMaxRange(range)];
1771     if (!startMarker || !endMarker)
1772         return nil;
1773     
1774     NSArray* array = [self arrayOfTextForTextMarkers:[NSArray arrayWithObjects:startMarker, endMarker, nil] attributed:NO];
1775     NSMutableArray* elements = [NSMutableArray array];
1776     for (id element in array) {
1777         if (![element isKindOfClass:[AccessibilityObjectWrapper class]])
1778             continue;
1779         [elements addObject:element];
1780     }
1781     return elements;
1782 }
1783
1784 - (NSString *)selectionRangeString
1785 {
1786     NSArray *markers = [self textMarkerRangeForSelection];
1787     return [self stringForTextMarkers:markers];
1788 }
1789
1790 - (WebAccessibilityTextMarker *)selectedTextMarker
1791 {
1792     if (![self _prepareAccessibilityCall])
1793         return nil;
1794     
1795     VisibleSelection selection = m_object->selection();
1796     VisiblePosition position = selection.visibleStart();
1797     
1798     // if there's no selection, start at the top of the document
1799     if (position.isNull())
1800         position = startOfDocument(m_object->document());
1801     
1802     return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:position cache:m_object->axObjectCache()];
1803 }
1804
1805 // This method is intended to return the marker at the end of the line starting at
1806 // the marker that is passed into the method.
1807 - (WebAccessibilityTextMarker *)lineEndMarkerForMarker:(WebAccessibilityTextMarker *)marker
1808 {
1809     if (![self _prepareAccessibilityCall])
1810         return nil;
1811
1812     if (!marker)
1813         return nil;
1814     
1815     VisiblePosition start = [marker visiblePosition];
1816     VisiblePosition lineEnd = m_object->nextLineEndPosition(start);
1817     
1818     return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:lineEnd cache:m_object->axObjectCache()];
1819 }
1820
1821 // This method is intended to return the marker at the start of the line starting at
1822 // the marker that is passed into the method.
1823 - (WebAccessibilityTextMarker *)lineStartMarkerForMarker:(WebAccessibilityTextMarker *)marker
1824 {
1825     if (![self _prepareAccessibilityCall])
1826         return nil;
1827
1828     if (!marker)
1829         return nil;
1830     
1831     VisiblePosition start = [marker visiblePosition];
1832     VisiblePosition lineStart = m_object->previousLineStartPosition(start);
1833     
1834     return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:lineStart cache:m_object->axObjectCache()];
1835 }
1836
1837 - (WebAccessibilityTextMarker *)nextMarkerForMarker:(WebAccessibilityTextMarker *)marker
1838 {
1839     if (![self _prepareAccessibilityCall])
1840         return nil;
1841
1842     if (!marker)
1843         return nil;
1844     
1845     VisiblePosition start = [marker visiblePosition];
1846     VisiblePosition nextMarker = m_object->nextVisiblePosition(start);
1847     
1848     return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:nextMarker cache:m_object->axObjectCache()];
1849 }
1850
1851 // This method is intended to return the marker at the start of the line starting at
1852 // the marker that is passed into the method.
1853 - (WebAccessibilityTextMarker *)previousMarkerForMarker:(WebAccessibilityTextMarker *)marker
1854 {
1855     if (![self _prepareAccessibilityCall])
1856         return nil;
1857
1858     if (!marker)
1859         return nil;
1860     
1861     VisiblePosition start = [marker visiblePosition];
1862     VisiblePosition previousMarker = m_object->previousVisiblePosition(start);
1863     
1864     return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:previousMarker cache:m_object->axObjectCache()];
1865 }
1866
1867 // This method is intended to return the bounds of a text marker range in screen coordinates.
1868 - (CGRect)frameForTextMarkers:(NSArray *)array
1869 {
1870     if (![self _prepareAccessibilityCall])
1871         return CGRectZero;
1872
1873     if ([array count] != 2)
1874         return CGRectZero;
1875     
1876     WebAccessibilityTextMarker* startMarker = [array objectAtIndex:0];
1877     WebAccessibilityTextMarker* endMarker = [array objectAtIndex:1];
1878     if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
1879         return CGRectZero;
1880
1881     IntRect rect = m_object->boundsForVisiblePositionRange(VisiblePositionRange([startMarker visiblePosition], [endMarker visiblePosition]));
1882     return [self _convertIntRectToScreenCoordinates:rect];
1883 }
1884
1885 - (WebAccessibilityTextMarker *)textMarkerForPoint:(CGPoint)point
1886 {
1887     if (![self _prepareAccessibilityCall])
1888         return nil;
1889     
1890     VisiblePosition pos = m_object->visiblePositionForPoint(IntPoint(point));
1891     return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:pos cache:m_object->axObjectCache()];
1892 }
1893
1894 - (NSString *)accessibilityIdentifier
1895 {
1896     if (![self _prepareAccessibilityCall])
1897         return nil;
1898     
1899     return m_object->getAttribute(HTMLNames::idAttr);
1900 }
1901
1902 - (NSString *)accessibilitySpeechHint
1903 {
1904     if (![self _prepareAccessibilityCall])
1905         return nil;
1906
1907     switch (m_object->speakProperty()) {
1908     default:
1909     case SpeakNormal:
1910         return @"normal";
1911     case SpeakNone:
1912         return @"none";
1913     case SpeakSpellOut:
1914         return @"spell-out";
1915     case SpeakDigits:
1916         return @"digits";
1917     case SpeakLiteralPunctuation:
1918         return @"literal-punctuation";
1919     case SpeakNoPunctuation:
1920         return @"no-punctuation";
1921     }
1922     
1923     return nil;
1924 }
1925
1926 - (BOOL)accessibilityARIAIsBusy
1927 {
1928     if (![self _prepareAccessibilityCall])
1929         return nil;
1930
1931     return m_object->ariaLiveRegionBusy();
1932 }
1933
1934 - (NSString *)accessibilityARIALiveRegionStatus
1935 {
1936     if (![self _prepareAccessibilityCall])
1937         return nil;
1938
1939     return m_object->ariaLiveRegionStatus();
1940 }
1941
1942 - (NSString *)accessibilityARIARelevantStatus
1943 {
1944     if (![self _prepareAccessibilityCall])
1945         return nil;
1946     
1947     return m_object->ariaLiveRegionRelevant();
1948 }
1949
1950 - (BOOL)accessibilityARIALiveRegionIsAtomic
1951 {
1952     if (![self _prepareAccessibilityCall])
1953         return nil;
1954     
1955     return m_object->ariaLiveRegionAtomic();
1956 }
1957
1958 - (NSString *)accessibilityInvalidStatus
1959 {
1960     if (![self _prepareAccessibilityCall])
1961         return nil;
1962     
1963     return m_object->invalidStatus();
1964 }
1965
1966 - (WebAccessibilityObjectWrapper *)accessibilityMathRootIndexObject
1967 {
1968     return m_object->mathRootIndexObject() ? m_object->mathRootIndexObject()->wrapper() : 0;
1969 }
1970
1971 - (WebAccessibilityObjectWrapper *)accessibilityMathRadicandObject
1972 {
1973     return m_object->mathRadicandObject() ? m_object->mathRadicandObject()->wrapper() : 0;
1974 }
1975
1976 - (WebAccessibilityObjectWrapper *)accessibilityMathNumeratorObject
1977 {
1978     return m_object->mathNumeratorObject() ? m_object->mathNumeratorObject()->wrapper() : 0;
1979 }
1980
1981 - (WebAccessibilityObjectWrapper *)accessibilityMathDenominatorObject
1982 {
1983     return m_object->mathDenominatorObject() ? m_object->mathDenominatorObject()->wrapper() : 0;
1984 }
1985
1986 - (WebAccessibilityObjectWrapper *)accessibilityMathBaseObject
1987 {
1988     return m_object->mathBaseObject() ? m_object->mathBaseObject()->wrapper() : 0;
1989 }
1990
1991 - (WebAccessibilityObjectWrapper *)accessibilityMathSubscriptObject
1992 {
1993     return m_object->mathSubscriptObject() ? m_object->mathSubscriptObject()->wrapper() : 0;
1994 }
1995
1996 - (WebAccessibilityObjectWrapper *)accessibilityMathSuperscriptObject
1997 {
1998     return m_object->mathSuperscriptObject() ? m_object->mathSuperscriptObject()->wrapper() : 0;
1999 }
2000
2001 - (WebAccessibilityObjectWrapper *)accessibilityMathUnderObject
2002 {
2003     return m_object->mathUnderObject() ? m_object->mathUnderObject()->wrapper() : 0;
2004 }
2005
2006 - (WebAccessibilityObjectWrapper *)accessibilityMathOverObject
2007 {
2008     return m_object->mathOverObject() ? m_object->mathOverObject()->wrapper() : 0;
2009 }
2010
2011 - (NSString *)accessibilityMathFencedOpenString
2012 {
2013     return m_object->mathFencedOpenString();
2014 }
2015
2016 - (NSString *)accessibilityMathFencedCloseString
2017 {
2018     return m_object->mathFencedCloseString();
2019 }
2020
2021 - (BOOL)accessibilityIsMathTopObject
2022 {
2023     return m_object->roleValue() == DocumentMathRole;
2024 }
2025
2026 - (NSInteger)accessibilityMathLineThickness
2027 {
2028     if (![self _prepareAccessibilityCall])
2029         return 0;
2030
2031     return m_object->mathLineThickness();
2032 }
2033
2034 - (NSString *)accessibilityMathType
2035 {
2036     if (m_object->roleValue() == MathElementRole) {
2037         if (m_object->isMathFraction())
2038             return @"AXMathFraction";
2039         if (m_object->isMathFenced())
2040             return @"AXMathFenced";
2041         if (m_object->isMathSubscriptSuperscript())
2042             return @"AXMathSubscriptSuperscript";
2043         if (m_object->isMathRow())
2044             return @"AXMathRow";
2045         if (m_object->isMathUnderOver())
2046             return @"AXMathUnderOver";
2047         if (m_object->isMathSquareRoot())
2048             return @"AXMathSquareRoot";
2049         if (m_object->isMathRoot())
2050             return @"AXMathRoot";
2051         if (m_object->isMathText())
2052             return @"AXMathText";
2053         if (m_object->isMathNumber())
2054             return @"AXMathNumber";
2055         if (m_object->isMathIdentifier())
2056             return @"AXMathIdentifier";
2057         if (m_object->isMathTable())
2058             return @"AXMathTable";
2059         if (m_object->isMathTableRow())
2060             return @"AXMathTableRow";
2061         if (m_object->isMathTableCell())
2062             return @"AXMathTableCell";
2063         if (m_object->isMathFenceOperator())
2064             return @"AXMathFenceOperator";
2065         if (m_object->isMathSeparatorOperator())
2066             return @"AXMathSeparatorOperator";
2067         if (m_object->isMathOperator())
2068             return @"AXMathOperator";
2069     }
2070     
2071     return nil;
2072 }
2073
2074 - (CGPoint)accessibilityClickPoint
2075 {
2076     return m_object->clickPoint();
2077 }
2078
2079 // These are used by DRT so that it can know when notifications are sent.
2080 // Since they are static, only one callback can be installed at a time (that's all DRT should need).
2081 typedef void (*AXPostedNotificationCallback)(id element, NSString* notification, void* context);
2082 static AXPostedNotificationCallback AXNotificationCallback = 0;
2083 static void* AXPostedNotificationContext = 0;
2084
2085 - (void)accessibilitySetPostedNotificationCallback:(AXPostedNotificationCallback)function withContext:(void*)context
2086 {
2087     AXNotificationCallback = function;
2088     AXPostedNotificationContext = context;
2089 }
2090
2091 - (void)accessibilityPostedNotification:(NSString *)notificationName
2092 {
2093     if (AXNotificationCallback && notificationName)
2094         AXNotificationCallback(self, notificationName, AXPostedNotificationContext);
2095 }
2096
2097 #ifndef NDEBUG
2098 - (NSString *)description
2099 {
2100     CGRect frame = [self accessibilityFrame];
2101     return [NSString stringWithFormat:@"Role: (%d) - Text: %@: Value: %@ -- Frame: %f %f %f %f", m_object ? m_object->roleValue() : 0, [self accessibilityLabel], [self accessibilityValue], frame.origin.x, frame.origin.y, frame.size.width, frame.size.height];
2102 }
2103 #endif
2104
2105 @end
2106
2107 #endif // HAVE(ACCESSIBILITY) && PLATFORM(IOS)