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