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"
48 #import "TextIterator.h"
49 #import "WAKScrollView.h"
52 #import "WebCoreThread.h"
53 #import "VisibleUnits.h"
55 #import <GraphicsServices/GraphicsServices.h>
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;
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;
76 @interface WAKView (iOSAccessibility)
77 - (BOOL)accessibilityIsIgnored;
80 using namespace WebCore;
81 using namespace HTMLNames;
83 // These are tokens accessibility uses to denote attributes.
84 static NSString * const UIAccessibilityTokenBlockquoteLevel = @"UIAccessibilityTokenBlockquoteLevel";
85 static NSString * const UIAccessibilityTokenHeadingLevel = @"UIAccessibilityTokenHeadingLevel";
86 static NSString * const UIAccessibilityTokenFontName = @"UIAccessibilityTokenFontName";
87 static NSString * const UIAccessibilityTokenFontFamily = @"UIAccessibilityTokenFontFamily";
88 static NSString * const UIAccessibilityTokenFontSize = @"UIAccessibilityTokenFontSize";
89 static NSString * const UIAccessibilityTokenBold = @"UIAccessibilityTokenBold";
90 static NSString * const UIAccessibilityTokenItalic = @"UIAccessibilityTokenItalic";
91 static NSString * const UIAccessibilityTokenUnderline = @"UIAccessibilityTokenUnderline";
93 static AccessibilityObjectWrapper* AccessibilityUnignoredAncestor(AccessibilityObjectWrapper *wrapper)
95 while (wrapper && ![wrapper isAccessibilityElement]) {
96 AccessibilityObject* object = [wrapper accessibilityObject];
100 if ([wrapper isAttachment] && ![[wrapper attachmentView] accessibilityIsIgnored])
103 AccessibilityObject* parentObject = object->parentObjectUnignored();
107 wrapper = parentObject->wrapper();
112 #pragma mark Accessibility Text Marker
114 @interface WebAccessibilityTextMarker : NSObject
116 AXObjectCache* _cache;
117 TextMarkerData _textMarkerData;
120 + (WebAccessibilityTextMarker *)textMarkerWithVisiblePosition:(VisiblePosition&)visiblePos cache:(AXObjectCache*)cache;
124 @implementation WebAccessibilityTextMarker
126 - (id)initWithTextMarker:(TextMarkerData *)data cache:(AXObjectCache*)cache
128 if (!(self = [super init]))
132 memcpy(&_textMarkerData, data, sizeof(TextMarkerData));
136 - (id)initWithData:(NSData *)data cache:(AXObjectCache*)cache
138 if (!(self = [super init]))
142 [data getBytes:&_textMarkerData length:sizeof(TextMarkerData)];
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
150 WebCore::AccessibilityObject* axObject = [wrapper accessibilityObject];
154 return [self initWithData:data cache:axObject->axObjectCache()];
157 + (WebAccessibilityTextMarker *)textMarkerWithVisiblePosition:(VisiblePosition&)visiblePos cache:(AXObjectCache*)cache
159 TextMarkerData textMarkerData;
160 cache->textMarkerDataForVisiblePosition(textMarkerData, visiblePos);
162 return [[[WebAccessibilityTextMarker alloc] initWithTextMarker:&textMarkerData cache:cache] autorelease];
165 - (NSData *)dataRepresentation
167 return [NSData dataWithBytes:&_textMarkerData length:sizeof(TextMarkerData)];
170 - (VisiblePosition)visiblePosition
172 return _cache->visiblePositionForTextMarkerData(_textMarkerData);
175 - (NSString *)description
177 return [NSString stringWithFormat:@"[AXTextMarker %p] = node: %p offset: %d", self, _textMarkerData.node, _textMarkerData.offset];
182 @implementation WebAccessibilityObjectWrapper
184 - (id)initWithAccessibilityObject:(AccessibilityObject*)axObject
186 self = [super initWithAccessibilityObject:axObject];
190 // Initialize to a sentinel value.
191 m_accessibilityTraitsFromAncestor = ULLONG_MAX;
192 m_isAccessibilityElement = -1;
199 // rdar://8798960 Make sure the object is gone early, so that anything _accessibilityUnregister
200 // does can't call back into the render tree.
203 if ([self respondsToSelector:@selector(_accessibilityUnregister)])
204 [self _accessibilityUnregister];
209 // We should have been detached before deallocated.
214 - (BOOL)_prepareAccessibilityCall
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];
222 // If we came back from our thread lock and we were detached, we will no longer have an m_object.
226 m_object->updateBackingStore();
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.
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); }
259 - (BOOL)accessibilityCanFuzzyHitTest
261 if (![self _prepareAccessibilityCall])
264 AccessibilityRole role = m_object->roleValue();
265 // Elements that can be returned when performing fuzzy hit testing.
270 case DisclosureTriangleRole:
272 case ImageMapLinkRole:
276 case ListBoxOptionRole:
279 case MenuItemCheckboxRole:
280 case MenuItemRadioRole:
281 case PopUpButtonRole:
282 case RadioButtonRole:
288 return !m_object->accessibilityIsIgnored();
294 - (AccessibilityObjectWrapper *)accessibilityPostProcessHitTest:(CGPoint)point
297 // The UIKit accessibility wrapper will override this and perform the post process hit test.
301 - (id)accessibilityHitTest:(CGPoint)point
303 if (![self _prepareAccessibilityCall])
306 // Try a fuzzy hit test first to find an accessible element.
307 RefPtr<AccessibilityObject> axObject;
309 AXAttributeCacheEnabler enableCache(m_object->axObjectCache());
310 axObject = m_object->accessibilityHitTest(IntPoint(point));
316 // If this is a good accessible object to return, no extra work is required.
317 if ([axObject->wrapper() accessibilityCanFuzzyHitTest])
318 return AccessibilityUnignoredAncestor(axObject->wrapper());
320 // Check to see if we can post-process this hit test to find a better candidate.
321 AccessibilityObjectWrapper* wrapper = [axObject->wrapper() accessibilityPostProcessHitTest:point];
323 return AccessibilityUnignoredAncestor(wrapper);
325 // Fall back to default behavior.
326 return AccessibilityUnignoredAncestor(axObject->wrapper());
329 - (NSInteger)accessibilityElementCount
331 if (![self _prepareAccessibilityCall])
334 AXAttributeCacheEnabler enableCache(m_object->axObjectCache());
335 if ([self isAttachment])
336 return [[self attachmentView] accessibilityElementCount];
338 return m_object->children().size();
341 - (id)accessibilityElementAtIndex:(NSInteger)index
343 if (![self _prepareAccessibilityCall])
346 AXAttributeCacheEnabler enableCache(m_object->axObjectCache());
347 if ([self isAttachment])
348 return [[self attachmentView] accessibilityElementAtIndex:index];
350 AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
351 if (static_cast<unsigned>(index) >= children.size())
354 AccessibilityObjectWrapper* wrapper = children[index]->wrapper();
355 if (children[index]->isAttachment())
356 return [wrapper attachmentView];
361 - (NSInteger)indexOfAccessibilityElement:(id)element
363 if (![self _prepareAccessibilityCall])
366 AXAttributeCacheEnabler enableCache(m_object->axObjectCache());
367 if ([self isAttachment])
368 return [[self attachmentView] indexOfAccessibilityElement:element];
370 AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
371 unsigned count = children.size();
372 for (unsigned k = 0; k < count; ++k) {
373 AccessibilityObjectWrapper* wrapper = children[k]->wrapper();
374 if (wrapper == element || (children[k]->isAttachment() && [wrapper attachmentView] == element))
381 - (CGPathRef)_accessibilityPath
383 if (![self _prepareAccessibilityCall])
386 if (!m_object->supportsPath())
389 Path path = m_object->elementPath();
393 return [self convertPathToScreenSpace:path];
396 - (NSString *)accessibilityLanguage
398 if (![self _prepareAccessibilityCall])
401 return m_object->language();
404 - (BOOL)_accessibilityIsLandmarkRole:(AccessibilityRole)role
407 case LandmarkApplicationRole:
408 case LandmarkBannerRole:
409 case LandmarkComplementaryRole:
410 case LandmarkContentInfoRole:
411 case LandmarkMainRole:
412 case LandmarkNavigationRole:
413 case LandmarkSearchRole:
420 - (AccessibilityObjectWrapper*)_accessibilityListAncestor
422 for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
423 AccessibilityRole role = parent->roleValue();
424 if (role == ListRole || role == ListBoxRole)
425 return parent->wrapper();
431 - (AccessibilityObjectWrapper*)_accessibilityLandmarkAncestor
433 for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
434 if ([self _accessibilityIsLandmarkRole:parent->roleValue()])
435 return parent->wrapper();
441 - (AccessibilityObjectWrapper*)_accessibilityTableAncestor
443 for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
444 if (parent->roleValue() == TableRole)
445 return parent->wrapper();
451 - (uint64_t)_accessibilityTraitsFromAncestors
454 AccessibilityRole role = m_object->roleValue();
456 // Trait information also needs to be gathered from the parents above the object.
457 // The parentObject is needed instead of the unignoredParentObject, because a table might be ignored, but information still needs to be gathered from it.
458 for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
459 AccessibilityRole parentRole = parent->roleValue();
460 if (parentRole == WebAreaRole)
463 switch (parentRole) {
465 case WebCoreLinkRole:
466 traits |= [self _axLinkTrait];
467 if (parent->isVisited())
468 traits |= [self _axVisitedTrait];
472 traits |= [self _axHeaderTrait];
473 // If this object has the header trait, we should set the value
474 // to the heading level. If it was a static text element, we need to store
475 // the value as the label, because the heading level needs to the value.
476 AccessibilityObjectWrapper* wrapper = parent->wrapper();
477 if (role == StaticTextRole)
478 [self setAccessibilityLabel:m_object->stringValue()];
479 [self setAccessibilityValue:[wrapper accessibilityValue]];
484 traits |= [self _axContainedByListTrait];
487 traits |= [self _axContainedByTableTrait];
490 if ([self _accessibilityIsLandmarkRole:parentRole])
491 traits |= [self _axContainedByLandmarkTrait];
499 - (uint64_t)accessibilityTraits
501 if (![self _prepareAccessibilityCall])
504 AccessibilityRole role = m_object->roleValue();
505 uint64_t traits = [self _axWebContentTrait];
508 case WebCoreLinkRole:
509 traits |= [self _axLinkTrait];
510 if (m_object->isVisited())
511 traits |= [self _axVisitedTrait];
513 // TextFieldRole is intended to fall through to TextAreaRole, in order to pick up the text entry and text cursor traits.
515 if (m_object->isPasswordField())
516 traits |= [self _axSecureTextFieldTrait];
518 traits |= [self _axTextEntryTrait];
519 if (m_object->isFocused())
520 traits |= ([self _axHasTextCursorTrait] | [self _axTextOperationsAvailableTrait]);
523 traits |= [self _axImageTrait];
526 traits |= [self _axTabButtonTrait];
529 traits |= [self _axButtonTrait];
530 if (m_object->isPressed())
531 traits |= [self _axToggleTrait];
533 case PopUpButtonRole:
534 traits |= [self _axPopupButtonTrait];
536 case RadioButtonRole:
537 traits |= [self _axRadioButtonTrait] | [self _axToggleTrait];
540 traits |= ([self _axButtonTrait] | [self _axToggleTrait]);
543 traits |= [self _axHeaderTrait];
546 traits |= [self _axStaticTextTrait];
549 traits |= [self _axAdjustableTrait];
553 case MenuItemCheckboxRole:
554 case MenuItemRadioRole:
555 traits |= [self _axMenuItemTrait];
561 if (m_object->isSelected())
562 traits |= [self _axSelectedTrait];
564 if (!m_object->isEnabled())
565 traits |= [self _axNotEnabledTrait];
567 if (m_accessibilityTraitsFromAncestor == ULLONG_MAX)
568 m_accessibilityTraitsFromAncestor = [self _accessibilityTraitsFromAncestors];
570 traits |= m_accessibilityTraitsFromAncestor;
575 - (BOOL)isSVGGroupElement
577 // If an SVG group element has a title, it should be an accessible element on iOS.
579 Node* node = m_object->node();
580 if (node && node->hasTagName(SVGNames::gTag) && [[self accessibilityLabel] length] > 0)
587 - (BOOL)determineIsAccessibilityElement
592 // Honor when something explicitly makes this an element (super will contain that logic)
593 if ([super isAccessibilityElement])
596 m_object->updateBackingStore();
598 switch (m_object->roleValue()) {
602 case PopUpButtonRole:
604 case RadioButtonRole:
607 case ValueIndicatorRole:
609 case ProgressIndicatorRole:
611 case IncrementorRole:
613 case DisclosureTriangleRole:
616 case ListBoxOptionRole:
618 case DocumentMathRole:
622 // Many text elements only contain a space.
623 if (![[[self accessibilityLabel] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length])
626 // Text elements that are just pieces of links or headers should not be exposed.
627 if ([AccessibilityUnignoredAncestor([self accessibilityContainer]) containsUnnaturallySegmentedChildren])
632 // Don't expose headers as elements; instead expose their children as elements, with the header trait (unless they have no children)
634 if (![self accessibilityElementCount])
638 // Links can sometimes be elements (when they only contain static text or don't contain anything).
639 // They should not be elements when containing text and other types.
640 case WebCoreLinkRole:
642 if ([self containsUnnaturallySegmentedChildren] || ![self accessibilityElementCount])
646 if ([self isSVGGroupElement])
648 // All other elements are ignored on the iphone.
654 case ApplicationRole:
664 case BusyIndicatorRole:
678 case RulerMarkerRole:
685 - (BOOL)isAccessibilityElement
687 if (![self _prepareAccessibilityCall])
690 if (m_isAccessibilityElement == -1)
691 m_isAccessibilityElement = [self determineIsAccessibilityElement];
693 return m_isAccessibilityElement;
696 - (BOOL)stringValueShouldBeUsedInLabel
698 if (m_object->isTextControl())
700 if (m_object->roleValue() == PopUpButtonRole)
702 if (m_object->isFileUploadButton())
708 - (BOOL)fileUploadButtonReturnsValueInTitle
713 static void appendStringToResult(NSMutableString *result, NSString *string)
716 if (![string length])
719 [result appendString:@", "];
720 [result appendString:string];
723 - (CGFloat)_accessibilityMinValue
725 return m_object->minValueForRange();
728 - (CGFloat)_accessibilityMaxValue
730 return m_object->maxValueForRange();
733 - (NSString *)accessibilityLabel
735 if (![self _prepareAccessibilityCall])
738 // check if the label was overriden
739 NSString *label = [super accessibilityLabel];
743 // iOS doesn't distinguish between a title and description field,
744 // so concatentation will yield the best result.
745 NSString *axTitle = [self accessibilityTitle];
746 NSString *axDescription = [self accessibilityDescription];
747 NSString *landmarkDescription = [self ariaLandmarkRoleDescription];
749 NSMutableString *result = [NSMutableString string];
751 appendStringToResult(result, axTitle);
752 appendStringToResult(result, axDescription);
753 if ([self stringValueShouldBeUsedInLabel]) {
754 NSString *valueLabel = m_object->stringValue();
755 valueLabel = [valueLabel stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
756 appendStringToResult(result, valueLabel);
758 appendStringToResult(result, landmarkDescription);
760 return [result length] ? result : nil;
763 - (AccessibilityTableCell*)tableCellParent
765 // Find if this element is in a table cell.
766 AccessibilityObject* cell = 0;
767 for (cell = m_object; cell && !cell->isTableCell(); cell = cell->parentObject())
773 return static_cast<AccessibilityTableCell*>(cell);
776 - (AccessibilityTable*)tableParent
778 // Find if the parent table for the table cell.
779 AccessibilityObject* parentTable = 0;
780 for (parentTable = m_object; parentTable && !parentTable->isDataTable(); parentTable = parentTable->parentObject())
786 return static_cast<AccessibilityTable*>(parentTable);
789 - (id)accessibilityTitleElement
791 if (![self _prepareAccessibilityCall])
794 AccessibilityObject* titleElement = m_object->titleUIElement();
796 return titleElement->wrapper();
801 // Meant to return row or column headers (or other things as the future permits).
802 - (NSArray *)accessibilityHeaderElements
804 if (![self _prepareAccessibilityCall])
807 AccessibilityTableCell* tableCell = [self tableCellParent];
811 AccessibilityTable* table = [self tableParent];
815 // Get the row and column range, so we can use them to find the headers.
816 pair<unsigned, unsigned> rowRange;
817 pair<unsigned, unsigned> columnRange;
818 tableCell->rowIndexRange(rowRange);
819 tableCell->columnIndexRange(columnRange);
821 AccessibilityObject::AccessibilityChildrenVector rowHeaders;
822 AccessibilityObject::AccessibilityChildrenVector columnHeaders;
823 table->rowHeaders(rowHeaders);
824 table->columnHeaders(columnHeaders);
826 NSMutableArray *headers = [NSMutableArray array];
828 unsigned columnRangeIndex = static_cast<unsigned>(columnRange.first);
829 if (columnRangeIndex < columnHeaders.size()) {
830 RefPtr<AccessibilityObject> columnHeader = columnHeaders[columnRange.first];
831 AccessibilityObjectWrapper* wrapper = columnHeader->wrapper();
833 [headers addObject:wrapper];
836 unsigned rowRangeIndex = static_cast<unsigned>(rowRange.first);
837 if (rowRangeIndex < rowHeaders.size()) {
838 RefPtr<AccessibilityObject> rowHeader = rowHeaders[rowRange.first];
839 AccessibilityObjectWrapper* wrapper = rowHeader->wrapper();
841 [headers addObject:wrapper];
847 - (id)accessibilityElementForRow:(NSInteger)row andColumn:(NSInteger)column
849 if (![self _prepareAccessibilityCall])
852 AccessibilityTable* table = [self tableParent];
856 AccessibilityTableCell* cell = table->cellForColumnAndRow(column, row);
859 return cell->wrapper();
862 - (NSRange)accessibilityRowRange
864 if (![self _prepareAccessibilityCall])
865 return NSMakeRange(NSNotFound, 0);
867 if (m_object->isRadioButton()) {
868 AccessibilityObject::AccessibilityChildrenVector radioButtonSiblings;
869 m_object->linkedUIElements(radioButtonSiblings);
870 if (radioButtonSiblings.size() <= 1)
871 return NSMakeRange(NSNotFound, 0);
873 return NSMakeRange(radioButtonSiblings.find(m_object), radioButtonSiblings.size());
876 AccessibilityTableCell* tableCell = [self tableCellParent];
878 return NSMakeRange(NSNotFound, 0);
880 pair<unsigned, unsigned> rowRange;
881 tableCell->rowIndexRange(rowRange);
882 return NSMakeRange(rowRange.first, rowRange.second);
885 - (NSRange)accessibilityColumnRange
887 if (![self _prepareAccessibilityCall])
888 return NSMakeRange(NSNotFound, 0);
890 AccessibilityTableCell* tableCell = [self tableCellParent];
892 return NSMakeRange(NSNotFound, 0);
894 pair<unsigned, unsigned> columnRange;
895 tableCell->columnIndexRange(columnRange);
896 return NSMakeRange(columnRange.first, columnRange.second);
899 - (NSString *)accessibilityPlaceholderValue
901 if (![self _prepareAccessibilityCall])
904 return m_object->placeholderValue();
907 - (NSString *)accessibilityValue
909 if (![self _prepareAccessibilityCall])
912 // check if the value was overriden
913 NSString *value = [super accessibilityValue];
917 if (m_object->isCheckboxOrRadio()) {
918 switch (m_object->checkboxOrRadioValue()) {
920 return [NSString stringWithFormat:@"%d", 0];
922 return [NSString stringWithFormat:@"%d", 1];
923 case ButtonStateMixed:
924 return [NSString stringWithFormat:@"%d", 2];
926 ASSERT_NOT_REACHED();
927 return [NSString stringWithFormat:@"%d", 0];
930 if (m_object->isButton() && m_object->isPressed())
931 return [NSString stringWithFormat:@"%d", 1];
933 // rdar://8131388 WebKit should expose the same info as UIKit for its password fields.
934 if (m_object->isPasswordField()) {
935 int passwordLength = m_object->accessibilityPasswordFieldLength();
936 NSMutableString* string = [NSMutableString string];
937 for (int k = 0; k < passwordLength; ++k)
938 [string appendString:@"•"];
942 // A text control should return its text data as the axValue (per iPhone AX API).
943 if (![self stringValueShouldBeUsedInLabel])
944 return m_object->stringValue();
946 if (m_object->isProgressIndicator() || m_object->isSlider()) {
947 // Prefer a valueDescription if provided by the author (through aria-valuetext).
948 String valueDescription = m_object->valueDescription();
949 if (!valueDescription.isEmpty())
950 return valueDescription;
952 return [NSString stringWithFormat:@"%.2f", m_object->valueForRange()];
955 if (m_object->isHeading())
956 return [NSString stringWithFormat:@"%d", m_object->headingLevel()];
961 - (BOOL)accessibilityIsComboBox
963 if (![self _prepareAccessibilityCall])
966 return m_object->roleValue() == ComboBoxRole;
969 - (NSString *)accessibilityHint
971 if (![self _prepareAccessibilityCall])
974 return [self accessibilityHelpText];
977 - (NSURL *)accessibilityURL
979 if (![self _prepareAccessibilityCall])
982 URL url = m_object->url();
988 - (CGPoint)convertPointToScreenSpace:(FloatPoint &)point
993 CGPoint cgPoint = CGPointMake(point.x(), point.y());
995 FrameView* frameView = m_object->documentFrameView();
997 WAKView* view = frameView->documentView();
998 cgPoint = [view convertPoint:cgPoint toView:nil];
1001 // we need the web document view to give us our final screen coordinates
1002 // because that can take account of the scroller
1003 id webDocument = [self _accessibilityWebDocumentView];
1005 cgPoint = [webDocument convertPoint:cgPoint toView:nil];
1010 - (CGRect)convertRectToScreenSpace:(IntRect &)rect
1015 CGSize size = CGSizeMake(rect.size().width(), rect.size().height());
1016 CGPoint point = CGPointMake(rect.x(), rect.y());
1018 CGRect frame = CGRectMake(point.x, point.y, size.width, size.height);
1020 FrameView* frameView = m_object->documentFrameView();
1022 WAKView* view = frameView->documentView();
1023 frame = [view convertRect:frame toView:nil];
1026 // we need the web document view to give us our final screen coordinates
1027 // because that can take account of the scroller
1028 id webDocument = [self _accessibilityWebDocumentView];
1030 frame = [webDocument convertRect:frame toView:nil];
1035 // Used by UIKit accessibility bundle to help determine distance during a hit-test.
1036 - (CGRect)accessibilityElementRect
1038 if (![self _prepareAccessibilityCall])
1041 LayoutRect rect = m_object->elementRect();
1042 return CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
1045 // The "center point" is where VoiceOver will "press" an object. This may not be the actual
1046 // center of the accessibilityFrame
1047 - (CGPoint)accessibilityActivationPoint
1049 if (![self _prepareAccessibilityCall])
1052 IntRect rect = pixelSnappedIntRect(m_object->boundingBoxRect());
1053 CGRect cgRect = [self convertRectToScreenSpace:rect];
1054 return CGPointMake(CGRectGetMidX(cgRect), CGRectGetMidY(cgRect));
1057 - (CGRect)accessibilityFrame
1059 if (![self _prepareAccessibilityCall])
1062 IntRect rect = pixelSnappedIntRect(m_object->elementRect());
1063 return [self convertRectToScreenSpace:rect];
1066 // Checks whether a link contains only static text and images (and has been divided unnaturally by <spans> and other nefarious mechanisms).
1067 - (BOOL)containsUnnaturallySegmentedChildren
1072 AccessibilityRole role = m_object->roleValue();
1073 if (role != LinkRole && role != WebCoreLinkRole)
1076 AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
1077 unsigned childrenSize = children.size();
1079 // If there's only one child, then it doesn't have segmented children.
1080 if (childrenSize == 1)
1083 for (unsigned i = 0; i < childrenSize; ++i) {
1084 AccessibilityRole role = children[i]->roleValue();
1085 if (role != StaticTextRole && role != ImageRole && role != GroupRole)
1092 - (id)accessibilityContainer
1094 if (![self _prepareAccessibilityCall])
1097 AXAttributeCacheEnabler enableCache(m_object->axObjectCache());
1099 // As long as there's a parent wrapper, that's the correct chain to climb.
1100 AccessibilityObject* parent = m_object->parentObjectUnignored();
1102 return parent->wrapper();
1104 // The only object without a parent wrapper should be a scroll view.
1105 ASSERT(m_object->isAccessibilityScrollView());
1107 // Verify this is the top document. If not, we might need to go through the platform widget.
1108 FrameView* frameView = m_object->documentFrameView();
1109 Document* document = m_object->document();
1110 if (document && frameView && document != document->topDocument())
1111 return frameView->platformWidget();
1113 // The top scroll view's parent is the web document view.
1114 return [self _accessibilityWebDocumentView];
1117 - (id)accessibilityFocusedUIElement
1119 if (![self _prepareAccessibilityCall])
1122 AccessibilityObject* focusedObj = m_object->focusedUIElement();
1127 return focusedObj->wrapper();
1130 - (id)_accessibilityWebDocumentView
1132 if (![self _prepareAccessibilityCall])
1135 // This method performs the crucial task of connecting to the UIWebDocumentView.
1136 // This is needed to correctly calculate the screen position of the AX object.
1137 static Class webViewClass = nil;
1139 webViewClass = NSClassFromString(@"WebView");
1144 FrameView* frameView = m_object->documentFrameView();
1149 // If this is the top level frame, the UIWebDocumentView should be returned.
1150 id parentView = frameView->documentView();
1151 while (parentView && ![parentView isKindOfClass:webViewClass])
1152 parentView = [parentView superview];
1154 // The parentView should have an accessibilityContainer, if the UIKit accessibility bundle was loaded.
1155 // The exception is DRT, which tests accessibility without the entire system turning accessibility on. Hence,
1156 // this check should be valid for everything except DRT.
1157 ASSERT([parentView accessibilityContainer] || applicationIsDumpRenderTree());
1159 return [parentView accessibilityContainer];
1162 - (NSArray *)_accessibilityNextElementsWithCount:(UInt32)count
1164 if (![self _prepareAccessibilityCall])
1167 return [[self _accessibilityWebDocumentView] _accessibilityNextElementsWithCount:count];
1170 - (NSArray *)_accessibilityPreviousElementsWithCount:(UInt32)count
1172 if (![self _prepareAccessibilityCall])
1175 return [[self _accessibilityWebDocumentView] _accessibilityPreviousElementsWithCount:count];
1178 - (BOOL)accessibilityRequired
1180 if (![self _prepareAccessibilityCall])
1183 return m_object->isRequired();
1186 - (NSArray *)accessibilityFlowToElements
1188 if (![self _prepareAccessibilityCall])
1191 AccessibilityObject::AccessibilityChildrenVector children;
1192 m_object->ariaFlowToElements(children);
1194 unsigned length = children.size();
1195 NSMutableArray* array = [NSMutableArray arrayWithCapacity:length];
1196 for (unsigned i = 0; i < length; ++i) {
1197 AccessibilityObjectWrapper* wrapper = children[i]->wrapper();
1202 if (children[i]->isAttachment() && [wrapper attachmentView])
1203 [array addObject:[wrapper attachmentView]];
1205 [array addObject:wrapper];
1210 - (id)accessibilityLinkedElement
1212 if (![self _prepareAccessibilityCall])
1215 // If this static text inside of a link, it should use its parent's linked element.
1216 AccessibilityObject* element = m_object;
1217 if (m_object->roleValue() == StaticTextRole && m_object->parentObjectUnignored()->isLink())
1218 element = m_object->parentObjectUnignored();
1220 AccessibilityObject::AccessibilityChildrenVector children;
1221 element->linkedUIElements(children);
1222 if (children.size() == 0)
1225 return children[0]->wrapper();
1229 - (BOOL)isAttachment
1234 return m_object->isAttachment();
1237 - (void)_accessibilityActivate
1239 if (![self _prepareAccessibilityCall])
1245 - (id)attachmentView
1247 if (![self _prepareAccessibilityCall])
1250 ASSERT([self isAttachment]);
1251 Widget* widget = m_object->widgetForAttachmentView();
1254 return widget->platformWidget();
1257 static RenderObject* rendererForView(WAKView* view)
1259 if (![view conformsToProtocol:@protocol(WebCoreFrameView)])
1262 WAKView<WebCoreFrameView>* frameView = (WAKView<WebCoreFrameView>*)view;
1263 Frame* frame = [frameView _web_frame];
1267 Node* node = frame->document()->ownerElement();
1271 return node->renderer();
1274 - (id)_accessibilityParentForSubview:(id)subview
1276 RenderObject* renderer = rendererForView(subview);
1280 AccessibilityObject* obj = renderer->document().axObjectCache()->getOrCreate(renderer);
1282 return obj->parentObjectUnignored()->wrapper();
1286 - (void)postFocusChangeNotification
1288 // The UIKit accessibility wrapper will override and post appropriate notification.
1291 - (void)postSelectedTextChangeNotification
1293 // The UIKit accessibility wrapper will override and post appropriate notification.
1296 - (void)postLayoutChangeNotification
1298 // The UIKit accessibility wrapper will override and post appropriate notification.
1301 - (void)postLiveRegionChangeNotification
1303 // The UIKit accessibility wrapper will override and post appropriate notification.
1306 - (void)postLoadCompleteNotification
1308 // The UIKit accessibility wrapper will override and post appropriate notification.
1311 - (void)postChildrenChangedNotification
1313 // The UIKit accessibility wrapper will override and post appropriate notification.
1316 - (void)postInvalidStatusChangedNotification
1318 // The UIKit accessibility wrapper will override and post appropriate notification.
1321 - (void)accessibilityElementDidBecomeFocused
1323 if (![self _prepareAccessibilityCall])
1326 // The focused VoiceOver element might be the text inside a link.
1327 // In those cases we should focus on the link itself.
1328 for (AccessibilityObject* object = m_object; object != nil; object = object->parentObject()) {
1329 if (object->roleValue() == WebAreaRole)
1332 if (object->canSetFocusAttribute()) {
1333 object->setFocused(true);
1339 - (void)accessibilityModifySelection:(TextGranularity)granularity increase:(BOOL)increase
1341 if (![self _prepareAccessibilityCall])
1344 FrameSelection& frameSelection = m_object->document()->frame()->selection();
1345 VisibleSelection selection = m_object->selection();
1346 VisiblePositionRange range = m_object->visiblePositionRange();
1348 // Before a selection with length exists, the cursor position needs to move to the right starting place.
1349 // That should be the beginning of this element (range.start). However, if the cursor is already within the
1350 // range of this element (the cursor is represented by selection), then the cursor does not need to move.
1351 if (frameSelection.isNone() && (selection.visibleStart() < range.start || selection.visibleEnd() > range.end))
1352 frameSelection.moveTo(range.start, UserTriggered);
1354 frameSelection.modify(FrameSelection::AlterationExtend, (increase) ? DirectionRight : DirectionLeft, granularity, UserTriggered);
1357 - (void)accessibilityIncreaseSelection:(TextGranularity)granularity
1359 [self accessibilityModifySelection:granularity increase:YES];
1362 - (void)accessibilityDecreaseSelection:(TextGranularity)granularity
1364 [self accessibilityModifySelection:granularity increase:NO];
1367 - (void)accessibilityMoveSelectionToMarker:(WebAccessibilityTextMarker *)marker
1369 if (![self _prepareAccessibilityCall])
1372 VisiblePosition visiblePosition = [marker visiblePosition];
1373 if (visiblePosition.isNull())
1376 FrameSelection& frameSelection = m_object->document()->frame()->selection();
1377 frameSelection.moveTo(visiblePosition, UserTriggered);
1380 - (void)accessibilityIncrement
1382 if (![self _prepareAccessibilityCall])
1385 m_object->increment();
1388 - (void)accessibilityDecrement
1390 if (![self _prepareAccessibilityCall])
1393 m_object->decrement();
1396 #pragma mark Accessibility Text Marker Handlers
1398 - (BOOL)_addAccessibilityObject:(AccessibilityObject*)axObject toTextMarkerArray:(NSMutableArray *)array
1403 AccessibilityObjectWrapper* wrapper = axObject->wrapper();
1407 // Don't add the same object twice, but since this has already been added, we should return
1408 // YES because we want to inform that it's in the array
1409 if ([array containsObject:wrapper])
1412 // Explicity set that this is now an element (in case other logic tries to override).
1413 [wrapper setValue:[NSNumber numberWithBool:YES] forKey:@"isAccessibilityElement"];
1414 [array addObject:wrapper];
1418 - (NSString *)stringForTextMarkers:(NSArray *)markers
1420 if (![self _prepareAccessibilityCall])
1423 if ([markers count] != 2)
1426 WebAccessibilityTextMarker* startMarker = [markers objectAtIndex:0];
1427 WebAccessibilityTextMarker* endMarker = [markers objectAtIndex:1];
1428 if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
1431 // extract the start and end VisiblePosition
1432 VisiblePosition startVisiblePosition = [startMarker visiblePosition];
1433 if (startVisiblePosition.isNull())
1436 VisiblePosition endVisiblePosition = [endMarker visiblePosition];
1437 if (endVisiblePosition.isNull())
1440 VisiblePositionRange visiblePosRange = VisiblePositionRange(startVisiblePosition, endVisiblePosition);
1441 return m_object->stringForVisiblePositionRange(visiblePosRange);
1444 static int blockquoteLevel(RenderObject* renderer)
1450 for (Node* node = renderer->node(); node; node = node->parentNode()) {
1451 if (node->hasTagName(blockquoteTag))
1458 static void AXAttributeStringSetBlockquoteLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1460 int quoteLevel = blockquoteLevel(renderer);
1463 [attrString addAttribute:UIAccessibilityTokenBlockquoteLevel value:[NSNumber numberWithInt:quoteLevel] range:range];
1465 [attrString removeAttribute:UIAccessibilityTokenBlockquoteLevel range:range];
1468 static void AXAttributeStringSetHeadingLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1473 AccessibilityObject* parentObject = renderer->document().axObjectCache()->getOrCreate(renderer->parent());
1474 int parentHeadingLevel = parentObject->headingLevel();
1476 if (parentHeadingLevel)
1477 [attrString addAttribute:UIAccessibilityTokenHeadingLevel value:[NSNumber numberWithInt:parentHeadingLevel] range:range];
1479 [attrString removeAttribute:UIAccessibilityTokenHeadingLevel range:range];
1482 static void AXAttributeStringSetFont(NSMutableAttributedString* attrString, GSFontRef font, NSRange range)
1487 const char* nameCStr = GSFontGetFullName(font);
1488 const char* familyCStr = GSFontGetFamilyName(font);
1489 NSNumber* size = [NSNumber numberWithFloat:GSFontGetSize(font)];
1490 NSNumber* bold = [NSNumber numberWithBool:GSFontIsBold(font)];
1491 GSFontTraitMask traits = GSFontGetTraits(font);
1493 [attrString addAttribute:UIAccessibilityTokenFontName value:[NSString stringWithUTF8String:nameCStr] range:range];
1495 [attrString addAttribute:UIAccessibilityTokenFontFamily value:[NSString stringWithUTF8String:familyCStr] range:range];
1496 if ([size boolValue])
1497 [attrString addAttribute:UIAccessibilityTokenFontSize value:size range:range];
1498 if ([bold boolValue] || (traits & GSBoldFontMask))
1499 [attrString addAttribute:UIAccessibilityTokenBold value:[NSNumber numberWithBool:YES] range:range];
1500 if (traits & GSItalicFontMask)
1501 [attrString addAttribute:UIAccessibilityTokenItalic value:[NSNumber numberWithBool:YES] range:range];
1505 static void AXAttributeStringSetNumber(NSMutableAttributedString* attrString, NSString* attribute, NSNumber* number, NSRange range)
1508 [attrString addAttribute:attribute value:number range:range];
1510 [attrString removeAttribute:attribute range:range];
1513 static void AXAttributeStringSetStyle(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1515 RenderStyle* style = renderer->style();
1517 // set basic font info
1518 AXAttributeStringSetFont(attrString, style->font().primaryFont()->getGSFont(), range);
1520 int decor = style->textDecorationsInEffect();
1521 if ((decor & (TextDecorationUnderline | TextDecorationLineThrough)) != 0) {
1522 // find colors using quirk mode approach (strict mode would use current
1523 // color for all but the root line box, which would use getTextDecorationColors)
1524 Color underline, overline, linethrough;
1525 renderer->getTextDecorationColors(decor, underline, overline, linethrough);
1527 if (decor & TextDecorationUnderline)
1528 AXAttributeStringSetNumber(attrString, UIAccessibilityTokenUnderline, [NSNumber numberWithBool:YES], range);
1532 static void AXAttributedStringAppendText(NSMutableAttributedString* attrString, Node* node, const UChar* chars, int length)
1534 // skip invisible text
1535 if (!node->renderer())
1538 // easier to calculate the range before appending the string
1539 NSRange attrStringRange = NSMakeRange([attrString length], length);
1541 // append the string from this node
1542 [[attrString mutableString] appendString:[NSString stringWithCharacters:chars length:length]];
1544 // set new attributes
1545 AXAttributeStringSetStyle(attrString, node->renderer(), attrStringRange);
1546 AXAttributeStringSetHeadingLevel(attrString, node->renderer(), attrStringRange);
1547 AXAttributeStringSetBlockquoteLevel(attrString, node->renderer(), attrStringRange);
1551 // This method is intended to return an array of strings and accessibility elements that
1552 // represent the objects on one line of rendered web content. The array of markers sent
1553 // in should be ordered and contain only a start and end marker.
1554 - (NSArray *)arrayOfTextForTextMarkers:(NSArray *)markers attributed:(BOOL)attributed
1556 if (![self _prepareAccessibilityCall])
1559 if ([markers count] != 2)
1562 WebAccessibilityTextMarker* startMarker = [markers objectAtIndex:0];
1563 WebAccessibilityTextMarker* endMarker = [markers objectAtIndex:1];
1564 if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
1567 // extract the start and end VisiblePosition
1568 VisiblePosition startVisiblePosition = [startMarker visiblePosition];
1569 if (startVisiblePosition.isNull())
1572 VisiblePosition endVisiblePosition = [endMarker visiblePosition];
1573 if (endVisiblePosition.isNull())
1576 // iterate over the range to build the AX attributed string
1577 NSMutableArray* array = [[NSMutableArray alloc] init];
1578 TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get());
1579 for (; !it.atEnd(); it.advance()) {
1580 // locate the node and starting offset for this range
1582 Node* node = it.range()->startContainer(exception);
1583 ASSERT(node == it.range()->endContainer(exception));
1584 int offset = it.range()->startOffset(exception);
1586 // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
1587 if (it.length() != 0) {
1589 // First check if this is represented by a link.
1590 AccessibilityObject* linkObject = AccessibilityObject::anchorElementForNode(node);
1591 if ([self _addAccessibilityObject:linkObject toTextMarkerArray:array])
1594 // Next check if this region is represented by a heading.
1595 AccessibilityObject* headingObject = AccessibilityObject::headingElementForNode(node);
1596 if ([self _addAccessibilityObject:headingObject toTextMarkerArray:array])
1599 String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition()));
1601 if (!listMarkerText.isEmpty())
1602 [array addObject:[NSString stringWithCharacters:listMarkerText.characters() length:listMarkerText.length()]];
1603 // There was not an element representation, so just return the text.
1604 [array addObject:[NSString stringWithCharacters:it.characters() length:it.length()]];
1608 String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition()));
1610 if (!listMarkerText.isEmpty()) {
1611 NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init];
1612 AXAttributedStringAppendText(attrString, node, listMarkerText.characters(), listMarkerText.length());
1613 [array addObject:attrString];
1614 [attrString release];
1617 NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
1618 AXAttributedStringAppendText(attrString, node, it.characters(), it.length());
1619 [array addObject:attrString];
1620 [attrString release];
1623 Node* replacedNode = node->childNode(offset);
1625 AccessibilityObject* obj = m_object->axObjectCache()->getOrCreate(replacedNode->renderer());
1626 if (obj && !obj->accessibilityIsIgnored())
1627 [self _addAccessibilityObject:obj toTextMarkerArray:array];
1632 return [array autorelease];
1635 - (NSRange)_convertToNSRange:(Range *)range
1637 if (!range || !range->startContainer())
1638 return NSMakeRange(NSNotFound, 0);
1640 Document* document = m_object->document();
1641 FrameSelection& frameSelection = document->frame()->selection();
1643 Element* selectionRoot = frameSelection.rootEditableElement();
1644 Element* scope = selectionRoot ? selectionRoot : document->documentElement();
1646 // Mouse events may cause TSM to attempt to create an NSRange for a portion of the view
1647 // that is not inside the current editable region. These checks ensure we don't produce
1648 // potentially invalid data when responding to such requests.
1649 if (range->startContainer() != scope && !range->startContainer()->isDescendantOf(scope))
1650 return NSMakeRange(NSNotFound, 0);
1651 if (range->endContainer() != scope && !range->endContainer()->isDescendantOf(scope))
1652 return NSMakeRange(NSNotFound, 0);
1654 RefPtr<Range> testRange = Range::create(&scope->document(), scope, 0, range->startContainer(), range->startOffset());
1655 ASSERT(testRange->startContainer() == scope);
1656 int startPosition = TextIterator::rangeLength(testRange.get());
1659 testRange->setEnd(range->endContainer(), range->endOffset(), ec);
1660 ASSERT(testRange->startContainer() == scope);
1661 int endPosition = TextIterator::rangeLength(testRange.get());
1662 return NSMakeRange(startPosition, endPosition - startPosition);
1665 - (PassRefPtr<Range>)_convertToDOMRange:(NSRange)nsrange
1667 if (nsrange.location > INT_MAX)
1669 if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
1670 nsrange.length = INT_MAX - nsrange.location;
1672 // our critical assumption is that we are only called by input methods that
1673 // concentrate on a given area containing the selection
1674 // We have to do this because of text fields and textareas. The DOM for those is not
1675 // directly in the document DOM, so serialization is problematic. Our solution is
1676 // to use the root editable element of the selection start as the positional base.
1677 // That fits with AppKit's idea of an input context.
1678 Document* document = m_object->document();
1679 FrameSelection& frameSelection = document->frame()->selection();
1680 Element* selectionRoot = frameSelection.rootEditableElement();
1681 Element* scope = selectionRoot ? selectionRoot : document->documentElement();
1682 return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length);
1685 // This method is intended to take a text marker representing a VisiblePosition and convert it
1686 // into a normalized location within the document.
1687 - (NSInteger)positionForTextMarker:(WebAccessibilityTextMarker *)marker
1689 if (![self _prepareAccessibilityCall])
1695 VisibleSelection selection([marker visiblePosition]);
1696 RefPtr<Range> range = selection.toNormalizedRange();
1697 NSRange nsRange = [self _convertToNSRange:range.get()];
1698 return nsRange.location;
1701 - (NSArray *)textMarkerRange
1703 if (![self _prepareAccessibilityCall])
1706 VisiblePositionRange range = m_object->visiblePositionRange();
1707 VisiblePosition startPosition = range.start;
1708 VisiblePosition endPosition = range.end;
1709 WebAccessibilityTextMarker* start = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:startPosition cache:m_object->axObjectCache()];
1710 WebAccessibilityTextMarker* end = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:endPosition cache:m_object->axObjectCache()];
1712 return [NSArray arrayWithObjects:start, end, nil];
1715 // A method to get the normalized text cursor range of an element. Used in DumpRenderTree.
1716 - (NSRange)elementTextRange
1718 if (![self _prepareAccessibilityCall])
1719 return NSMakeRange(NSNotFound, 0);
1721 NSArray *markers = [self textMarkerRange];
1722 if ([markers count] != 2)
1723 return NSMakeRange(NSNotFound, 0);
1725 WebAccessibilityTextMarker *startMarker = [markers objectAtIndex:0];
1726 WebAccessibilityTextMarker *endMarker = [markers objectAtIndex:1];
1728 NSInteger startPosition = [self positionForTextMarker:startMarker];
1729 NSInteger endPosition = [self positionForTextMarker:endMarker];
1731 return NSMakeRange(startPosition, endPosition - startPosition);
1734 - (AccessibilityObjectWrapper *)accessibilityObjectForTextMarker:(WebAccessibilityTextMarker *)marker
1736 if (![self _prepareAccessibilityCall])
1742 VisiblePosition visiblePosition = [marker visiblePosition];
1743 AccessibilityObject* obj = m_object->accessibilityObjectForPosition(visiblePosition);
1747 return AccessibilityUnignoredAncestor(obj->wrapper());
1750 - (NSArray *)textMarkerRangeForSelection
1752 if (![self _prepareAccessibilityCall])
1755 VisibleSelection selection = m_object->selection();
1756 if (selection.isNone())
1758 VisiblePosition startPosition = selection.visibleStart();
1759 VisiblePosition endPosition = selection.visibleEnd();
1761 WebAccessibilityTextMarker* startMarker = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:startPosition cache:m_object->axObjectCache()];
1762 WebAccessibilityTextMarker* endMarker = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:endPosition cache:m_object->axObjectCache()];
1763 if (!startMarker || !endMarker)
1766 return [NSArray arrayWithObjects:startMarker, endMarker, nil];
1769 - (WebAccessibilityTextMarker *)textMarkerForPosition:(NSInteger)position
1771 if (![self _prepareAccessibilityCall])
1774 PassRefPtr<Range> range = [self _convertToDOMRange:NSMakeRange(position, 0)];
1778 VisibleSelection selection = VisibleSelection(range.get(), DOWNSTREAM);
1780 VisiblePosition visiblePosition = selection.visibleStart();
1781 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:visiblePosition cache:m_object->axObjectCache()];
1784 - (id)_stringForRange:(NSRange)range attributed:(BOOL)attributed
1786 if (![self _prepareAccessibilityCall])
1789 WebAccessibilityTextMarker* startMarker = [self textMarkerForPosition:range.location];
1790 WebAccessibilityTextMarker* endMarker = [self textMarkerForPosition:NSMaxRange(range)];
1792 // Clients don't always know the exact range, rather than force them to compute it,
1793 // allow clients to overshoot and use the max text marker range.
1794 if (!startMarker || !endMarker) {
1795 NSArray *markers = [self textMarkerRange];
1796 if ([markers count] != 2)
1799 startMarker = [markers objectAtIndex:0];
1801 endMarker = [markers objectAtIndex:1];
1804 NSArray* array = [self arrayOfTextForTextMarkers:[NSArray arrayWithObjects:startMarker, endMarker, nil] attributed:attributed];
1805 Class returnClass = attributed ? [NSMutableAttributedString class] : [NSMutableString class];
1806 id returnValue = [[[returnClass alloc] init] autorelease];
1808 NSInteger count = [array count];
1809 for (NSInteger k = 0; k < count; ++k) {
1810 id object = [array objectAtIndex:k];
1812 if (![object isKindOfClass:returnClass])
1816 [(NSMutableAttributedString *)returnValue appendAttributedString:object];
1818 [(NSMutableString *)returnValue appendString:object];
1824 // A convenience method for getting the text of a NSRange. Currently used only by DRT.
1825 - (NSString *)stringForRange:(NSRange)range
1827 return [self _stringForRange:range attributed:NO];
1830 - (NSAttributedString *)attributedStringForRange:(NSRange)range
1832 return [self _stringForRange:range attributed:YES];
1835 // A convenience method for getting the accessibility objects of a NSRange. Currently used only by DRT.
1836 - (NSArray *)elementsForRange:(NSRange)range
1838 if (![self _prepareAccessibilityCall])
1841 WebAccessibilityTextMarker* startMarker = [self textMarkerForPosition:range.location];
1842 WebAccessibilityTextMarker* endMarker = [self textMarkerForPosition:NSMaxRange(range)];
1843 if (!startMarker || !endMarker)
1846 NSArray* array = [self arrayOfTextForTextMarkers:[NSArray arrayWithObjects:startMarker, endMarker, nil] attributed:NO];
1847 NSMutableArray* elements = [NSMutableArray array];
1848 for (id element in array) {
1849 if (![element isKindOfClass:[AccessibilityObjectWrapper class]])
1851 [elements addObject:element];
1856 - (NSString *)selectionRangeString
1858 NSArray *markers = [self textMarkerRangeForSelection];
1859 return [self stringForTextMarkers:markers];
1862 - (WebAccessibilityTextMarker *)selectedTextMarker
1864 if (![self _prepareAccessibilityCall])
1867 VisibleSelection selection = m_object->selection();
1868 VisiblePosition position = selection.visibleStart();
1870 // if there's no selection, start at the top of the document
1871 if (position.isNull())
1872 position = startOfDocument(m_object->document());
1874 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:position cache:m_object->axObjectCache()];
1877 // This method is intended to return the marker at the end of the line starting at
1878 // the marker that is passed into the method.
1879 - (WebAccessibilityTextMarker *)lineEndMarkerForMarker:(WebAccessibilityTextMarker *)marker
1881 if (![self _prepareAccessibilityCall])
1887 VisiblePosition start = [marker visiblePosition];
1888 VisiblePosition lineEnd = m_object->nextLineEndPosition(start);
1890 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:lineEnd cache:m_object->axObjectCache()];
1893 // This method is intended to return the marker at the start of the line starting at
1894 // the marker that is passed into the method.
1895 - (WebAccessibilityTextMarker *)lineStartMarkerForMarker:(WebAccessibilityTextMarker *)marker
1897 if (![self _prepareAccessibilityCall])
1903 VisiblePosition start = [marker visiblePosition];
1904 VisiblePosition lineStart = m_object->previousLineStartPosition(start);
1906 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:lineStart cache:m_object->axObjectCache()];
1909 - (WebAccessibilityTextMarker *)nextMarkerForMarker:(WebAccessibilityTextMarker *)marker
1911 if (![self _prepareAccessibilityCall])
1917 VisiblePosition start = [marker visiblePosition];
1918 VisiblePosition nextMarker = m_object->nextVisiblePosition(start);
1920 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:nextMarker cache:m_object->axObjectCache()];
1923 // This method is intended to return the marker at the start of the line starting at
1924 // the marker that is passed into the method.
1925 - (WebAccessibilityTextMarker *)previousMarkerForMarker:(WebAccessibilityTextMarker *)marker
1927 if (![self _prepareAccessibilityCall])
1933 VisiblePosition start = [marker visiblePosition];
1934 VisiblePosition previousMarker = m_object->previousVisiblePosition(start);
1936 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:previousMarker cache:m_object->axObjectCache()];
1939 // This method is intended to return the bounds of a text marker range in screen coordinates.
1940 - (CGRect)frameForTextMarkers:(NSArray *)array
1942 if (![self _prepareAccessibilityCall])
1945 if ([array count] != 2)
1948 WebAccessibilityTextMarker* startMarker = [array objectAtIndex:0];
1949 WebAccessibilityTextMarker* endMarker = [array objectAtIndex:1];
1950 if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
1953 IntRect rect = m_object->boundsForVisiblePositionRange(VisiblePositionRange([startMarker visiblePosition], [endMarker visiblePosition]));
1954 return [self convertRectToScreenSpace:rect];
1957 - (WebAccessibilityTextMarker *)textMarkerForPoint:(CGPoint)point
1959 if (![self _prepareAccessibilityCall])
1962 VisiblePosition pos = m_object->visiblePositionForPoint(IntPoint(point));
1963 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:pos cache:m_object->axObjectCache()];
1966 - (NSString *)accessibilityIdentifier
1968 if (![self _prepareAccessibilityCall])
1971 return m_object->getAttribute(HTMLNames::idAttr);
1974 - (NSString *)accessibilitySpeechHint
1976 if (![self _prepareAccessibilityCall])
1979 switch (m_object->speakProperty()) {
1986 return @"spell-out";
1989 case SpeakLiteralPunctuation:
1990 return @"literal-punctuation";
1991 case SpeakNoPunctuation:
1992 return @"no-punctuation";
1998 - (BOOL)accessibilityARIAIsBusy
2000 if (![self _prepareAccessibilityCall])
2003 return m_object->ariaLiveRegionBusy();
2006 - (NSString *)accessibilityARIALiveRegionStatus
2008 if (![self _prepareAccessibilityCall])
2011 return m_object->ariaLiveRegionStatus();
2014 - (NSString *)accessibilityARIARelevantStatus
2016 if (![self _prepareAccessibilityCall])
2019 return m_object->ariaLiveRegionRelevant();
2022 - (BOOL)accessibilityARIALiveRegionIsAtomic
2024 if (![self _prepareAccessibilityCall])
2027 return m_object->ariaLiveRegionAtomic();
2030 - (NSString *)accessibilityInvalidStatus
2032 if (![self _prepareAccessibilityCall])
2035 return m_object->invalidStatus();
2038 - (WebAccessibilityObjectWrapper *)accessibilityMathRootIndexObject
2040 if (![self _prepareAccessibilityCall])
2043 return m_object->mathRootIndexObject() ? m_object->mathRootIndexObject()->wrapper() : 0;
2046 - (WebAccessibilityObjectWrapper *)accessibilityMathRadicandObject
2048 if (![self _prepareAccessibilityCall])
2051 return m_object->mathRadicandObject() ? m_object->mathRadicandObject()->wrapper() : 0;
2054 - (WebAccessibilityObjectWrapper *)accessibilityMathNumeratorObject
2056 if (![self _prepareAccessibilityCall])
2059 return m_object->mathNumeratorObject() ? m_object->mathNumeratorObject()->wrapper() : 0;
2062 - (WebAccessibilityObjectWrapper *)accessibilityMathDenominatorObject
2064 if (![self _prepareAccessibilityCall])
2067 return m_object->mathDenominatorObject() ? m_object->mathDenominatorObject()->wrapper() : 0;
2070 - (WebAccessibilityObjectWrapper *)accessibilityMathBaseObject
2072 if (![self _prepareAccessibilityCall])
2075 return m_object->mathBaseObject() ? m_object->mathBaseObject()->wrapper() : 0;
2078 - (WebAccessibilityObjectWrapper *)accessibilityMathSubscriptObject
2080 if (![self _prepareAccessibilityCall])
2083 return m_object->mathSubscriptObject() ? m_object->mathSubscriptObject()->wrapper() : 0;
2086 - (WebAccessibilityObjectWrapper *)accessibilityMathSuperscriptObject
2088 if (![self _prepareAccessibilityCall])
2091 return m_object->mathSuperscriptObject() ? m_object->mathSuperscriptObject()->wrapper() : 0;
2094 - (WebAccessibilityObjectWrapper *)accessibilityMathUnderObject
2096 if (![self _prepareAccessibilityCall])
2099 return m_object->mathUnderObject() ? m_object->mathUnderObject()->wrapper() : 0;
2102 - (WebAccessibilityObjectWrapper *)accessibilityMathOverObject
2104 if (![self _prepareAccessibilityCall])
2107 return m_object->mathOverObject() ? m_object->mathOverObject()->wrapper() : 0;
2110 - (NSString *)accessibilityPlatformMathSubscriptKey
2112 return @"AXMSubscriptObject";
2115 - (NSString *)accessibilityPlatformMathSuperscriptKey
2117 return @"AXMSuperscriptObject";
2120 - (NSArray *)accessibilityMathPostscripts
2122 if (![self _prepareAccessibilityCall])
2125 return [self accessibilityMathPostscriptPairs];
2128 - (NSArray *)accessibilityMathPrescripts
2130 if (![self _prepareAccessibilityCall])
2133 return [self accessibilityMathPrescriptPairs];
2136 - (NSString *)accessibilityMathFencedOpenString
2138 if (![self _prepareAccessibilityCall])
2141 return m_object->mathFencedOpenString();
2144 - (NSString *)accessibilityMathFencedCloseString
2146 if (![self _prepareAccessibilityCall])
2149 return m_object->mathFencedCloseString();
2152 - (BOOL)accessibilityIsMathTopObject
2154 if (![self _prepareAccessibilityCall])
2157 return m_object->roleValue() == DocumentMathRole;
2160 - (NSInteger)accessibilityMathLineThickness
2162 if (![self _prepareAccessibilityCall])
2165 return m_object->mathLineThickness();
2168 - (NSString *)accessibilityMathType
2170 if (![self _prepareAccessibilityCall])
2173 if (m_object->roleValue() == MathElementRole) {
2174 if (m_object->isMathFraction())
2175 return @"AXMathFraction";
2176 if (m_object->isMathFenced())
2177 return @"AXMathFenced";
2178 if (m_object->isMathSubscriptSuperscript())
2179 return @"AXMathSubscriptSuperscript";
2180 if (m_object->isMathRow())
2181 return @"AXMathRow";
2182 if (m_object->isMathUnderOver())
2183 return @"AXMathUnderOver";
2184 if (m_object->isMathSquareRoot())
2185 return @"AXMathSquareRoot";
2186 if (m_object->isMathRoot())
2187 return @"AXMathRoot";
2188 if (m_object->isMathText())
2189 return @"AXMathText";
2190 if (m_object->isMathNumber())
2191 return @"AXMathNumber";
2192 if (m_object->isMathIdentifier())
2193 return @"AXMathIdentifier";
2194 if (m_object->isMathTable())
2195 return @"AXMathTable";
2196 if (m_object->isMathTableRow())
2197 return @"AXMathTableRow";
2198 if (m_object->isMathTableCell())
2199 return @"AXMathTableCell";
2200 if (m_object->isMathFenceOperator())
2201 return @"AXMathFenceOperator";
2202 if (m_object->isMathSeparatorOperator())
2203 return @"AXMathSeparatorOperator";
2204 if (m_object->isMathOperator())
2205 return @"AXMathOperator";
2206 if (m_object->isMathMultiscript())
2207 return @"AXMathMultiscript";
2213 - (CGPoint)accessibilityClickPoint
2215 return m_object->clickPoint();
2218 // These are used by DRT so that it can know when notifications are sent.
2219 // Since they are static, only one callback can be installed at a time (that's all DRT should need).
2220 typedef void (*AXPostedNotificationCallback)(id element, NSString* notification, void* context);
2221 static AXPostedNotificationCallback AXNotificationCallback = 0;
2222 static void* AXPostedNotificationContext = 0;
2224 - (void)accessibilitySetPostedNotificationCallback:(AXPostedNotificationCallback)function withContext:(void*)context
2226 AXNotificationCallback = function;
2227 AXPostedNotificationContext = context;
2230 - (void)accessibilityPostedNotification:(NSString *)notificationName
2232 if (AXNotificationCallback && notificationName)
2233 AXNotificationCallback(self, notificationName, AXPostedNotificationContext);
2237 - (NSString *)description
2239 CGRect frame = [self accessibilityFrame];
2240 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];
2246 #endif // HAVE(ACCESSIBILITY) && PLATFORM(IOS)