8e1281418d2f39a6efba0c38da1fa946ff0ff762
[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     if (m_object->isFileUploadButton())
662         return NO;
663
664     return YES;
665 }
666
667 - (BOOL)fileUploadButtonReturnsValueInTitle
668 {
669     return NO;
670 }
671
672 - (NSString *)accessibilityLabel
673 {
674     if (![self _prepareAccessibilityCall])
675         return nil;
676
677     // check if the label was overriden
678     NSString *label = [super accessibilityLabel];
679     if (label)
680         return label;
681
682     // iOS doesn't distinguish between a title and description field,
683     // so concatentation will yield the best result.
684     NSString *axTitle = [self accessibilityTitle];
685     NSString *axDescription = [self accessibilityDescription];
686     NSUInteger axTitleLength = [axTitle length];
687     NSUInteger axDescriptionLength = [axDescription length];
688     
689     if (axTitleLength && axDescriptionLength)
690         return [axTitle stringByAppendingFormat:@", %@", axDescription];
691     else if (axTitleLength)
692         return axTitle;
693     else if (axDescriptionLength)
694         return axDescription;
695     else if ([self stringValueShouldBeUsedInLabel])
696         return m_object->stringValue();
697     
698     return nil;
699 }
700
701 - (AccessibilityTableCell*)tableCellParent
702 {
703     // Find if this element is in a table cell.
704     AccessibilityObject* cell = 0;
705     for (cell = m_object; cell && !cell->isTableCell(); cell = cell->parentObject()) 
706     { }
707     
708     if (!cell)
709         return 0;
710
711     return static_cast<AccessibilityTableCell*>(cell);
712 }
713
714 - (AccessibilityTable*)tableParent
715 {
716     // Find if the parent table for the table cell.
717     AccessibilityObject* parentTable = 0;
718     for (parentTable = m_object; parentTable && !parentTable->isDataTable(); parentTable = parentTable->parentObject()) 
719     { }
720     
721     if (!parentTable)
722         return 0;
723     
724     return static_cast<AccessibilityTable*>(parentTable);
725 }
726
727 - (id)accessibilityTitleElement
728 {
729     if (![self _prepareAccessibilityCall])
730         return nil;
731
732     AccessibilityObject* titleElement = m_object->titleUIElement();
733     if (titleElement)
734         return titleElement->wrapper();
735
736     return nil;
737 }
738
739 // Meant to return row or column headers (or other things as the future permits).
740 - (NSArray *)accessibilityHeaderElements
741 {
742     if (![self _prepareAccessibilityCall])
743         return nil;
744
745     AccessibilityTableCell* tableCell = [self tableCellParent];
746     if (!tableCell)
747         return nil;
748     
749     AccessibilityTable* table = [self tableParent];
750     if (!table)
751         return nil;
752     
753     // Get the row and column range, so we can use them to find the headers.
754     pair<unsigned, unsigned> rowRange;
755     pair<unsigned, unsigned> columnRange;
756     tableCell->rowIndexRange(rowRange);
757     tableCell->columnIndexRange(columnRange);
758     
759     AccessibilityObject::AccessibilityChildrenVector rowHeaders;
760     AccessibilityObject::AccessibilityChildrenVector columnHeaders;
761     table->rowHeaders(rowHeaders);
762     table->columnHeaders(columnHeaders);
763     
764     NSMutableArray *headers = [NSMutableArray array];
765     
766     unsigned columnRangeIndex = static_cast<unsigned>(columnRange.first);
767     if (columnRangeIndex < columnHeaders.size()) {
768         RefPtr<AccessibilityObject> columnHeader = columnHeaders[columnRange.first];
769         AccessibilityObjectWrapper* wrapper = columnHeader->wrapper();
770         if (wrapper)
771             [headers addObject:wrapper];
772     }
773
774     unsigned rowRangeIndex = static_cast<unsigned>(rowRange.first);
775     if (rowRangeIndex < rowHeaders.size()) {
776         RefPtr<AccessibilityObject> rowHeader = rowHeaders[rowRange.first];
777         AccessibilityObjectWrapper* wrapper = rowHeader->wrapper();
778         if (wrapper)
779             [headers addObject:wrapper];
780     }
781         
782     return headers;
783 }
784
785 - (id)accessibilityElementForRow:(NSInteger)row andColumn:(NSInteger)column
786 {
787     if (![self _prepareAccessibilityCall])
788         return nil;
789
790     AccessibilityTable* table = [self tableParent];
791     if (!table)
792         return nil;
793
794     AccessibilityTableCell* cell = table->cellForColumnAndRow(column, row);
795     if (!cell)
796         return nil;
797     return cell->wrapper();
798 }
799
800 - (NSRange)accessibilityRowRange
801 {
802     if (![self _prepareAccessibilityCall])
803         return NSMakeRange(NSNotFound, 0);
804
805     if (m_object->isRadioButton()) {
806         AccessibilityObject::AccessibilityChildrenVector radioButtonSiblings;
807         m_object->linkedUIElements(radioButtonSiblings);
808         if (radioButtonSiblings.size() <= 1)
809             return NSMakeRange(NSNotFound, 0);
810         
811         return NSMakeRange(radioButtonSiblings.find(m_object), radioButtonSiblings.size());
812     }
813     
814     AccessibilityTableCell* tableCell = [self tableCellParent];
815     if (!tableCell)
816         return NSMakeRange(NSNotFound, 0);
817     
818     pair<unsigned, unsigned> rowRange;
819     tableCell->rowIndexRange(rowRange);
820     return NSMakeRange(rowRange.first, rowRange.second);
821 }
822
823 - (NSRange)accessibilityColumnRange
824 {
825     if (![self _prepareAccessibilityCall])
826         return NSMakeRange(NSNotFound, 0);
827
828     AccessibilityTableCell* tableCell = [self tableCellParent];
829     if (!tableCell)
830         return NSMakeRange(NSNotFound, 0);
831     
832     pair<unsigned, unsigned> columnRange;
833     tableCell->columnIndexRange(columnRange);
834     return NSMakeRange(columnRange.first, columnRange.second);
835 }
836
837 - (NSString *)accessibilityPlaceholderValue
838 {
839     if (![self _prepareAccessibilityCall])
840         return nil;
841
842     return m_object->placeholderValue();
843 }
844
845 - (NSString *)accessibilityValue
846 {
847     if (![self _prepareAccessibilityCall])
848         return nil;
849     
850     // check if the value was overriden
851     NSString *value = [super accessibilityValue];
852     if (value)
853         return value;
854     
855     if (m_object->isCheckboxOrRadio()) {
856         switch (m_object->checkboxOrRadioValue()) {
857         case ButtonStateOff:
858             return [NSString stringWithFormat:@"%d", 0];
859         case ButtonStateOn:
860             return [NSString stringWithFormat:@"%d", 1];
861         case ButtonStateMixed:
862             return [NSString stringWithFormat:@"%d", 2];
863         }
864         ASSERT_NOT_REACHED();
865         return [NSString stringWithFormat:@"%d", 0];
866     }
867     
868     if (m_object->isButton() && m_object->isPressed())
869         return [NSString stringWithFormat:@"%d", 1];
870
871     // rdar://8131388 WebKit should expose the same info as UIKit for its password fields.
872     if (m_object->isPasswordField()) {
873         int passwordLength = m_object->accessibilityPasswordFieldLength();
874         NSMutableString* string = [NSMutableString string];
875         for (int k = 0; k < passwordLength; ++k)
876             [string appendString:@"•"];
877         return string;
878     }
879     
880     // A text control should return its text data as the axValue (per iPhone AX API).
881     if (![self stringValueShouldBeUsedInLabel])
882         return m_object->stringValue();
883     
884     if (m_object->isProgressIndicator() || m_object->isSlider()) {
885         // Prefer a valueDescription if provided by the author (through aria-valuetext).
886         String valueDescription = m_object->valueDescription();
887         if (!valueDescription.isEmpty())
888             return valueDescription;
889
890         return [NSString stringWithFormat:@"%.2f", m_object->valueForRange()];
891     }
892
893     if (m_object->isHeading())
894         return [NSString stringWithFormat:@"%d", m_object->headingLevel()];
895     
896     return nil;
897 }
898
899 - (BOOL)accessibilityIsComboBox
900 {
901     if (![self _prepareAccessibilityCall])
902         return NO;
903
904     return m_object->roleValue() == ComboBoxRole;
905 }
906
907 - (NSString *)accessibilityHint
908 {
909     if (![self _prepareAccessibilityCall])
910         return nil;
911
912     return [self accessibilityHelpText];
913 }
914
915 - (NSURL *)accessibilityURL
916 {
917     if (![self _prepareAccessibilityCall])
918         return nil;
919     
920     KURL url = m_object->url();
921     if (url.isNull())
922         return nil;
923     return (NSURL*)url;
924 }
925
926 - (CGRect)_convertIntRectToScreenCoordinates:(IntRect)rect
927 {
928     if (!m_object)
929         return CGRectZero;
930     
931     CGSize size = CGSizeMake(rect.size().width(), rect.size().height());
932     CGPoint point = CGPointMake(rect.x(), rect.y());
933     
934     CGRect frame = CGRectMake(point.x, point.y, size.width, size.height);
935     
936     FrameView* frameView = m_object->documentFrameView();
937     if (frameView) {
938         WAKView* view = frameView->documentView();
939         frame = [view convertRect:frame toView:nil];
940     }
941     
942     // we need the web document view to give us our final screen coordinates
943     // because that can take account of the scroller
944     id webDocument = [self _accessibilityWebDocumentView];
945     if (webDocument)
946         frame = [webDocument convertRect:frame toView:nil];
947     
948     return frame;
949 }
950
951 // Used by UIKit accessibility bundle to help determine distance during a hit-test.
952 - (CGRect)accessibilityElementRect
953 {
954     if (![self _prepareAccessibilityCall])
955         return CGRectZero;
956     
957     LayoutRect rect = m_object->elementRect();
958     return CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
959 }
960
961 // The "center point" is where VoiceOver will "press" an object. This may not be the actual
962 // center of the accessibilityFrame
963 - (CGPoint)accessibilityActivationPoint
964 {
965     if (![self _prepareAccessibilityCall])
966         return CGPointZero;
967     
968     IntRect rect = pixelSnappedIntRect(m_object->boundingBoxRect());
969     CGRect cgRect = [self _convertIntRectToScreenCoordinates:rect];
970     return CGPointMake(CGRectGetMidX(cgRect), CGRectGetMidY(cgRect));
971 }
972
973 - (CGRect)accessibilityFrame
974 {
975     if (![self _prepareAccessibilityCall])
976         return CGRectZero;
977     
978     IntRect rect = pixelSnappedIntRect(m_object->elementRect());
979     return [self _convertIntRectToScreenCoordinates:rect];
980 }
981
982 // Checks whether a link contains only static text and images (and has been divided unnaturally by <spans> and other nefarious mechanisms).
983 - (BOOL)containsUnnaturallySegmentedChildren
984 {
985     if (!m_object)
986         return NO;
987     
988     AccessibilityRole role = m_object->roleValue();
989     if (role != LinkRole && role != WebCoreLinkRole)
990         return NO;
991     
992     AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
993     unsigned childrenSize = children.size();
994
995     // If there's only one child, then it doesn't have segmented children. 
996     if (childrenSize == 1)
997         return NO;
998     
999     for (unsigned i = 0; i < childrenSize; ++i) {
1000         AccessibilityRole role = children[i]->roleValue();
1001         if (role != StaticTextRole && role != ImageRole && role != GroupRole)
1002             return NO;
1003     }
1004     
1005     return YES;
1006 }
1007
1008 - (id)accessibilityContainer
1009 {
1010     if (![self _prepareAccessibilityCall])
1011         return nil;
1012     
1013     // As long as there's a parent wrapper, that's the correct chain to climb.
1014     AccessibilityObject* parent = m_object->parentObjectUnignored(); 
1015     if (parent)
1016         return parent->wrapper();
1017
1018     // The only object without a parent wrapper should be a scroll view.
1019     ASSERT(m_object->isAccessibilityScrollView());
1020     
1021     // Verify this is the top document. If not, we might need to go through the platform widget.
1022     FrameView* frameView = m_object->documentFrameView();
1023     Document* document = m_object->document();
1024     if (document && frameView && document != document->topDocument())
1025         return frameView->platformWidget();
1026     
1027     // The top scroll view's parent is the web document view.
1028     return [self _accessibilityWebDocumentView];
1029 }
1030
1031 - (id)accessibilityFocusedUIElement
1032 {
1033     if (![self _prepareAccessibilityCall])
1034         return nil;
1035     
1036     AccessibilityObject* focusedObj = m_object->focusedUIElement();
1037     
1038     if (!focusedObj)
1039         return nil;
1040     
1041     return focusedObj->wrapper();
1042 }
1043
1044 - (id)_accessibilityWebDocumentView
1045 {
1046     if (![self _prepareAccessibilityCall])
1047         return nil;
1048
1049     // This method performs the crucial task of connecting to the UIWebDocumentView.
1050     // This is needed to correctly calculate the screen position of the AX object.
1051     static Class webViewClass = nil;
1052     if (!webViewClass)
1053         webViewClass = NSClassFromString(@"WebView");
1054
1055     if (!webViewClass)
1056         return nil;
1057     
1058     FrameView* frameView = m_object->documentFrameView();
1059
1060     if (!frameView)
1061         return nil;
1062     
1063     // If this is the top level frame, the UIWebDocumentView should be returned.
1064     id parentView = frameView->documentView();
1065     while (parentView && ![parentView isKindOfClass:webViewClass])
1066         parentView = [parentView superview];
1067     
1068     // The parentView should have an accessibilityContainer, if the UIKit accessibility bundle was loaded. 
1069     // The exception is DRT, which tests accessibility without the entire system turning accessibility on. Hence,
1070     // this check should be valid for everything except DRT.
1071     ASSERT([parentView accessibilityContainer] || applicationIsDumpRenderTree());
1072     
1073     return [parentView accessibilityContainer];
1074 }
1075
1076 - (NSArray *)_accessibilityNextElementsWithCount:(UInt32)count
1077 {    
1078     if (![self _prepareAccessibilityCall])
1079         return nil;
1080     
1081     return [[self _accessibilityWebDocumentView] _accessibilityNextElementsWithCount:count];
1082 }
1083
1084 - (NSArray *)_accessibilityPreviousElementsWithCount:(UInt32)count
1085 {
1086     if (![self _prepareAccessibilityCall])
1087         return nil;
1088     
1089     return [[self _accessibilityWebDocumentView] _accessibilityPreviousElementsWithCount:count];
1090 }
1091
1092 - (BOOL)accessibilityRequired
1093 {
1094     if (![self _prepareAccessibilityCall])
1095         return NO;
1096
1097     return m_object->isRequired();
1098 }
1099
1100 - (NSArray *)accessibilityFlowToElements
1101 {
1102     if (![self _prepareAccessibilityCall])
1103         return nil;
1104     
1105     AccessibilityObject::AccessibilityChildrenVector children;
1106     m_object->ariaFlowToElements(children);
1107     
1108     unsigned length = children.size();
1109     NSMutableArray* array = [NSMutableArray arrayWithCapacity:length];
1110     for (unsigned i = 0; i < length; ++i) {
1111         AccessibilityObjectWrapper* wrapper = children[i]->wrapper();
1112         ASSERT(wrapper);
1113         if (!wrapper)
1114             continue;
1115
1116         if (children[i]->isAttachment() && [wrapper attachmentView])
1117             [array addObject:[wrapper attachmentView]];
1118         else
1119             [array addObject:wrapper];
1120     }
1121     return array;
1122 }
1123
1124 - (id)accessibilityLinkedElement
1125 {
1126     if (![self _prepareAccessibilityCall])
1127         return nil;
1128     
1129     // If this static text inside of a link, it should use its parent's linked element.
1130     AccessibilityObject* element = m_object;
1131     if (m_object->roleValue() == StaticTextRole && m_object->parentObjectUnignored()->isLink())
1132         element = m_object->parentObjectUnignored();
1133     
1134     AccessibilityObject::AccessibilityChildrenVector children;
1135     element->linkedUIElements(children);
1136     if (children.size() == 0)
1137         return nil;
1138     
1139     return children[0]->wrapper();
1140 }
1141
1142
1143 - (BOOL)isAttachment
1144 {
1145     if (!m_object)
1146         return NO;
1147     
1148     return m_object->isAttachment();
1149 }
1150
1151 - (void)_accessibilityActivate
1152 {
1153     if (![self _prepareAccessibilityCall])
1154         return;
1155
1156     m_object->press();
1157 }
1158
1159 - (id)attachmentView
1160 {
1161     if (![self _prepareAccessibilityCall])
1162         return nil;
1163
1164     ASSERT([self isAttachment]);
1165     Widget* widget = m_object->widgetForAttachmentView();
1166     if (!widget)
1167         return nil;
1168     return widget->platformWidget();    
1169 }
1170
1171 static RenderObject* rendererForView(WAKView* view)
1172 {
1173     if (![view conformsToProtocol:@protocol(WebCoreFrameView)])
1174         return 0;
1175     
1176     WAKView<WebCoreFrameView>* frameView = (WAKView<WebCoreFrameView>*)view;
1177     Frame* frame = [frameView _web_frame];
1178     if (!frame)
1179         return 0;
1180     
1181     Node* node = frame->document()->ownerElement();
1182     if (!node)
1183         return 0;
1184     
1185     return node->renderer();
1186 }
1187
1188 - (id)_accessibilityParentForSubview:(id)subview
1189 {   
1190     RenderObject* renderer = rendererForView(subview);
1191     if (!renderer)
1192         return nil;
1193     
1194     AccessibilityObject* obj = renderer->document()->axObjectCache()->getOrCreate(renderer);
1195     if (obj)
1196         return obj->parentObjectUnignored()->wrapper();
1197     return nil;
1198 }
1199
1200 - (void)postFocusChangeNotification
1201 {
1202     // The UIKit accessibility wrapper will override and post appropriate notification.
1203 }
1204
1205 - (void)postSelectedTextChangeNotification
1206 {
1207     // The UIKit accessibility wrapper will override and post appropriate notification.    
1208 }
1209
1210 - (void)postLayoutChangeNotification
1211 {
1212     // The UIKit accessibility wrapper will override and post appropriate notification.        
1213 }
1214
1215 - (void)postLiveRegionChangeNotification
1216 {
1217     // The UIKit accessibility wrapper will override and post appropriate notification.    
1218 }
1219
1220 - (void)postLoadCompleteNotification
1221 {
1222     // The UIKit accessibility wrapper will override and post appropriate notification.    
1223 }
1224
1225 - (void)postChildrenChangedNotification
1226 {
1227     // The UIKit accessibility wrapper will override and post appropriate notification.    
1228 }
1229
1230 - (void)postInvalidStatusChangedNotification
1231 {
1232     // The UIKit accessibility wrapper will override and post appropriate notification.        
1233 }
1234
1235 - (void)accessibilityElementDidBecomeFocused
1236 {
1237     if (![self _prepareAccessibilityCall])
1238         return;
1239     
1240     // The focused VoiceOver element might be the text inside a link.
1241     // In those cases we should focus on the link itself.
1242     for (AccessibilityObject* object = m_object; object != nil; object = object->parentObject()) {
1243         if (object->roleValue() == WebAreaRole)
1244             break;
1245
1246         if (object->canSetFocusAttribute()) {
1247             object->setFocused(true);
1248             break;
1249         }
1250     }
1251 }
1252
1253 - (void)accessibilityModifySelection:(TextGranularity)granularity increase:(BOOL)increase
1254 {
1255     if (![self _prepareAccessibilityCall])
1256         return;
1257     
1258     FrameSelection* frameSelection = m_object->document()->frame()->selection();
1259     VisibleSelection selection = m_object->selection();
1260     VisiblePositionRange range = m_object->visiblePositionRange();
1261     
1262     // Before a selection with length exists, the cursor position needs to move to the right starting place.
1263     // That should be the beginning of this element (range.start). However, if the cursor is already within the 
1264     // range of this element (the cursor is represented by selection), then the cursor does not need to move.
1265     if (frameSelection->isNone() && (selection.visibleStart() < range.start || selection.visibleEnd() > range.end))
1266         frameSelection->moveTo(range.start, UserTriggered);
1267     
1268     frameSelection->modify(FrameSelection::AlterationExtend, (increase) ? DirectionRight : DirectionLeft, granularity, UserTriggered);
1269 }
1270
1271 - (void)accessibilityIncreaseSelection:(TextGranularity)granularity
1272 {
1273     [self accessibilityModifySelection:granularity increase:YES];
1274 }
1275
1276 - (void)accessibilityDecreaseSelection:(TextGranularity)granularity
1277 {
1278     [self accessibilityModifySelection:granularity increase:NO];
1279 }
1280
1281 - (void)accessibilityMoveSelectionToMarker:(WebAccessibilityTextMarker *)marker
1282 {
1283     if (![self _prepareAccessibilityCall])
1284         return;
1285     
1286     VisiblePosition visiblePosition = [marker visiblePosition];
1287     if (visiblePosition.isNull())
1288         return;
1289
1290     FrameSelection* frameSelection = m_object->document()->frame()->selection();
1291     frameSelection->moveTo(visiblePosition, UserTriggered);
1292 }
1293
1294 - (void)accessibilityIncrement
1295 {
1296     if (![self _prepareAccessibilityCall])
1297         return;
1298
1299     m_object->increment();
1300 }
1301
1302 - (void)accessibilityDecrement
1303 {
1304     if (![self _prepareAccessibilityCall])
1305         return;
1306
1307     m_object->decrement();
1308 }
1309
1310 #pragma mark Accessibility Text Marker Handlers
1311
1312 - (BOOL)_addAccessibilityObject:(AccessibilityObject*)axObject toTextMarkerArray:(NSMutableArray *)array
1313 {
1314     if (!axObject)
1315         return NO;
1316     
1317     AccessibilityObjectWrapper* wrapper = axObject->wrapper();
1318     if (!wrapper)
1319         return NO;
1320
1321     // Don't add the same object twice, but since this has already been added, we should return
1322     // YES because we want to inform that it's in the array
1323     if ([array containsObject:wrapper])
1324         return YES;
1325     
1326     // Explicity set that this is now an element (in case other logic tries to override).
1327     [wrapper setValue:[NSNumber numberWithBool:YES] forKey:@"isAccessibilityElement"];    
1328     [array addObject:wrapper];
1329     return YES;
1330 }
1331
1332 - (NSString *)stringForTextMarkers:(NSArray *)markers
1333 {
1334     if (![self _prepareAccessibilityCall])
1335         return nil;
1336     
1337     if ([markers count] != 2)
1338         return nil;
1339     
1340     WebAccessibilityTextMarker* startMarker = [markers objectAtIndex:0];
1341     WebAccessibilityTextMarker* endMarker = [markers objectAtIndex:1];
1342     if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
1343         return nil;
1344     
1345     // extract the start and end VisiblePosition
1346     VisiblePosition startVisiblePosition = [startMarker visiblePosition];
1347     if (startVisiblePosition.isNull())
1348         return nil;
1349     
1350     VisiblePosition endVisiblePosition = [endMarker visiblePosition];
1351     if (endVisiblePosition.isNull())
1352         return nil;
1353
1354     VisiblePositionRange visiblePosRange = VisiblePositionRange(startVisiblePosition, endVisiblePosition);
1355     return m_object->stringForVisiblePositionRange(visiblePosRange);
1356 }
1357
1358 static int blockquoteLevel(RenderObject* renderer)
1359 {
1360     if (!renderer)
1361         return 0;
1362     
1363     int result = 0;
1364     for (Node* node = renderer->node(); node; node = node->parentNode()) {
1365         if (node->hasTagName(blockquoteTag))
1366             result += 1;
1367     }
1368     
1369     return result;
1370 }
1371
1372 static void AXAttributeStringSetBlockquoteLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1373 {
1374     int quoteLevel = blockquoteLevel(renderer);
1375     
1376     if (quoteLevel)
1377         [attrString addAttribute:UIAccessibilityTokenBlockquoteLevel value:[NSNumber numberWithInt:quoteLevel] range:range];
1378     else
1379         [attrString removeAttribute:UIAccessibilityTokenBlockquoteLevel range:range];
1380 }
1381
1382 static void AXAttributeStringSetHeadingLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1383 {
1384     if (!renderer)
1385         return;
1386     
1387     AccessibilityObject* parentObject = renderer->document()->axObjectCache()->getOrCreate(renderer->parent());
1388     int parentHeadingLevel = parentObject->headingLevel();
1389     
1390     if (parentHeadingLevel)
1391         [attrString addAttribute:UIAccessibilityTokenHeadingLevel value:[NSNumber numberWithInt:parentHeadingLevel] range:range];
1392     else
1393         [attrString removeAttribute:UIAccessibilityTokenHeadingLevel range:range];
1394 }
1395
1396 static void AXAttributeStringSetFont(NSMutableAttributedString* attrString, GSFontRef font, NSRange range)
1397 {
1398     if (!font)
1399         return;
1400     
1401     const char* nameCStr = GSFontGetFullName(font);
1402     const char* familyCStr = GSFontGetFamilyName(font);
1403     NSNumber* size = [NSNumber numberWithFloat:GSFontGetSize(font)];
1404     NSNumber* bold = [NSNumber numberWithBool:GSFontIsBold(font)];
1405     GSFontTraitMask traits = GSFontGetTraits(font);
1406     if (nameCStr)
1407         [attrString addAttribute:UIAccessibilityTokenFontName value:[NSString stringWithUTF8String:nameCStr] range:range];
1408     if (familyCStr)
1409         [attrString addAttribute:UIAccessibilityTokenFontFamily value:[NSString stringWithUTF8String:familyCStr] range:range];
1410     if ([size boolValue])
1411         [attrString addAttribute:UIAccessibilityTokenFontSize value:size range:range];
1412     if ([bold boolValue] || (traits & GSBoldFontMask))
1413         [attrString addAttribute:UIAccessibilityTokenBold value:[NSNumber numberWithBool:YES] range:range];
1414     if (traits & GSItalicFontMask)
1415         [attrString addAttribute:UIAccessibilityTokenItalic value:[NSNumber numberWithBool:YES] range:range];
1416
1417 }
1418
1419 static void AXAttributeStringSetNumber(NSMutableAttributedString* attrString, NSString* attribute, NSNumber* number, NSRange range)
1420 {
1421     if (number)
1422         [attrString addAttribute:attribute value:number range:range];
1423     else
1424         [attrString removeAttribute:attribute range:range];
1425 }
1426
1427 static void AXAttributeStringSetStyle(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1428 {
1429     RenderStyle* style = renderer->style();
1430     
1431     // set basic font info
1432     AXAttributeStringSetFont(attrString, style->font().primaryFont()->getGSFont(), range);
1433                 
1434     int decor = style->textDecorationsInEffect();
1435     if ((decor & (UNDERLINE | LINE_THROUGH)) != 0) {
1436         // find colors using quirk mode approach (strict mode would use current
1437         // color for all but the root line box, which would use getTextDecorationColors)
1438         Color underline, overline, linethrough;
1439         renderer->getTextDecorationColors(decor, underline, overline, linethrough);
1440         
1441         if (decor & UNDERLINE)
1442             AXAttributeStringSetNumber(attrString, UIAccessibilityTokenUnderline, [NSNumber numberWithBool:YES], range);
1443     }
1444 }
1445
1446 static void AXAttributedStringAppendText(NSMutableAttributedString* attrString, Node* node, const UChar* chars, int length)
1447 {
1448     // skip invisible text
1449     if (!node->renderer())
1450         return;
1451     
1452     // easier to calculate the range before appending the string
1453     NSRange attrStringRange = NSMakeRange([attrString length], length);
1454     
1455     // append the string from this node
1456     [[attrString mutableString] appendString:[NSString stringWithCharacters:chars length:length]];
1457     
1458     // set new attributes
1459     AXAttributeStringSetStyle(attrString, node->renderer(), attrStringRange);
1460     AXAttributeStringSetHeadingLevel(attrString, node->renderer(), attrStringRange);
1461     AXAttributeStringSetBlockquoteLevel(attrString, node->renderer(), attrStringRange);    
1462 }
1463
1464
1465 // This method is intended to return an array of strings and accessibility elements that 
1466 // represent the objects on one line of rendered web content. The array of markers sent
1467 // in should be ordered and contain only a start and end marker.
1468 - (NSArray *)arrayOfTextForTextMarkers:(NSArray *)markers attributed:(BOOL)attributed
1469 {
1470     if (![self _prepareAccessibilityCall])
1471         return nil;
1472
1473     if ([markers count] != 2)
1474         return nil;
1475     
1476     WebAccessibilityTextMarker* startMarker = [markers objectAtIndex:0];
1477     WebAccessibilityTextMarker* endMarker = [markers objectAtIndex:1];
1478     if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
1479         return nil;
1480     
1481     // extract the start and end VisiblePosition
1482     VisiblePosition startVisiblePosition = [startMarker visiblePosition];
1483     if (startVisiblePosition.isNull())
1484         return nil;
1485     
1486     VisiblePosition endVisiblePosition = [endMarker visiblePosition];
1487     if (endVisiblePosition.isNull())
1488         return nil;
1489     
1490     // iterate over the range to build the AX attributed string
1491     NSMutableArray* array = [[NSMutableArray alloc] init];
1492     TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get());
1493     for (; !it.atEnd(); it.advance()) {
1494         // locate the node and starting offset for this range
1495         int exception = 0;
1496         Node* node = it.range()->startContainer(exception);
1497         ASSERT(node == it.range()->endContainer(exception));
1498         int offset = it.range()->startOffset(exception);
1499         
1500         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
1501         if (it.length() != 0) {
1502             if (!attributed) {
1503                 // First check if this is represented by a link.
1504                 AccessibilityObject* linkObject = AccessibilityObject::anchorElementForNode(node);
1505                 if ([self _addAccessibilityObject:linkObject toTextMarkerArray:array])
1506                     continue;
1507                 
1508                 // Next check if this region is represented by a heading.
1509                 AccessibilityObject* headingObject = AccessibilityObject::headingElementForNode(node);
1510                 if ([self _addAccessibilityObject:headingObject toTextMarkerArray:array])
1511                     continue;
1512                 
1513                 String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition())); 
1514                 
1515                 if (!listMarkerText.isEmpty()) 
1516                     [array addObject:[NSString stringWithCharacters:listMarkerText.characters() length:listMarkerText.length()]];
1517                 // There was not an element representation, so just return the text.
1518                 [array addObject:[NSString stringWithCharacters:it.characters() length:it.length()]];
1519             }
1520             else
1521             {
1522                 String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition())); 
1523
1524                 if (!listMarkerText.isEmpty()) {
1525                     NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init];
1526                     AXAttributedStringAppendText(attrString, node, listMarkerText.characters(), listMarkerText.length());
1527                     [array addObject:attrString];
1528                     [attrString release];
1529                 }
1530                 
1531                 NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
1532                 AXAttributedStringAppendText(attrString, node, it.characters(), it.length());
1533                 [array addObject:attrString];
1534                 [attrString release];
1535             }
1536         } else {
1537             Node* replacedNode = node->childNode(offset);
1538             if (replacedNode) {
1539                 AccessibilityObject* obj = m_object->axObjectCache()->getOrCreate(replacedNode->renderer());
1540                 if (obj && !obj->accessibilityIsIgnored())
1541                     [self _addAccessibilityObject:obj toTextMarkerArray:array];
1542             }
1543         }
1544     }
1545     
1546     return [array autorelease];
1547 }
1548
1549 - (NSRange)_convertToNSRange:(Range *)range
1550 {
1551     if (!range || !range->startContainer())
1552         return NSMakeRange(NSNotFound, 0);
1553     
1554     Document* document = m_object->document();
1555     FrameSelection* frameSelection = document->frame()->selection();
1556     
1557     Element* selectionRoot = frameSelection->rootEditableElement();
1558     Element* scope = selectionRoot ? selectionRoot : document->documentElement();
1559     
1560     // Mouse events may cause TSM to attempt to create an NSRange for a portion of the view
1561     // that is not inside the current editable region.  These checks ensure we don't produce
1562     // potentially invalid data when responding to such requests.
1563     if (range->startContainer() != scope && !range->startContainer()->isDescendantOf(scope))
1564         return NSMakeRange(NSNotFound, 0);
1565     if (range->endContainer() != scope && !range->endContainer()->isDescendantOf(scope))
1566         return NSMakeRange(NSNotFound, 0);
1567     
1568     RefPtr<Range> testRange = Range::create(scope->document(), scope, 0, range->startContainer(), range->startOffset());
1569     ASSERT(testRange->startContainer() == scope);
1570     int startPosition = TextIterator::rangeLength(testRange.get());
1571     
1572     ExceptionCode ec;
1573     testRange->setEnd(range->endContainer(), range->endOffset(), ec);
1574     ASSERT(testRange->startContainer() == scope);
1575     int endPosition = TextIterator::rangeLength(testRange.get());
1576     return NSMakeRange(startPosition, endPosition - startPosition);
1577 }
1578
1579 - (PassRefPtr<Range>)_convertToDOMRange:(NSRange)nsrange
1580 {
1581     if (nsrange.location > INT_MAX)
1582         return 0;
1583     if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
1584         nsrange.length = INT_MAX - nsrange.location;
1585         
1586     // our critical assumption is that we are only called by input methods that
1587     // concentrate on a given area containing the selection
1588     // We have to do this because of text fields and textareas. The DOM for those is not
1589     // directly in the document DOM, so serialization is problematic. Our solution is
1590     // to use the root editable element of the selection start as the positional base.
1591     // That fits with AppKit's idea of an input context.
1592     Document* document = m_object->document();
1593     FrameSelection* frameSelection = document->frame()->selection();
1594     Element* selectionRoot = frameSelection->rootEditableElement();
1595     Element* scope = selectionRoot ? selectionRoot : document->documentElement();
1596     return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length);
1597 }
1598
1599 // This method is intended to take a text marker representing a VisiblePosition and convert it
1600 // into a normalized location within the document.
1601 - (NSInteger)positionForTextMarker:(WebAccessibilityTextMarker *)marker
1602 {
1603     if (![self _prepareAccessibilityCall])
1604         return NSNotFound;
1605
1606     if (!marker)
1607         return NSNotFound;    
1608
1609     VisibleSelection selection([marker visiblePosition]);
1610     RefPtr<Range> range = selection.toNormalizedRange();
1611     NSRange nsRange = [self _convertToNSRange:range.get()];
1612     return nsRange.location;
1613 }
1614
1615 - (NSArray *)textMarkerRange
1616 {
1617     if (![self _prepareAccessibilityCall])
1618         return nil;
1619     
1620     VisiblePositionRange range = m_object->visiblePositionRange();
1621     VisiblePosition startPosition = range.start;
1622     VisiblePosition endPosition = range.end;
1623     WebAccessibilityTextMarker* start = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:startPosition cache:m_object->axObjectCache()];
1624     WebAccessibilityTextMarker* end = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:endPosition cache:m_object->axObjectCache()];
1625     
1626     return [NSArray arrayWithObjects:start, end, nil];
1627 }
1628
1629 // A method to get the normalized text cursor range of an element. Used in DumpRenderTree.
1630 - (NSRange)elementTextRange
1631 {
1632     if (![self _prepareAccessibilityCall])
1633         return NSMakeRange(NSNotFound, 0);
1634
1635     NSArray *markers = [self textMarkerRange];
1636     if ([markers count] != 2)
1637         return NSMakeRange(NSNotFound, 0);
1638     
1639     WebAccessibilityTextMarker *startMarker = [markers objectAtIndex:0];
1640     WebAccessibilityTextMarker *endMarker = [markers objectAtIndex:1];
1641     
1642     NSInteger startPosition = [self positionForTextMarker:startMarker];
1643     NSInteger endPosition = [self positionForTextMarker:endMarker];
1644     
1645     return NSMakeRange(startPosition, endPosition - startPosition);
1646 }
1647
1648 - (AccessibilityObjectWrapper *)accessibilityObjectForTextMarker:(WebAccessibilityTextMarker *)marker
1649 {
1650     if (![self _prepareAccessibilityCall])
1651         return nil;
1652
1653     if (!marker)
1654         return nil;
1655     
1656     VisiblePosition visiblePosition = [marker visiblePosition];
1657     AccessibilityObject* obj = m_object->accessibilityObjectForPosition(visiblePosition);
1658     if (!obj)
1659         return nil;
1660     
1661     return AccessibilityUnignoredAncestor(obj->wrapper());
1662 }
1663
1664 - (NSArray *)textMarkerRangeForSelection
1665 {
1666     if (![self _prepareAccessibilityCall])
1667         return nil;
1668     
1669     VisibleSelection selection = m_object->selection();
1670     if (selection.isNone())
1671         return nil;
1672     VisiblePosition startPosition = selection.visibleStart();
1673     VisiblePosition endPosition = selection.visibleEnd();
1674
1675     WebAccessibilityTextMarker* startMarker = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:startPosition cache:m_object->axObjectCache()];
1676     WebAccessibilityTextMarker* endMarker = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:endPosition cache:m_object->axObjectCache()];
1677     if (!startMarker || !endMarker)
1678         return nil;
1679     
1680     return [NSArray arrayWithObjects:startMarker, endMarker, nil];
1681 }
1682
1683 - (WebAccessibilityTextMarker *)textMarkerForPosition:(NSInteger)position
1684 {
1685     if (![self _prepareAccessibilityCall])
1686         return nil;
1687
1688     PassRefPtr<Range> range = [self _convertToDOMRange:NSMakeRange(position, 0)];
1689     if (!range)
1690         return nil;
1691     
1692     VisibleSelection selection = VisibleSelection(range.get(), DOWNSTREAM);
1693
1694     VisiblePosition visiblePosition = selection.visibleStart();
1695     return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:visiblePosition cache:m_object->axObjectCache()];
1696 }
1697
1698 - (id)_stringForRange:(NSRange)range attributed:(BOOL)attributed
1699 {
1700     if (![self _prepareAccessibilityCall])
1701         return nil;
1702     
1703     WebAccessibilityTextMarker* startMarker = [self textMarkerForPosition:range.location];
1704     WebAccessibilityTextMarker* endMarker = [self textMarkerForPosition:NSMaxRange(range)];
1705     
1706     // Clients don't always know the exact range, rather than force them to compute it,
1707     // allow clients to overshoot and use the max text marker range.
1708     if (!startMarker || !endMarker) {
1709         NSArray *markers = [self textMarkerRange];
1710         if ([markers count] != 2)
1711             return nil;
1712         if (!startMarker)
1713             startMarker = [markers objectAtIndex:0];
1714         if (!endMarker)
1715             endMarker = [markers objectAtIndex:1];
1716     }
1717     
1718     NSArray* array = [self arrayOfTextForTextMarkers:[NSArray arrayWithObjects:startMarker, endMarker, nil] attributed:attributed];
1719     Class returnClass = attributed ? [NSMutableAttributedString class] : [NSMutableString class];
1720     id returnValue = [[[returnClass alloc] init] autorelease];
1721     
1722     NSInteger count = [array count];
1723     for (NSInteger k = 0; k < count; ++k) {
1724         id object = [array objectAtIndex:k];
1725
1726         if (![object isKindOfClass:returnClass])
1727             continue;
1728         
1729         if (attributed)
1730             [(NSMutableAttributedString *)returnValue appendAttributedString:object];
1731         else
1732             [(NSMutableString *)returnValue appendString:object];
1733     }
1734     return returnValue;
1735 }
1736
1737
1738 // A convenience method for getting the text of a NSRange. Currently used only by DRT.
1739 - (NSString *)stringForRange:(NSRange)range
1740 {
1741     return [self _stringForRange:range attributed:NO];
1742 }
1743
1744 - (NSAttributedString *)attributedStringForRange:(NSRange)range
1745 {
1746     return [self _stringForRange:range attributed:YES];
1747 }
1748
1749 // A convenience method for getting the accessibility objects of a NSRange. Currently used only by DRT.
1750 - (NSArray *)elementsForRange:(NSRange)range
1751 {
1752     if (![self _prepareAccessibilityCall])
1753         return nil;
1754     
1755     WebAccessibilityTextMarker* startMarker = [self textMarkerForPosition:range.location];
1756     WebAccessibilityTextMarker* endMarker = [self textMarkerForPosition:NSMaxRange(range)];
1757     if (!startMarker || !endMarker)
1758         return nil;
1759     
1760     NSArray* array = [self arrayOfTextForTextMarkers:[NSArray arrayWithObjects:startMarker, endMarker, nil] attributed:NO];
1761     NSMutableArray* elements = [NSMutableArray array];
1762     for (id element in array) {
1763         if (![element isKindOfClass:[AccessibilityObjectWrapper class]])
1764             continue;
1765         [elements addObject:element];
1766     }
1767     return elements;
1768 }
1769
1770 - (NSString *)selectionRangeString
1771 {
1772     NSArray *markers = [self textMarkerRangeForSelection];
1773     return [self stringForTextMarkers:markers];
1774 }
1775
1776 - (WebAccessibilityTextMarker *)selectedTextMarker
1777 {
1778     if (![self _prepareAccessibilityCall])
1779         return nil;
1780     
1781     VisibleSelection selection = m_object->selection();
1782     VisiblePosition position = selection.visibleStart();
1783     
1784     // if there's no selection, start at the top of the document
1785     if (position.isNull())
1786         position = startOfDocument(m_object->document());
1787     
1788     return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:position cache:m_object->axObjectCache()];
1789 }
1790
1791 // This method is intended to return the marker at the end of the line starting at
1792 // the marker that is passed into the method.
1793 - (WebAccessibilityTextMarker *)lineEndMarkerForMarker:(WebAccessibilityTextMarker *)marker
1794 {
1795     if (![self _prepareAccessibilityCall])
1796         return nil;
1797
1798     if (!marker)
1799         return nil;
1800     
1801     VisiblePosition start = [marker visiblePosition];
1802     VisiblePosition lineEnd = m_object->nextLineEndPosition(start);
1803     
1804     return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:lineEnd cache:m_object->axObjectCache()];
1805 }
1806
1807 // This method is intended to return the marker at the start of the line starting at
1808 // the marker that is passed into the method.
1809 - (WebAccessibilityTextMarker *)lineStartMarkerForMarker:(WebAccessibilityTextMarker *)marker
1810 {
1811     if (![self _prepareAccessibilityCall])
1812         return nil;
1813
1814     if (!marker)
1815         return nil;
1816     
1817     VisiblePosition start = [marker visiblePosition];
1818     VisiblePosition lineStart = m_object->previousLineStartPosition(start);
1819     
1820     return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:lineStart cache:m_object->axObjectCache()];
1821 }
1822
1823 - (WebAccessibilityTextMarker *)nextMarkerForMarker:(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 nextMarker = m_object->nextVisiblePosition(start);
1833     
1834     return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:nextMarker cache:m_object->axObjectCache()];
1835 }
1836
1837 // This method is intended to return the marker at the start of the line starting at
1838 // the marker that is passed into the method.
1839 - (WebAccessibilityTextMarker *)previousMarkerForMarker:(WebAccessibilityTextMarker *)marker
1840 {
1841     if (![self _prepareAccessibilityCall])
1842         return nil;
1843
1844     if (!marker)
1845         return nil;
1846     
1847     VisiblePosition start = [marker visiblePosition];
1848     VisiblePosition previousMarker = m_object->previousVisiblePosition(start);
1849     
1850     return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:previousMarker cache:m_object->axObjectCache()];
1851 }
1852
1853 // This method is intended to return the bounds of a text marker range in screen coordinates.
1854 - (CGRect)frameForTextMarkers:(NSArray *)array
1855 {
1856     if (![self _prepareAccessibilityCall])
1857         return CGRectZero;
1858
1859     if ([array count] != 2)
1860         return CGRectZero;
1861     
1862     WebAccessibilityTextMarker* startMarker = [array objectAtIndex:0];
1863     WebAccessibilityTextMarker* endMarker = [array objectAtIndex:1];
1864     if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
1865         return CGRectZero;
1866
1867     IntRect rect = m_object->boundsForVisiblePositionRange(VisiblePositionRange([startMarker visiblePosition], [endMarker visiblePosition]));
1868     return [self _convertIntRectToScreenCoordinates:rect];
1869 }
1870
1871 - (WebAccessibilityTextMarker *)textMarkerForPoint:(CGPoint)point
1872 {
1873     if (![self _prepareAccessibilityCall])
1874         return nil;
1875     
1876     VisiblePosition pos = m_object->visiblePositionForPoint(IntPoint(point));
1877     return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:pos cache:m_object->axObjectCache()];
1878 }
1879
1880 - (NSString *)accessibilityIdentifier
1881 {
1882     if (![self _prepareAccessibilityCall])
1883         return nil;
1884     
1885     return m_object->getAttribute(HTMLNames::idAttr);
1886 }
1887
1888 - (NSString *)accessibilitySpeechHint
1889 {
1890     if (![self _prepareAccessibilityCall])
1891         return nil;
1892
1893     switch (m_object->speakProperty()) {
1894     default:
1895     case SpeakNormal:
1896         return @"normal";
1897     case SpeakNone:
1898         return @"none";
1899     case SpeakSpellOut:
1900         return @"spell-out";
1901     case SpeakDigits:
1902         return @"digits";
1903     case SpeakLiteralPunctuation:
1904         return @"literal-punctuation";
1905     case SpeakNoPunctuation:
1906         return @"no-punctuation";
1907     }
1908     
1909     return nil;
1910 }
1911
1912 - (BOOL)accessibilityARIAIsBusy
1913 {
1914     if (![self _prepareAccessibilityCall])
1915         return nil;
1916
1917     return m_object->ariaLiveRegionBusy();
1918 }
1919
1920 - (NSString *)accessibilityARIALiveRegionStatus
1921 {
1922     if (![self _prepareAccessibilityCall])
1923         return nil;
1924
1925     return m_object->ariaLiveRegionStatus();
1926 }
1927
1928 - (NSString *)accessibilityARIARelevantStatus
1929 {
1930     if (![self _prepareAccessibilityCall])
1931         return nil;
1932     
1933     return m_object->ariaLiveRegionRelevant();
1934 }
1935
1936 - (BOOL)accessibilityARIALiveRegionIsAtomic
1937 {
1938     if (![self _prepareAccessibilityCall])
1939         return nil;
1940     
1941     return m_object->ariaLiveRegionAtomic();
1942 }
1943
1944 - (NSString *)accessibilityInvalidStatus
1945 {
1946     if (![self _prepareAccessibilityCall])
1947         return nil;
1948     
1949     return m_object->invalidStatus();
1950 }
1951
1952 - (WebAccessibilityObjectWrapper *)accessibilityMathRootIndexObject
1953 {
1954     return m_object->mathRootIndexObject() ? m_object->mathRootIndexObject()->wrapper() : 0;
1955 }
1956
1957 - (WebAccessibilityObjectWrapper *)accessibilityMathRadicandObject
1958 {
1959     return m_object->mathRadicandObject() ? m_object->mathRadicandObject()->wrapper() : 0;
1960 }
1961
1962 - (WebAccessibilityObjectWrapper *)accessibilityMathNumeratorObject
1963 {
1964     return m_object->mathNumeratorObject() ? m_object->mathNumeratorObject()->wrapper() : 0;
1965 }
1966
1967 - (WebAccessibilityObjectWrapper *)accessibilityMathDenominatorObject
1968 {
1969     return m_object->mathDenominatorObject() ? m_object->mathDenominatorObject()->wrapper() : 0;
1970 }
1971
1972 - (WebAccessibilityObjectWrapper *)accessibilityMathBaseObject
1973 {
1974     return m_object->mathBaseObject() ? m_object->mathBaseObject()->wrapper() : 0;
1975 }
1976
1977 - (WebAccessibilityObjectWrapper *)accessibilityMathSubscriptObject
1978 {
1979     return m_object->mathSubscriptObject() ? m_object->mathSubscriptObject()->wrapper() : 0;
1980 }
1981
1982 - (WebAccessibilityObjectWrapper *)accessibilityMathSuperscriptObject
1983 {
1984     return m_object->mathSuperscriptObject() ? m_object->mathSuperscriptObject()->wrapper() : 0;
1985 }
1986
1987 - (WebAccessibilityObjectWrapper *)accessibilityMathUnderObject
1988 {
1989     return m_object->mathUnderObject() ? m_object->mathUnderObject()->wrapper() : 0;
1990 }
1991
1992 - (WebAccessibilityObjectWrapper *)accessibilityMathOverObject
1993 {
1994     return m_object->mathOverObject() ? m_object->mathOverObject()->wrapper() : 0;
1995 }
1996
1997 - (NSString *)accessibilityMathFencedOpenString
1998 {
1999     return m_object->mathFencedOpenString();
2000 }
2001
2002 - (NSString *)accessibilityMathFencedCloseString
2003 {
2004     return m_object->mathFencedCloseString();
2005 }
2006
2007 - (BOOL)accessibilityIsMathTopObject
2008 {
2009     return m_object->roleValue() == DocumentMathRole;
2010 }
2011
2012 - (NSInteger)accessibilityMathLineThickness
2013 {
2014     if (![self _prepareAccessibilityCall])
2015         return 0;
2016
2017     return m_object->mathLineThickness();
2018 }
2019
2020 - (NSString *)accessibilityMathType
2021 {
2022     if (m_object->roleValue() == MathElementRole) {
2023         if (m_object->isMathFraction())
2024             return @"AXMathFraction";
2025         if (m_object->isMathFenced())
2026             return @"AXMathFenced";
2027         if (m_object->isMathSubscriptSuperscript())
2028             return @"AXMathSubscriptSuperscript";
2029         if (m_object->isMathRow())
2030             return @"AXMathRow";
2031         if (m_object->isMathUnderOver())
2032             return @"AXMathUnderOver";
2033         if (m_object->isMathSquareRoot())
2034             return @"AXMathSquareRoot";
2035         if (m_object->isMathRoot())
2036             return @"AXMathRoot";
2037         if (m_object->isMathText())
2038             return @"AXMathText";
2039         if (m_object->isMathNumber())
2040             return @"AXMathNumber";
2041         if (m_object->isMathIdentifier())
2042             return @"AXMathIdentifier";
2043         if (m_object->isMathTable())
2044             return @"AXMathTable";
2045         if (m_object->isMathTableRow())
2046             return @"AXMathTableRow";
2047         if (m_object->isMathTableCell())
2048             return @"AXMathTableCell";
2049         if (m_object->isMathFenceOperator())
2050             return @"AXMathFenceOperator";
2051         if (m_object->isMathSeparatorOperator())
2052             return @"AXMathSeparatorOperator";
2053         if (m_object->isMathOperator())
2054             return @"AXMathOperator";
2055     }
2056     
2057     return nil;
2058 }
2059
2060 // These are used by DRT so that it can know when notifications are sent.
2061 // Since they are static, only one callback can be installed at a time (that's all DRT should need).
2062 typedef void (*AXPostedNotificationCallback)(id element, NSString* notification, void* context);
2063 static AXPostedNotificationCallback AXNotificationCallback = 0;
2064 static void* AXPostedNotificationContext = 0;
2065
2066 - (void)accessibilitySetPostedNotificationCallback:(AXPostedNotificationCallback)function withContext:(void*)context
2067 {
2068     AXNotificationCallback = function;
2069     AXPostedNotificationContext = context;
2070 }
2071
2072 - (void)accessibilityPostedNotification:(NSString *)notificationName
2073 {
2074     if (AXNotificationCallback && notificationName)
2075         AXNotificationCallback(self, notificationName, AXPostedNotificationContext);
2076 }
2077
2078 #ifndef NDEBUG
2079 - (NSString *)description
2080 {
2081     CGRect frame = [self accessibilityFrame];
2082     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];
2083 }
2084 #endif
2085
2086 @end
2087
2088 #endif // HAVE(ACCESSIBILITY) && PLATFORM(IOS)