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