2 * Copyright (C) 2008, Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #import "WebAccessibilityObjectWrapperIOS.h"
29 #if HAVE(ACCESSIBILITY) && PLATFORM(IOS)
31 #import "AccessibilityRenderObject.h"
32 #import "AccessibilityTable.h"
33 #import "AccessibilityTableCell.h"
36 #import "FrameSelection.h"
38 #import "HitTestResult.h"
39 #import "HTMLFrameOwnerElement.h"
40 #import "HTMLInputElement.h"
45 #import "RenderView.h"
46 #import "RuntimeApplicationChecksIOS.h"
47 #import "TextIterator.h"
48 #import "WAKScrollView.h"
51 #import "WebCoreThread.h"
52 #import "visible_units.h"
54 #import <GraphicsServices/GraphicsServices.h>
56 @interface NSObject (AccessibilityPrivate)
57 - (void)_accessibilityUnregister;
58 - (NSString *)accessibilityLabel;
59 - (NSString *)accessibilityValue;
60 - (BOOL)isAccessibilityElement;
61 - (NSInteger)accessibilityElementCount;
62 - (id)accessibilityElementAtIndex:(NSInteger)index;
63 - (NSInteger)indexOfAccessibilityElement:(id)element;
66 @interface WebAccessibilityObjectWrapper (AccessibilityPrivate)
67 - (id)_accessibilityWebDocumentView;
68 - (id)accessibilityContainer;
69 - (void)setAccessibilityLabel:(NSString *)label;
70 - (void)setAccessibilityValue:(NSString *)value;
71 - (BOOL)containsUnnaturallySegmentedChildren;
72 - (NSInteger)positionForTextMarker:(id)marker;
75 @interface WAKView (iOSAccessibility)
76 - (BOOL)accessibilityIsIgnored;
79 using namespace WebCore;
80 using namespace HTMLNames;
82 // These are tokens accessibility uses to denote attributes.
83 static NSString * const UIAccessibilityTokenBlockquoteLevel = @"UIAccessibilityTokenBlockquoteLevel";
84 static NSString * const UIAccessibilityTokenHeadingLevel = @"UIAccessibilityTokenHeadingLevel";
85 static NSString * const UIAccessibilityTokenFontName = @"UIAccessibilityTokenFontName";
86 static NSString * const UIAccessibilityTokenFontFamily = @"UIAccessibilityTokenFontFamily";
87 static NSString * const UIAccessibilityTokenFontSize = @"UIAccessibilityTokenFontSize";
88 static NSString * const UIAccessibilityTokenBold = @"UIAccessibilityTokenBold";
89 static NSString * const UIAccessibilityTokenItalic = @"UIAccessibilityTokenItalic";
90 static NSString * const UIAccessibilityTokenUnderline = @"UIAccessibilityTokenUnderline";
92 static AccessibilityObjectWrapper* AccessibilityUnignoredAncestor(AccessibilityObjectWrapper *wrapper)
94 while (wrapper && ![wrapper isAccessibilityElement]) {
95 AccessibilityObject* object = [wrapper accessibilityObject];
99 if ([wrapper isAttachment] && ![[wrapper attachmentView] accessibilityIsIgnored])
102 AccessibilityObject* parentObject = object->parentObjectUnignored();
106 wrapper = parentObject->wrapper();
111 #pragma mark Accessibility Text Marker
113 @interface WebAccessibilityTextMarker : NSObject
115 AXObjectCache* _cache;
116 TextMarkerData _textMarkerData;
119 + (WebAccessibilityTextMarker *)textMarkerWithVisiblePosition:(VisiblePosition&)visiblePos cache:(AXObjectCache*)cache;
123 @implementation WebAccessibilityTextMarker
125 - (id)initWithTextMarker:(TextMarkerData *)data cache:(AXObjectCache*)cache
127 if (!(self = [super init]))
131 memcpy(&_textMarkerData, data, sizeof(TextMarkerData));
135 - (id)initWithData:(NSData *)data cache:(AXObjectCache*)cache
137 if (!(self = [super init]))
141 [data getBytes:&_textMarkerData length:sizeof(TextMarkerData)];
146 // This is needed for external clients to be able to create a text marker without having a pointer to the cache.
147 - (id)initWithData:(NSData *)data accessibilityObject:(AccessibilityObjectWrapper *)wrapper
149 WebCore::AccessibilityObject* axObject = [wrapper accessibilityObject];
153 return [self initWithData:data cache:axObject->axObjectCache()];
156 + (WebAccessibilityTextMarker *)textMarkerWithVisiblePosition:(VisiblePosition&)visiblePos cache:(AXObjectCache*)cache
158 TextMarkerData textMarkerData;
159 cache->textMarkerDataForVisiblePosition(textMarkerData, visiblePos);
161 return [[[WebAccessibilityTextMarker alloc] initWithTextMarker:&textMarkerData cache:cache] autorelease];
164 - (NSData *)dataRepresentation
166 return [NSData dataWithBytes:&_textMarkerData length:sizeof(TextMarkerData)];
169 - (VisiblePosition)visiblePosition
171 return _cache->visiblePositionForTextMarkerData(_textMarkerData);
174 - (NSString *)description
176 return [NSString stringWithFormat:@"[AXTextMarker %p] = node: %p offset: %d", self, _textMarkerData.node, _textMarkerData.offset];
181 @implementation WebAccessibilityObjectWrapper
183 - (id)initWithAccessibilityObject:(AccessibilityObject*)axObject
185 self = [super initWithAccessibilityObject:axObject];
189 // Initialize to a sentinel value.
190 m_accessibilityTraitsFromAncestor = ULLONG_MAX;
191 m_isAccessibilityElement = -1;
198 // rdar://8798960 Make sure the object is gone early, so that anything _accessibilityUnregister
199 // does can't call back into the render tree.
202 if ([self respondsToSelector:@selector(_accessibilityUnregister)])
203 [self _accessibilityUnregister];
208 // We should have been detached before deallocated.
213 - (BOOL)_prepareAccessibilityCall
215 // rdar://7980318 if we start a call, then block in WebThreadLock(), then we're dealloced on another, thread, we could
216 // crash, so we should retain ourself for the duration of usage here.
217 [[self retain] autorelease];
221 // If we came back from our thread lock and we were detached, we will no longer have an m_object.
225 m_object->updateBackingStore();
232 // These are here so that we don't have to import AXRuntime.
233 // The methods will be swizzled when the accessibility bundle is loaded.
235 - (uint64_t)_axLinkTrait { return (1 << 0); }
236 - (uint64_t)_axVisitedTrait { return (1 << 1); }
237 - (uint64_t)_axHeaderTrait { return (1 << 2); }
238 - (uint64_t)_axContainedByListTrait { return (1 << 3); }
239 - (uint64_t)_axContainedByTableTrait { return (1 << 4); }
240 - (uint64_t)_axContainedByLandmarkTrait { return (1 << 5); }
241 - (uint64_t)_axWebContentTrait { return (1 << 6); }
242 - (uint64_t)_axSecureTextFieldTrait { return (1 << 7); }
243 - (uint64_t)_axTextEntryTrait { return (1 << 8); }
244 - (uint64_t)_axHasTextCursorTrait { return (1 << 9); }
245 - (uint64_t)_axTextOperationsAvailableTrait { return (1 << 10); }
246 - (uint64_t)_axImageTrait { return (1 << 11); }
247 - (uint64_t)_axTabButtonTrait { return (1 << 12); }
248 - (uint64_t)_axButtonTrait { return (1 << 13); }
249 - (uint64_t)_axToggleTrait { return (1 << 14); }
250 - (uint64_t)_axPopupButtonTrait { return (1 << 15); }
251 - (uint64_t)_axStaticTextTrait { return (1 << 16); }
252 - (uint64_t)_axAdjustableTrait { return (1 << 17); }
253 - (uint64_t)_axMenuItemTrait { return (1 << 18); }
254 - (uint64_t)_axSelectedTrait { return (1 << 19); }
255 - (uint64_t)_axNotEnabledTrait { return (1 << 20); }
256 - (uint64_t)_axRadioButtonTrait { return (1 << 21); }
258 - (BOOL)accessibilityCanFuzzyHitTest
260 if (![self _prepareAccessibilityCall])
263 AccessibilityRole role = m_object->roleValue();
264 // Elements that can be returned when performing fuzzy hit testing.
269 case DisclosureTriangleRole:
271 case ImageMapLinkRole:
275 case ListBoxOptionRole:
278 case PopUpButtonRole:
279 case RadioButtonRole:
285 return !m_object->accessibilityIsIgnored();
291 - (AccessibilityObjectWrapper *)accessibilityPostProcessHitTest:(CGPoint)point
294 // The UIKit accessibility wrapper will override this and perform the post process hit test.
298 - (id)accessibilityHitTest:(CGPoint)point
300 if (![self _prepareAccessibilityCall])
303 // Try a fuzzy hit test first to find an accessible element.
304 RefPtr<AccessibilityObject> axObject = m_object->accessibilityHitTest(IntPoint(point));
308 // If this is a good accessible object to return, no extra work is required.
309 if ([axObject->wrapper() accessibilityCanFuzzyHitTest])
310 return AccessibilityUnignoredAncestor(axObject->wrapper());
312 // Check to see if we can post-process this hit test to find a better candidate.
313 AccessibilityObjectWrapper* wrapper = [axObject->wrapper() accessibilityPostProcessHitTest:point];
315 return AccessibilityUnignoredAncestor(wrapper);
317 // Fall back to default behavior.
318 return AccessibilityUnignoredAncestor(axObject->wrapper());
321 - (NSInteger)accessibilityElementCount
323 if (![self _prepareAccessibilityCall])
326 if ([self isAttachment])
327 return [[self attachmentView] accessibilityElementCount];
329 return m_object->children().size();
332 - (id)accessibilityElementAtIndex:(NSInteger)index
334 if (![self _prepareAccessibilityCall])
337 if ([self isAttachment])
338 return [[self attachmentView] accessibilityElementAtIndex:index];
340 AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
341 if (static_cast<unsigned>(index) >= children.size())
344 AccessibilityObjectWrapper* wrapper = children[index]->wrapper();
345 if (children[index]->isAttachment())
346 return [wrapper attachmentView];
351 - (NSInteger)indexOfAccessibilityElement:(id)element
353 if (![self _prepareAccessibilityCall])
356 if ([self isAttachment])
357 return [[self attachmentView] indexOfAccessibilityElement:element];
359 AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
360 unsigned count = children.size();
361 for (unsigned k = 0; k < count; ++k) {
362 AccessibilityObjectWrapper* wrapper = children[k]->wrapper();
363 if (wrapper == element || (children[k]->isAttachment() && [wrapper attachmentView] == element))
370 - (NSString *)accessibilityLanguage
372 if (![self _prepareAccessibilityCall])
375 return m_object->language();
378 - (BOOL)_accessibilityIsLandmarkRole:(AccessibilityRole)role
381 case LandmarkApplicationRole:
382 case LandmarkBannerRole:
383 case LandmarkComplementaryRole:
384 case LandmarkContentInfoRole:
385 case LandmarkMainRole:
386 case LandmarkNavigationRole:
387 case LandmarkSearchRole:
394 - (AccessibilityObjectWrapper*)_accessibilityListAncestor
396 for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
397 AccessibilityRole role = parent->roleValue();
398 if (role == ListRole || role == ListBoxRole)
399 return parent->wrapper();
405 - (AccessibilityObjectWrapper*)_accessibilityLandmarkAncestor
407 for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
408 if ([self _accessibilityIsLandmarkRole:parent->roleValue()])
409 return parent->wrapper();
415 - (AccessibilityObjectWrapper*)_accessibilityTableAncestor
417 for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
418 if (parent->roleValue() == TableRole)
419 return parent->wrapper();
425 - (uint64_t)_accessibilityTraitsFromAncestors
428 AccessibilityRole role = m_object->roleValue();
430 // Trait information also needs to be gathered from the parents above the object.
431 // The parentObject is needed instead of the unignoredParentObject, because a table might be ignored, but information still needs to be gathered from it.
432 for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
433 AccessibilityRole parentRole = parent->roleValue();
434 if (parentRole == WebAreaRole)
437 switch (parentRole) {
439 case WebCoreLinkRole:
440 traits |= [self _axLinkTrait];
441 if (parent->isVisited())
442 traits |= [self _axVisitedTrait];
446 traits |= [self _axHeaderTrait];
447 // If this object has the header trait, we should set the value
448 // to the heading level. If it was a static text element, we need to store
449 // the value as the label, because the heading level needs to the value.
450 AccessibilityObjectWrapper* wrapper = parent->wrapper();
451 if (role == StaticTextRole)
452 [self setAccessibilityLabel:m_object->stringValue()];
453 [self setAccessibilityValue:[wrapper accessibilityValue]];
458 traits |= [self _axContainedByListTrait];
461 traits |= [self _axContainedByTableTrait];
464 if ([self _accessibilityIsLandmarkRole:parentRole])
465 traits |= [self _axContainedByLandmarkTrait];
473 - (uint64_t)accessibilityTraits
475 if (![self _prepareAccessibilityCall])
478 AccessibilityRole role = m_object->roleValue();
479 uint64_t traits = [self _axWebContentTrait];
482 case WebCoreLinkRole:
483 traits |= [self _axLinkTrait];
484 if (m_object->isVisited())
485 traits |= [self _axVisitedTrait];
487 // TextFieldRole is intended to fall through to TextAreaRole, in order to pick up the text entry and text cursor traits.
489 if (m_object->isPasswordField())
490 traits |= [self _axSecureTextFieldTrait];
492 traits |= [self _axTextEntryTrait];
493 if (m_object->isFocused())
494 traits |= ([self _axHasTextCursorTrait] | [self _axTextOperationsAvailableTrait]);
497 traits |= [self _axImageTrait];
500 traits |= [self _axTabButtonTrait];
503 traits |= [self _axButtonTrait];
504 if (m_object->isPressed())
505 traits |= [self _axToggleTrait];
507 case PopUpButtonRole:
508 traits |= [self _axPopupButtonTrait];
510 case RadioButtonRole:
511 traits |= [self _axRadioButtonTrait] | [self _axToggleTrait];
514 traits |= ([self _axButtonTrait] | [self _axToggleTrait]);
517 traits |= [self _axHeaderTrait];
520 traits |= [self _axStaticTextTrait];
523 traits |= [self _axAdjustableTrait];
527 traits |= [self _axMenuItemTrait];
533 if (m_object->isSelected())
534 traits |= [self _axSelectedTrait];
536 if (!m_object->isEnabled())
537 traits |= [self _axNotEnabledTrait];
539 if (m_accessibilityTraitsFromAncestor == ULLONG_MAX)
540 m_accessibilityTraitsFromAncestor = [self _accessibilityTraitsFromAncestors];
542 traits |= m_accessibilityTraitsFromAncestor;
547 - (BOOL)determineIsAccessibilityElement
552 // Honor when something explicitly makes this an element (super will contain that logic)
553 if ([super isAccessibilityElement])
556 m_object->updateBackingStore();
558 switch (m_object->roleValue()) {
562 case PopUpButtonRole:
564 case RadioButtonRole:
567 case ValueIndicatorRole:
570 case IncrementorRole:
572 case DisclosureTriangleRole:
575 case ListBoxOptionRole:
577 case DocumentMathRole:
581 // Many text elements only contain a space.
582 if (![[[self accessibilityLabel] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length])
585 // Text elements that are just pieces of links or headers should not be exposed.
586 if ([AccessibilityUnignoredAncestor([self accessibilityContainer]) containsUnnaturallySegmentedChildren])
591 // Don't expose headers as elements; instead expose their children as elements, with the header trait (unless they have no children)
593 if (![self accessibilityElementCount])
597 // Links can sometimes be elements (when they only contain static text or don't contain anything).
598 // They should not be elements when containing text and other types.
599 case WebCoreLinkRole:
601 if ([self containsUnnaturallySegmentedChildren] || ![self accessibilityElementCount])
605 // All other elements are ignored on the iphone.
611 case ApplicationRole:
622 case BusyIndicatorRole:
623 case ProgressIndicatorRole:
637 case RulerMarkerRole:
644 - (BOOL)isAccessibilityElement
646 if (![self _prepareAccessibilityCall])
649 if (m_isAccessibilityElement == -1)
650 m_isAccessibilityElement = [self determineIsAccessibilityElement];
652 return m_isAccessibilityElement;
655 - (BOOL)stringValueShouldBeUsedInLabel
657 if (m_object->isTextControl())
659 if (m_object->roleValue() == PopUpButtonRole)
665 - (NSString *)accessibilityLabel
667 if (![self _prepareAccessibilityCall])
670 // check if the label was overriden
671 NSString *label = [super accessibilityLabel];
675 NSString* title = m_object->title();
677 title = m_object->accessibilityDescription();
679 // Everything except a text control needs to return their string value in the axLabel (per iPhone AX API).
680 if (![title length] && [self stringValueShouldBeUsedInLabel])
681 title = m_object->stringValue();
686 - (AccessibilityTableCell*)tableCellParent
688 // Find if this element is in a table cell.
689 AccessibilityObject* cell = 0;
690 for (cell = m_object; cell && !cell->isTableCell(); cell = cell->parentObject())
696 return static_cast<AccessibilityTableCell*>(cell);
699 - (AccessibilityTable*)tableParent
701 // Find if the parent table for the table cell.
702 AccessibilityObject* parentTable = 0;
703 for (parentTable = m_object; parentTable && !parentTable->isDataTable(); parentTable = parentTable->parentObject())
709 return static_cast<AccessibilityTable*>(parentTable);
712 - (id)accessibilityTitleElement
714 if (![self _prepareAccessibilityCall])
717 AccessibilityObject* titleElement = m_object->titleUIElement();
719 return titleElement->wrapper();
724 // Meant to return row or column headers (or other things as the future permits).
725 - (NSArray *)accessibilityHeaderElements
727 if (![self _prepareAccessibilityCall])
730 AccessibilityTableCell* tableCell = [self tableCellParent];
734 AccessibilityTable* table = [self tableParent];
738 // Get the row and column range, so we can use them to find the headers.
739 pair<int, int> rowRange;
740 pair<int, int> columnRange;
741 tableCell->rowIndexRange(rowRange);
742 tableCell->columnIndexRange(columnRange);
744 AccessibilityObject::AccessibilityChildrenVector rowHeaders;
745 AccessibilityObject::AccessibilityChildrenVector columnHeaders;
746 table->rowHeaders(rowHeaders);
747 table->columnHeaders(columnHeaders);
749 NSMutableArray *headers = [NSMutableArray array];
751 unsigned columnRangeIndex = static_cast<unsigned>(columnRange.first);
752 if (columnRangeIndex < columnHeaders.size()) {
753 RefPtr<AccessibilityObject> columnHeader = columnHeaders[columnRange.first];
754 AccessibilityObjectWrapper* wrapper = columnHeader->wrapper();
756 [headers addObject:wrapper];
759 unsigned rowRangeIndex = static_cast<unsigned>(rowRange.first);
760 if (rowRangeIndex < rowHeaders.size()) {
761 RefPtr<AccessibilityObject> rowHeader = rowHeaders[rowRange.first];
762 AccessibilityObjectWrapper* wrapper = rowHeader->wrapper();
764 [headers addObject:wrapper];
770 - (id)accessibilityElementForRow:(NSInteger)row andColumn:(NSInteger)column
772 if (![self _prepareAccessibilityCall])
775 AccessibilityTable* table = [self tableParent];
779 AccessibilityTableCell* cell = table->cellForColumnAndRow(column, row);
782 return cell->wrapper();
785 - (NSRange)accessibilityRowRange
787 if (![self _prepareAccessibilityCall])
788 return NSMakeRange(NSNotFound, 0);
790 if (m_object->isRadioButton()) {
791 AccessibilityObject::AccessibilityChildrenVector radioButtonSiblings;
792 m_object->linkedUIElements(radioButtonSiblings);
793 if (radioButtonSiblings.size() <= 1)
794 return NSMakeRange(NSNotFound, 0);
796 return NSMakeRange(radioButtonSiblings.find(m_object), radioButtonSiblings.size());
799 AccessibilityTableCell* tableCell = [self tableCellParent];
801 return NSMakeRange(NSNotFound, 0);
803 pair<int, int> rowRange;
804 tableCell->rowIndexRange(rowRange);
805 return NSMakeRange(rowRange.first, rowRange.second);
808 - (NSRange)accessibilityColumnRange
810 if (![self _prepareAccessibilityCall])
811 return NSMakeRange(NSNotFound, 0);
813 AccessibilityTableCell* tableCell = [self tableCellParent];
815 return NSMakeRange(NSNotFound, 0);
817 pair<int, int> columnRange;
818 tableCell->columnIndexRange(columnRange);
819 return NSMakeRange(columnRange.first, columnRange.second);
822 - (NSString *)accessibilityPlaceholderValue
824 if (![self _prepareAccessibilityCall])
827 return m_object->placeholderValue();
830 - (NSString *)accessibilityValue
832 if (![self _prepareAccessibilityCall])
835 // check if the value was overriden
836 NSString *value = [super accessibilityValue];
840 if (m_object->isCheckboxOrRadio()) {
841 switch (m_object->checkboxOrRadioValue()) {
843 return [NSString stringWithFormat:@"%d", 0];
845 return [NSString stringWithFormat:@"%d", 1];
846 case ButtonStateMixed:
847 return [NSString stringWithFormat:@"%d", 2];
849 ASSERT_NOT_REACHED();
850 return [NSString stringWithFormat:@"%d", 0];
853 if (m_object->isButton() && m_object->isPressed())
854 return [NSString stringWithFormat:@"%d", 1];
856 // rdar://8131388 WebKit should expose the same info as UIKit for its password fields.
857 if (m_object->isPasswordField()) {
858 int passwordLength = m_object->accessibilityPasswordFieldLength();
859 NSMutableString* string = [NSMutableString string];
860 for (int k = 0; k < passwordLength; ++k)
861 [string appendString:@"•"];
865 // A text control should return its text data as the axValue (per iPhone AX API).
866 if (![self stringValueShouldBeUsedInLabel])
867 return m_object->stringValue();
869 if (m_object->isProgressIndicator() || m_object->isSlider()) {
870 // Prefer a valueDescription if provided by the author (through aria-valuetext).
871 String valueDescription = m_object->valueDescription();
872 if (!valueDescription.isEmpty())
873 return valueDescription;
875 return [NSString stringWithFormat:@"%.2f", m_object->valueForRange()];
878 if (m_object->isHeading())
879 return [NSString stringWithFormat:@"%d", m_object->headingLevel()];
884 - (BOOL)accessibilityIsComboBox
886 if (![self _prepareAccessibilityCall])
889 return m_object->roleValue() == ComboBoxRole;
892 - (NSString *)accessibilityHint
894 if (![self _prepareAccessibilityCall])
897 return m_object->helpText();
900 - (NSURL *)accessibilityURL
902 if (![self _prepareAccessibilityCall])
905 KURL url = m_object->url();
911 - (CGRect)_convertIntRectToScreenCoordinates:(IntRect)rect
916 CGSize size = CGSizeMake(rect.size().width(), rect.size().height());
917 CGPoint point = CGPointMake(rect.x(), rect.y());
919 CGRect frame = CGRectMake(point.x, point.y, size.width, size.height);
921 FrameView* frameView = m_object->documentFrameView();
923 WAKView* view = frameView->documentView();
924 frame = [view convertRect:frame toView:nil];
927 // we need the web document view to give us our final screen coordinates
928 // because that can take account of the scroller
929 id webDocument = [self _accessibilityWebDocumentView];
931 frame = [webDocument convertRect:frame toView:nil];
936 // Used by UIKit accessibility bundle to help determine distance during a hit-test.
937 - (CGRect)accessibilityElementRect
939 if (![self _prepareAccessibilityCall])
942 LayoutRect rect = m_object->elementRect();
943 return CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
946 // The "center point" is where VoiceOver will "press" an object. This may not be the actual
947 // center of the accessibilityFrame
948 - (CGPoint)accessibilityActivationPoint
950 if (![self _prepareAccessibilityCall])
953 IntRect rect = pixelSnappedIntRect(m_object->boundingBoxRect());
954 CGRect cgRect = [self _convertIntRectToScreenCoordinates:rect];
955 return CGPointMake(CGRectGetMidX(cgRect), CGRectGetMidY(cgRect));
958 - (CGRect)accessibilityFrame
960 if (![self _prepareAccessibilityCall])
963 IntRect rect = pixelSnappedIntRect(m_object->elementRect());
964 return [self _convertIntRectToScreenCoordinates:rect];
967 // Checks whether a link contains only static text and images (and has been divided unnaturally by <spans> and other nefarious mechanisms).
968 - (BOOL)containsUnnaturallySegmentedChildren
973 AccessibilityRole role = m_object->roleValue();
974 if (role != LinkRole && role != WebCoreLinkRole)
977 AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
978 unsigned childrenSize = children.size();
980 // If there's only one child, then it doesn't have segmented children.
981 if (childrenSize == 1)
984 for (unsigned i = 0; i < childrenSize; ++i) {
985 AccessibilityRole role = children[i]->roleValue();
986 if (role != StaticTextRole && role != ImageRole && role != GroupRole)
993 - (id)accessibilityContainer
995 if (![self _prepareAccessibilityCall])
998 // As long as there's a parent wrapper, that's the correct chain to climb.
999 AccessibilityObject* parent = m_object->parentObjectUnignored();
1001 return parent->wrapper();
1003 // The only object without a parent wrapper should be a scroll view.
1004 ASSERT(m_object->isAccessibilityScrollView());
1006 // Verify this is the top document. If not, we might need to go through the platform widget.
1007 FrameView* frameView = m_object->documentFrameView();
1008 Document* document = m_object->document();
1009 if (document && frameView && document != document->topDocument())
1010 return frameView->platformWidget();
1012 // The top scroll view's parent is the web document view.
1013 return [self _accessibilityWebDocumentView];
1016 - (id)accessibilityFocusedUIElement
1018 if (![self _prepareAccessibilityCall])
1021 AccessibilityObject* focusedObj = m_object->focusedUIElement();
1026 return focusedObj->wrapper();
1029 - (id)_accessibilityWebDocumentView
1031 if (![self _prepareAccessibilityCall])
1034 // This method performs the crucial task of connecting to the UIWebDocumentView.
1035 // This is needed to correctly calculate the screen position of the AX object.
1036 static Class webViewClass = nil;
1038 webViewClass = NSClassFromString(@"WebView");
1043 FrameView* frameView = m_object->documentFrameView();
1048 // If this is the top level frame, the UIWebDocumentView should be returned.
1049 id parentView = frameView->documentView();
1050 while (parentView && ![parentView isKindOfClass:webViewClass])
1051 parentView = [parentView superview];
1053 // The parentView should have an accessibilityContainer, if the UIKit accessibility bundle was loaded.
1054 // The exception is DRT, which tests accessibility without the entire system turning accessibility on. Hence,
1055 // this check should be valid for everything except DRT.
1056 ASSERT([parentView accessibilityContainer] || applicationIsDumpRenderTree());
1058 return [parentView accessibilityContainer];
1061 - (NSArray *)_accessibilityNextElementsWithCount:(UInt32)count
1063 if (![self _prepareAccessibilityCall])
1066 return [[self _accessibilityWebDocumentView] _accessibilityNextElementsWithCount:count];
1069 - (NSArray *)_accessibilityPreviousElementsWithCount:(UInt32)count
1071 if (![self _prepareAccessibilityCall])
1074 return [[self _accessibilityWebDocumentView] _accessibilityPreviousElementsWithCount:count];
1077 - (BOOL)accessibilityRequired
1079 if (![self _prepareAccessibilityCall])
1082 return m_object->isRequired();
1085 - (NSArray *)accessibilityFlowToElements
1087 if (![self _prepareAccessibilityCall])
1090 AccessibilityObject::AccessibilityChildrenVector children;
1091 m_object->ariaFlowToElements(children);
1093 unsigned length = children.size();
1094 NSMutableArray* array = [NSMutableArray arrayWithCapacity:length];
1095 for (unsigned i = 0; i < length; ++i) {
1096 AccessibilityObjectWrapper* wrapper = children[i]->wrapper();
1101 if (children[i]->isAttachment() && [wrapper attachmentView])
1102 [array addObject:[wrapper attachmentView]];
1104 [array addObject:wrapper];
1109 - (id)accessibilityLinkedElement
1111 if (![self _prepareAccessibilityCall])
1114 // If this static text inside of a link, it should use its parent's linked element.
1115 AccessibilityObject* element = m_object;
1116 if (m_object->roleValue() == StaticTextRole && m_object->parentObjectUnignored()->isLink())
1117 element = m_object->parentObjectUnignored();
1119 AccessibilityObject::AccessibilityChildrenVector children;
1120 element->linkedUIElements(children);
1121 if (children.size() == 0)
1124 return children[0]->wrapper();
1128 - (BOOL)isAttachment
1133 return m_object->isAttachment();
1136 - (void)_accessibilityActivate
1138 if (![self _prepareAccessibilityCall])
1144 - (id)attachmentView
1146 if (![self _prepareAccessibilityCall])
1149 ASSERT([self isAttachment]);
1150 Widget* widget = m_object->widgetForAttachmentView();
1153 return widget->platformWidget();
1156 static RenderObject* rendererForView(WAKView* view)
1158 if (![view conformsToProtocol:@protocol(WebCoreFrameView)])
1161 WAKView<WebCoreFrameView>* frameView = (WAKView<WebCoreFrameView>*)view;
1162 Frame* frame = [frameView _web_frame];
1166 Node* node = frame->document()->ownerElement();
1170 return node->renderer();
1173 - (id)_accessibilityParentForSubview:(id)subview
1175 RenderObject* renderer = rendererForView(subview);
1179 AccessibilityObject* obj = renderer->document()->axObjectCache()->getOrCreate(renderer);
1181 return obj->parentObjectUnignored()->wrapper();
1185 - (void)postFocusChangeNotification
1187 // The UIKit accessibility wrapper will override and post appropriate notification.
1190 - (void)postSelectedTextChangeNotification
1192 // The UIKit accessibility wrapper will override and post appropriate notification.
1195 - (void)postLayoutChangeNotification
1197 // The UIKit accessibility wrapper will override and post appropriate notification.
1200 - (void)postLiveRegionChangeNotification
1202 // The UIKit accessibility wrapper will override and post appropriate notification.
1205 - (void)postLoadCompleteNotification
1207 // The UIKit accessibility wrapper will override and post appropriate notification.
1210 - (void)postChildrenChangedNotification
1212 // The UIKit accessibility wrapper will override and post appropriate notification.
1215 - (void)postInvalidStatusChangedNotification
1217 // The UIKit accessibility wrapper will override and post appropriate notification.
1220 - (void)accessibilityElementDidBecomeFocused
1222 if (![self _prepareAccessibilityCall])
1225 // The focused VoiceOver element might be the text inside a link.
1226 // In those cases we should focus on the link itself.
1227 for (AccessibilityObject* object = m_object; object != nil; object = object->parentObject()) {
1228 if (object->roleValue() == WebAreaRole)
1231 if (object->canSetFocusAttribute()) {
1232 object->setFocused(true);
1238 - (void)accessibilityModifySelection:(TextGranularity)granularity increase:(BOOL)increase
1240 if (![self _prepareAccessibilityCall])
1243 FrameSelection* frameSelection = m_object->document()->frame()->selection();
1244 VisibleSelection selection = m_object->selection();
1245 VisiblePositionRange range = m_object->visiblePositionRange();
1247 // Before a selection with length exists, the cursor position needs to move to the right starting place.
1248 // That should be the beginning of this element (range.start). However, if the cursor is already within the
1249 // range of this element (the cursor is represented by selection), then the cursor does not need to move.
1250 if (frameSelection->isNone() && (selection.visibleStart() < range.start || selection.visibleEnd() > range.end))
1251 frameSelection->moveTo(range.start, UserTriggered);
1253 frameSelection->modify(FrameSelection::AlterationExtend, (increase) ? DirectionRight : DirectionLeft, granularity, UserTriggered);
1256 - (void)accessibilityIncreaseSelection:(TextGranularity)granularity
1258 [self accessibilityModifySelection:granularity increase:YES];
1261 - (void)accessibilityDecreaseSelection:(TextGranularity)granularity
1263 [self accessibilityModifySelection:granularity increase:NO];
1266 - (void)accessibilityMoveSelectionToMarker:(WebAccessibilityTextMarker *)marker
1268 if (![self _prepareAccessibilityCall])
1271 VisiblePosition visiblePosition = [marker visiblePosition];
1272 if (visiblePosition.isNull())
1275 FrameSelection* frameSelection = m_object->document()->frame()->selection();
1276 frameSelection->moveTo(visiblePosition, UserTriggered);
1279 - (void)accessibilityIncrement
1281 if (![self _prepareAccessibilityCall])
1284 m_object->increment();
1287 - (void)accessibilityDecrement
1289 if (![self _prepareAccessibilityCall])
1292 m_object->decrement();
1295 #pragma mark Accessibility Text Marker Handlers
1297 - (BOOL)_addAccessibilityObject:(AccessibilityObject*)axObject toTextMarkerArray:(NSMutableArray *)array
1302 AccessibilityObjectWrapper* wrapper = axObject->wrapper();
1306 // Don't add the same object twice, but since this has already been added, we should return
1307 // YES because we want to inform that it's in the array
1308 if ([array containsObject:wrapper])
1311 // Explicity set that this is now an element (in case other logic tries to override).
1312 [wrapper setValue:[NSNumber numberWithBool:YES] forKey:@"isAccessibilityElement"];
1313 [array addObject:wrapper];
1317 - (NSString *)stringForTextMarkers:(NSArray *)markers
1319 if (![self _prepareAccessibilityCall])
1322 if ([markers count] != 2)
1325 WebAccessibilityTextMarker* startMarker = [markers objectAtIndex:0];
1326 WebAccessibilityTextMarker* endMarker = [markers objectAtIndex:1];
1327 if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
1330 // extract the start and end VisiblePosition
1331 VisiblePosition startVisiblePosition = [startMarker visiblePosition];
1332 if (startVisiblePosition.isNull())
1335 VisiblePosition endVisiblePosition = [endMarker visiblePosition];
1336 if (endVisiblePosition.isNull())
1339 VisiblePositionRange visiblePosRange = VisiblePositionRange(startVisiblePosition, endVisiblePosition);
1340 return m_object->stringForVisiblePositionRange(visiblePosRange);
1343 static int blockquoteLevel(RenderObject* renderer)
1349 for (Node* node = renderer->node(); node; node = node->parentNode()) {
1350 if (node->hasTagName(blockquoteTag))
1357 static void AXAttributeStringSetBlockquoteLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1359 int quoteLevel = blockquoteLevel(renderer);
1362 [attrString addAttribute:UIAccessibilityTokenBlockquoteLevel value:[NSNumber numberWithInt:quoteLevel] range:range];
1364 [attrString removeAttribute:UIAccessibilityTokenBlockquoteLevel range:range];
1367 static void AXAttributeStringSetHeadingLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1372 AccessibilityObject* parentObject = renderer->document()->axObjectCache()->getOrCreate(renderer->parent());
1373 int parentHeadingLevel = parentObject->headingLevel();
1375 if (parentHeadingLevel)
1376 [attrString addAttribute:UIAccessibilityTokenHeadingLevel value:[NSNumber numberWithInt:parentHeadingLevel] range:range];
1378 [attrString removeAttribute:UIAccessibilityTokenHeadingLevel range:range];
1381 static void AXAttributeStringSetFont(NSMutableAttributedString* attrString, GSFontRef font, NSRange range)
1386 const char* nameCStr = GSFontGetFullName(font);
1387 const char* familyCStr = GSFontGetFamilyName(font);
1388 NSNumber* size = [NSNumber numberWithFloat:GSFontGetSize(font)];
1389 NSNumber* bold = [NSNumber numberWithBool:GSFontIsBold(font)];
1390 GSFontTraitMask traits = GSFontGetTraits(font);
1392 [attrString addAttribute:UIAccessibilityTokenFontName value:[NSString stringWithUTF8String:nameCStr] range:range];
1394 [attrString addAttribute:UIAccessibilityTokenFontFamily value:[NSString stringWithUTF8String:familyCStr] range:range];
1395 if ([size boolValue])
1396 [attrString addAttribute:UIAccessibilityTokenFontSize value:size range:range];
1397 if ([bold boolValue] || (traits & GSBoldFontMask))
1398 [attrString addAttribute:UIAccessibilityTokenBold value:[NSNumber numberWithBool:YES] range:range];
1399 if (traits & GSItalicFontMask)
1400 [attrString addAttribute:UIAccessibilityTokenItalic value:[NSNumber numberWithBool:YES] range:range];
1404 static void AXAttributeStringSetNumber(NSMutableAttributedString* attrString, NSString* attribute, NSNumber* number, NSRange range)
1407 [attrString addAttribute:attribute value:number range:range];
1409 [attrString removeAttribute:attribute range:range];
1412 static void AXAttributeStringSetStyle(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1414 RenderStyle* style = renderer->style();
1416 // set basic font info
1417 AXAttributeStringSetFont(attrString, style->font().primaryFont()->getGSFont(), range);
1419 int decor = style->textDecorationsInEffect();
1420 if ((decor & (UNDERLINE | LINE_THROUGH)) != 0) {
1421 // find colors using quirk mode approach (strict mode would use current
1422 // color for all but the root line box, which would use getTextDecorationColors)
1423 Color underline, overline, linethrough;
1424 renderer->getTextDecorationColors(decor, underline, overline, linethrough);
1426 if (decor & UNDERLINE)
1427 AXAttributeStringSetNumber(attrString, UIAccessibilityTokenUnderline, [NSNumber numberWithBool:YES], range);
1431 static void AXAttributedStringAppendText(NSMutableAttributedString* attrString, Node* node, const UChar* chars, int length)
1433 // skip invisible text
1434 if (!node->renderer())
1437 // easier to calculate the range before appending the string
1438 NSRange attrStringRange = NSMakeRange([attrString length], length);
1440 // append the string from this node
1441 [[attrString mutableString] appendString:[NSString stringWithCharacters:chars length:length]];
1443 // set new attributes
1444 AXAttributeStringSetStyle(attrString, node->renderer(), attrStringRange);
1445 AXAttributeStringSetHeadingLevel(attrString, node->renderer(), attrStringRange);
1446 AXAttributeStringSetBlockquoteLevel(attrString, node->renderer(), attrStringRange);
1450 // This method is intended to return an array of strings and accessibility elements that
1451 // represent the objects on one line of rendered web content. The array of markers sent
1452 // in should be ordered and contain only a start and end marker.
1453 - (NSArray *)arrayOfTextForTextMarkers:(NSArray *)markers attributed:(BOOL)attributed
1455 if (![self _prepareAccessibilityCall])
1458 if ([markers count] != 2)
1461 WebAccessibilityTextMarker* startMarker = [markers objectAtIndex:0];
1462 WebAccessibilityTextMarker* endMarker = [markers objectAtIndex:1];
1463 if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
1466 // extract the start and end VisiblePosition
1467 VisiblePosition startVisiblePosition = [startMarker visiblePosition];
1468 if (startVisiblePosition.isNull())
1471 VisiblePosition endVisiblePosition = [endMarker visiblePosition];
1472 if (endVisiblePosition.isNull())
1475 // iterate over the range to build the AX attributed string
1476 NSMutableArray* array = [[NSMutableArray alloc] init];
1477 TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get());
1478 for (; !it.atEnd(); it.advance()) {
1479 // locate the node and starting offset for this range
1481 Node* node = it.range()->startContainer(exception);
1482 ASSERT(node == it.range()->endContainer(exception));
1483 int offset = it.range()->startOffset(exception);
1485 // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
1486 if (it.length() != 0) {
1488 // First check if this is represented by a link.
1489 AccessibilityObject* linkObject = AccessibilityObject::anchorElementForNode(node);
1490 if ([self _addAccessibilityObject:linkObject toTextMarkerArray:array])
1493 // Next check if this region is represented by a heading.
1494 AccessibilityObject* headingObject = AccessibilityObject::headingElementForNode(node);
1495 if ([self _addAccessibilityObject:headingObject toTextMarkerArray:array])
1498 String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition()));
1500 if (!listMarkerText.isEmpty())
1501 [array addObject:[NSString stringWithCharacters:listMarkerText.characters() length:listMarkerText.length()]];
1502 // There was not an element representation, so just return the text.
1503 [array addObject:[NSString stringWithCharacters:it.characters() length:it.length()]];
1507 String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition()));
1509 if (!listMarkerText.isEmpty()) {
1510 NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init];
1511 AXAttributedStringAppendText(attrString, node, listMarkerText.characters(), listMarkerText.length());
1512 [array addObject:attrString];
1513 [attrString release];
1516 NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
1517 AXAttributedStringAppendText(attrString, node, it.characters(), it.length());
1518 [array addObject:attrString];
1519 [attrString release];
1522 Node* replacedNode = node->childNode(offset);
1524 AccessibilityObject* obj = m_object->axObjectCache()->getOrCreate(replacedNode->renderer());
1525 if (obj && !obj->accessibilityIsIgnored())
1526 [self _addAccessibilityObject:obj toTextMarkerArray:array];
1531 return [array autorelease];
1534 - (NSRange)_convertToNSRange:(Range *)range
1536 if (!range || !range->startContainer())
1537 return NSMakeRange(NSNotFound, 0);
1539 Document* document = m_object->document();
1540 FrameSelection* frameSelection = document->frame()->selection();
1542 Element* selectionRoot = frameSelection->rootEditableElement();
1543 Element* scope = selectionRoot ? selectionRoot : document->documentElement();
1545 // Mouse events may cause TSM to attempt to create an NSRange for a portion of the view
1546 // that is not inside the current editable region. These checks ensure we don't produce
1547 // potentially invalid data when responding to such requests.
1548 if (range->startContainer() != scope && !range->startContainer()->isDescendantOf(scope))
1549 return NSMakeRange(NSNotFound, 0);
1550 if (range->endContainer() != scope && !range->endContainer()->isDescendantOf(scope))
1551 return NSMakeRange(NSNotFound, 0);
1553 RefPtr<Range> testRange = Range::create(scope->document(), scope, 0, range->startContainer(), range->startOffset());
1554 ASSERT(testRange->startContainer() == scope);
1555 int startPosition = TextIterator::rangeLength(testRange.get());
1558 testRange->setEnd(range->endContainer(), range->endOffset(), ec);
1559 ASSERT(testRange->startContainer() == scope);
1560 int endPosition = TextIterator::rangeLength(testRange.get());
1561 return NSMakeRange(startPosition, endPosition - startPosition);
1564 - (PassRefPtr<Range>)_convertToDOMRange:(NSRange)nsrange
1566 if (nsrange.location > INT_MAX)
1568 if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
1569 nsrange.length = INT_MAX - nsrange.location;
1571 // our critical assumption is that we are only called by input methods that
1572 // concentrate on a given area containing the selection
1573 // We have to do this because of text fields and textareas. The DOM for those is not
1574 // directly in the document DOM, so serialization is problematic. Our solution is
1575 // to use the root editable element of the selection start as the positional base.
1576 // That fits with AppKit's idea of an input context.
1577 Document* document = m_object->document();
1578 FrameSelection* frameSelection = document->frame()->selection();
1579 Element* selectionRoot = frameSelection->rootEditableElement();
1580 Element* scope = selectionRoot ? selectionRoot : document->documentElement();
1581 return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length);
1584 // This method is intended to take a text marker representing a VisiblePosition and convert it
1585 // into a normalized location within the document.
1586 - (NSInteger)positionForTextMarker:(WebAccessibilityTextMarker *)marker
1588 if (![self _prepareAccessibilityCall])
1594 VisibleSelection selection([marker visiblePosition]);
1595 RefPtr<Range> range = selection.toNormalizedRange();
1596 NSRange nsRange = [self _convertToNSRange:range.get()];
1597 return nsRange.location;
1600 - (NSArray *)textMarkerRange
1602 if (![self _prepareAccessibilityCall])
1605 VisiblePositionRange range = m_object->visiblePositionRange();
1606 VisiblePosition startPosition = range.start;
1607 VisiblePosition endPosition = range.end;
1608 WebAccessibilityTextMarker* start = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:startPosition cache:m_object->axObjectCache()];
1609 WebAccessibilityTextMarker* end = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:endPosition cache:m_object->axObjectCache()];
1611 return [NSArray arrayWithObjects:start, end, nil];
1614 // A method to get the normalized text cursor range of an element. Used in DumpRenderTree.
1615 - (NSRange)elementTextRange
1617 if (![self _prepareAccessibilityCall])
1618 return NSMakeRange(NSNotFound, 0);
1620 NSArray *markers = [self textMarkerRange];
1621 if ([markers count] != 2)
1622 return NSMakeRange(NSNotFound, 0);
1624 WebAccessibilityTextMarker *startMarker = [markers objectAtIndex:0];
1625 WebAccessibilityTextMarker *endMarker = [markers objectAtIndex:1];
1627 NSInteger startPosition = [self positionForTextMarker:startMarker];
1628 NSInteger endPosition = [self positionForTextMarker:endMarker];
1630 return NSMakeRange(startPosition, endPosition - startPosition);
1633 - (AccessibilityObjectWrapper *)accessibilityObjectForTextMarker:(WebAccessibilityTextMarker *)marker
1635 if (![self _prepareAccessibilityCall])
1641 VisiblePosition visiblePosition = [marker visiblePosition];
1642 AccessibilityObject* obj = m_object->accessibilityObjectForPosition(visiblePosition);
1646 return AccessibilityUnignoredAncestor(obj->wrapper());
1649 - (NSArray *)textMarkerRangeForSelection
1651 if (![self _prepareAccessibilityCall])
1654 VisibleSelection selection = m_object->selection();
1655 if (selection.isNone())
1657 VisiblePosition startPosition = selection.visibleStart();
1658 VisiblePosition endPosition = selection.visibleEnd();
1660 WebAccessibilityTextMarker* startMarker = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:startPosition cache:m_object->axObjectCache()];
1661 WebAccessibilityTextMarker* endMarker = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:endPosition cache:m_object->axObjectCache()];
1662 if (!startMarker || !endMarker)
1665 return [NSArray arrayWithObjects:startMarker, endMarker, nil];
1668 - (WebAccessibilityTextMarker *)textMarkerForPosition:(NSInteger)position
1670 if (![self _prepareAccessibilityCall])
1673 PassRefPtr<Range> range = [self _convertToDOMRange:NSMakeRange(position, 0)];
1677 VisibleSelection selection = VisibleSelection(range.get(), DOWNSTREAM);
1679 VisiblePosition visiblePosition = selection.visibleStart();
1680 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:visiblePosition cache:m_object->axObjectCache()];
1683 - (id)_stringForRange:(NSRange)range attributed:(BOOL)attributed
1685 if (![self _prepareAccessibilityCall])
1688 WebAccessibilityTextMarker* startMarker = [self textMarkerForPosition:range.location];
1689 WebAccessibilityTextMarker* endMarker = [self textMarkerForPosition:NSMaxRange(range)];
1691 // Clients don't always know the exact range, rather than force them to compute it,
1692 // allow clients to overshoot and use the max text marker range.
1693 if (!startMarker || !endMarker) {
1694 NSArray *markers = [self textMarkerRange];
1695 if ([markers count] != 2)
1698 startMarker = [markers objectAtIndex:0];
1700 endMarker = [markers objectAtIndex:1];
1703 NSArray* array = [self arrayOfTextForTextMarkers:[NSArray arrayWithObjects:startMarker, endMarker, nil] attributed:attributed];
1704 Class returnClass = attributed ? [NSMutableAttributedString class] : [NSMutableString class];
1705 id returnValue = [[[returnClass alloc] init] autorelease];
1707 NSInteger count = [array count];
1708 for (NSInteger k = 0; k < count; ++k) {
1709 id object = [array objectAtIndex:k];
1711 if (![object isKindOfClass:returnClass])
1715 [(NSMutableAttributedString *)returnValue appendAttributedString:object];
1717 [(NSMutableString *)returnValue appendString:object];
1723 // A convenience method for getting the text of a NSRange. Currently used only by DRT.
1724 - (NSString *)stringForRange:(NSRange)range
1726 return [self _stringForRange:range attributed:NO];
1729 - (NSAttributedString *)attributedStringForRange:(NSRange)range
1731 return [self _stringForRange:range attributed:YES];
1734 // A convenience method for getting the accessibility objects of a NSRange. Currently used only by DRT.
1735 - (NSArray *)elementsForRange:(NSRange)range
1737 if (![self _prepareAccessibilityCall])
1740 WebAccessibilityTextMarker* startMarker = [self textMarkerForPosition:range.location];
1741 WebAccessibilityTextMarker* endMarker = [self textMarkerForPosition:NSMaxRange(range)];
1742 if (!startMarker || !endMarker)
1745 NSArray* array = [self arrayOfTextForTextMarkers:[NSArray arrayWithObjects:startMarker, endMarker, nil] attributed:NO];
1746 NSMutableArray* elements = [NSMutableArray array];
1747 for (id element in array) {
1748 if (![element isKindOfClass:[AccessibilityObjectWrapper class]])
1750 [elements addObject:element];
1755 - (NSString *)selectionRangeString
1757 NSArray *markers = [self textMarkerRangeForSelection];
1758 return [self stringForTextMarkers:markers];
1761 - (WebAccessibilityTextMarker *)selectedTextMarker
1763 if (![self _prepareAccessibilityCall])
1766 VisibleSelection selection = m_object->selection();
1767 VisiblePosition position = selection.visibleStart();
1769 // if there's no selection, start at the top of the document
1770 if (position.isNull())
1771 position = startOfDocument(m_object->document());
1773 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:position cache:m_object->axObjectCache()];
1776 // This method is intended to return the marker at the end of the line starting at
1777 // the marker that is passed into the method.
1778 - (WebAccessibilityTextMarker *)lineEndMarkerForMarker:(WebAccessibilityTextMarker *)marker
1780 if (![self _prepareAccessibilityCall])
1786 VisiblePosition start = [marker visiblePosition];
1787 VisiblePosition lineEnd = m_object->nextLineEndPosition(start);
1789 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:lineEnd cache:m_object->axObjectCache()];
1792 // This method is intended to return the marker at the start of the line starting at
1793 // the marker that is passed into the method.
1794 - (WebAccessibilityTextMarker *)lineStartMarkerForMarker:(WebAccessibilityTextMarker *)marker
1796 if (![self _prepareAccessibilityCall])
1802 VisiblePosition start = [marker visiblePosition];
1803 VisiblePosition lineStart = m_object->previousLineStartPosition(start);
1805 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:lineStart cache:m_object->axObjectCache()];
1808 - (WebAccessibilityTextMarker *)nextMarkerForMarker:(WebAccessibilityTextMarker *)marker
1810 if (![self _prepareAccessibilityCall])
1816 VisiblePosition start = [marker visiblePosition];
1817 VisiblePosition nextMarker = m_object->nextVisiblePosition(start);
1819 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:nextMarker cache:m_object->axObjectCache()];
1822 // This method is intended to return the marker at the start of the line starting at
1823 // the marker that is passed into the method.
1824 - (WebAccessibilityTextMarker *)previousMarkerForMarker:(WebAccessibilityTextMarker *)marker
1826 if (![self _prepareAccessibilityCall])
1832 VisiblePosition start = [marker visiblePosition];
1833 VisiblePosition previousMarker = m_object->previousVisiblePosition(start);
1835 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:previousMarker cache:m_object->axObjectCache()];
1838 // This method is intended to return the bounds of a text marker range in screen coordinates.
1839 - (CGRect)frameForTextMarkers:(NSArray *)array
1841 if (![self _prepareAccessibilityCall])
1844 if ([array count] != 2)
1847 WebAccessibilityTextMarker* startMarker = [array objectAtIndex:0];
1848 WebAccessibilityTextMarker* endMarker = [array objectAtIndex:1];
1849 if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
1852 IntRect rect = m_object->boundsForVisiblePositionRange(VisiblePositionRange([startMarker visiblePosition], [endMarker visiblePosition]));
1853 return [self _convertIntRectToScreenCoordinates:rect];
1856 - (WebAccessibilityTextMarker *)textMarkerForPoint:(CGPoint)point
1858 if (![self _prepareAccessibilityCall])
1861 VisiblePosition pos = m_object->visiblePositionForPoint(IntPoint(point));
1862 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:pos cache:m_object->axObjectCache()];
1865 - (NSString *)accessibilityIdentifier
1867 if (![self _prepareAccessibilityCall])
1870 return m_object->getAttribute(HTMLNames::idAttr);
1873 - (NSString *)accessibilitySpeechHint
1875 if (![self _prepareAccessibilityCall])
1878 switch (m_object->speakProperty()) {
1885 return @"spell-out";
1888 case SpeakLiteralPunctuation:
1889 return @"literal-punctuation";
1890 case SpeakNoPunctuation:
1891 return @"no-punctuation";
1897 - (BOOL)accessibilityARIAIsBusy
1899 if (![self _prepareAccessibilityCall])
1902 return m_object->ariaLiveRegionBusy();
1905 - (NSString *)accessibilityARIALiveRegionStatus
1907 if (![self _prepareAccessibilityCall])
1910 return m_object->ariaLiveRegionStatus();
1913 - (NSString *)accessibilityARIARelevantStatus
1915 if (![self _prepareAccessibilityCall])
1918 return m_object->ariaLiveRegionRelevant();
1921 - (BOOL)accessibilityARIALiveRegionIsAtomic
1923 if (![self _prepareAccessibilityCall])
1926 return m_object->ariaLiveRegionAtomic();
1929 - (NSString *)accessibilityInvalidStatus
1931 if (![self _prepareAccessibilityCall])
1934 return m_object->invalidStatus();
1937 - (WebAccessibilityObjectWrapper *)accessibilityMathRootIndexObject
1939 return m_object->mathRootIndexObject() ? m_object->mathRootIndexObject()->wrapper() : 0;
1942 - (WebAccessibilityObjectWrapper *)accessibilityMathRadicandObject
1944 return m_object->mathRadicandObject() ? m_object->mathRadicandObject()->wrapper() : 0;
1947 - (WebAccessibilityObjectWrapper *)accessibilityMathNumeratorObject
1949 return m_object->mathNumeratorObject() ? m_object->mathNumeratorObject()->wrapper() : 0;
1952 - (WebAccessibilityObjectWrapper *)accessibilityMathDenominatorObject
1954 return m_object->mathDenominatorObject() ? m_object->mathDenominatorObject()->wrapper() : 0;
1957 - (WebAccessibilityObjectWrapper *)accessibilityMathBaseObject
1959 return m_object->mathBaseObject() ? m_object->mathBaseObject()->wrapper() : 0;
1962 - (WebAccessibilityObjectWrapper *)accessibilityMathSubscriptObject
1964 return m_object->mathSubscriptObject() ? m_object->mathSubscriptObject()->wrapper() : 0;
1967 - (WebAccessibilityObjectWrapper *)accessibilityMathSuperscriptObject
1969 return m_object->mathSuperscriptObject() ? m_object->mathSuperscriptObject()->wrapper() : 0;
1972 - (WebAccessibilityObjectWrapper *)accessibilityMathUnderObject
1974 return m_object->mathUnderObject() ? m_object->mathUnderObject()->wrapper() : 0;
1977 - (WebAccessibilityObjectWrapper *)accessibilityMathOverObject
1979 return m_object->mathOverObject() ? m_object->mathOverObject()->wrapper() : 0;
1982 - (NSString *)accessibilityMathFencedOpenString
1984 return m_object->mathFencedOpenString();
1987 - (NSString *)accessibilityMathFencedCloseString
1989 return m_object->mathFencedCloseString();
1992 - (BOOL)accessibilityIsMathTopObject
1994 return m_object->roleValue() == DocumentMathRole;
1997 - (NSString *)accessibilityMathType
1999 if (m_object->roleValue() == MathElementRole) {
2000 if (m_object->isMathFraction())
2001 return @"AXMathFraction";
2002 if (m_object->isMathFenced())
2003 return @"AXMathFenced";
2004 if (m_object->isMathSubscriptSuperscript())
2005 return @"AXMathSubscriptSuperscript";
2006 if (m_object->isMathRow())
2007 return @"AXMathRow";
2008 if (m_object->isMathUnderOver())
2009 return @"AXMathUnderOver";
2010 if (m_object->isMathSquareRoot())
2011 return @"AXMathSquareRoot";
2012 if (m_object->isMathRoot())
2013 return @"AXMathRoot";
2014 if (m_object->isMathText())
2015 return @"AXMathText";
2016 if (m_object->isMathNumber())
2017 return @"AXMathNumber";
2018 if (m_object->isMathIdentifier())
2019 return @"AXMathIdentifier";
2020 if (m_object->isMathTable())
2021 return @"AXMathTable";
2022 if (m_object->isMathTableRow())
2023 return @"AXMathTableRow";
2024 if (m_object->isMathTableCell())
2025 return @"AXMathTableCell";
2026 if (m_object->isMathFenceOperator())
2027 return @"AXMathFenceOperator";
2028 if (m_object->isMathSeparatorOperator())
2029 return @"AXMathSeparatorOperator";
2030 if (m_object->isMathOperator())
2031 return @"AXMathOperator";
2037 // These are used by DRT so that it can know when notifications are sent.
2038 // Since they are static, only one callback can be installed at a time (that's all DRT should need).
2039 typedef void (*AXPostedNotificationCallback)(id element, NSString* notification, void* context);
2040 static AXPostedNotificationCallback AXNotificationCallback = 0;
2041 static void* AXPostedNotificationContext = 0;
2043 - (void)accessibilitySetPostedNotificationCallback:(AXPostedNotificationCallback)function withContext:(void*)context
2045 AXNotificationCallback = function;
2046 AXPostedNotificationContext = context;
2049 - (void)accessibilityPostedNotification:(NSString *)notificationName
2051 if (AXNotificationCallback && notificationName)
2052 AXNotificationCallback(self, notificationName, AXPostedNotificationContext);
2056 - (NSString *)description
2058 CGRect frame = [self accessibilityFrame];
2059 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];
2065 #endif // HAVE(ACCESSIBILITY) && PLATFORM(IOS)