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