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