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