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