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 INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #import "WebAccessibilityObjectWrapperIOS.h"
29 #if HAVE(ACCESSIBILITY) && PLATFORM(IOS)
31 #import "AccessibilityRenderObject.h"
32 #import "AccessibilityScrollView.h"
33 #import "AccessibilityTable.h"
34 #import "AccessibilityTableCell.h"
36 #import "ChromeClient.h"
37 #import "FontCascade.h"
39 #import "FrameSelection.h"
41 #import "HitTestResult.h"
42 #import "HTMLFrameOwnerElement.h"
43 #import "HTMLInputElement.h"
47 #import "LocalizedStrings.h"
50 #import "RenderView.h"
51 #import "RuntimeApplicationChecksIOS.h"
53 #import "SVGElement.h"
54 #import "TextIterator.h"
55 #import "WAKScrollView.h"
58 #import "WebCoreThread.h"
59 #import "VisibleUnits.h"
61 #import <CoreText/CoreText.h>
63 @interface NSObject (AccessibilityPrivate)
64 - (void)_accessibilityUnregister;
65 - (NSString *)accessibilityLabel;
66 - (NSString *)accessibilityValue;
67 - (BOOL)isAccessibilityElement;
68 - (NSInteger)accessibilityElementCount;
69 - (id)accessibilityElementAtIndex:(NSInteger)index;
70 - (NSInteger)indexOfAccessibilityElement:(id)element;
73 @interface WebAccessibilityObjectWrapper (AccessibilityPrivate)
74 - (id)_accessibilityWebDocumentView;
75 - (id)accessibilityContainer;
76 - (void)setAccessibilityLabel:(NSString *)label;
77 - (void)setAccessibilityValue:(NSString *)value;
78 - (BOOL)containsUnnaturallySegmentedChildren;
79 - (NSInteger)positionForTextMarker:(id)marker;
82 @interface WAKView (iOSAccessibility)
83 - (BOOL)accessibilityIsIgnored;
86 using namespace WebCore;
87 using namespace HTMLNames;
89 typedef NS_ENUM(NSInteger, UIAccessibilityScrollDirection) {
90 UIAccessibilityScrollDirectionRight = 1,
91 UIAccessibilityScrollDirectionLeft,
92 UIAccessibilityScrollDirectionUp,
93 UIAccessibilityScrollDirectionDown,
94 UIAccessibilityScrollDirectionNext,
95 UIAccessibilityScrollDirectionPrevious
98 // These are tokens accessibility uses to denote attributes.
99 static NSString * const UIAccessibilityTokenBlockquoteLevel = @"UIAccessibilityTokenBlockquoteLevel";
100 static NSString * const UIAccessibilityTokenHeadingLevel = @"UIAccessibilityTokenHeadingLevel";
101 static NSString * const UIAccessibilityTokenFontName = @"UIAccessibilityTokenFontName";
102 static NSString * const UIAccessibilityTokenFontFamily = @"UIAccessibilityTokenFontFamily";
103 static NSString * const UIAccessibilityTokenFontSize = @"UIAccessibilityTokenFontSize";
104 static NSString * const UIAccessibilityTokenBold = @"UIAccessibilityTokenBold";
105 static NSString * const UIAccessibilityTokenItalic = @"UIAccessibilityTokenItalic";
106 static NSString * const UIAccessibilityTokenUnderline = @"UIAccessibilityTokenUnderline";
107 static NSString * const UIAccessibilityTokenLanguage = @"UIAccessibilityTokenLanguage";
109 static AccessibilityObjectWrapper* AccessibilityUnignoredAncestor(AccessibilityObjectWrapper *wrapper)
111 while (wrapper && ![wrapper isAccessibilityElement]) {
112 AccessibilityObject* object = [wrapper accessibilityObject];
116 if ([wrapper isAttachment] && ![[wrapper attachmentView] accessibilityIsIgnored])
119 AccessibilityObject* parentObject = object->parentObjectUnignored();
123 wrapper = parentObject->wrapper();
128 #pragma mark Accessibility Text Marker
130 @interface WebAccessibilityTextMarker : NSObject
132 AXObjectCache* _cache;
133 TextMarkerData _textMarkerData;
136 + (WebAccessibilityTextMarker *)textMarkerWithVisiblePosition:(VisiblePosition&)visiblePos cache:(AXObjectCache*)cache;
140 @implementation WebAccessibilityTextMarker
142 - (id)initWithTextMarker:(TextMarkerData *)data cache:(AXObjectCache*)cache
144 if (!(self = [super init]))
148 memcpy(&_textMarkerData, data, sizeof(TextMarkerData));
152 - (id)initWithData:(NSData *)data cache:(AXObjectCache*)cache
154 if (!(self = [super init]))
158 [data getBytes:&_textMarkerData length:sizeof(TextMarkerData)];
163 // This is needed for external clients to be able to create a text marker without having a pointer to the cache.
164 - (id)initWithData:(NSData *)data accessibilityObject:(AccessibilityObjectWrapper *)wrapper
166 WebCore::AccessibilityObject* axObject = [wrapper accessibilityObject];
170 return [self initWithData:data cache:axObject->axObjectCache()];
173 + (WebAccessibilityTextMarker *)textMarkerWithVisiblePosition:(VisiblePosition&)visiblePos cache:(AXObjectCache*)cache
175 TextMarkerData textMarkerData;
176 cache->textMarkerDataForVisiblePosition(textMarkerData, visiblePos);
178 return [[[WebAccessibilityTextMarker alloc] initWithTextMarker:&textMarkerData cache:cache] autorelease];
181 - (NSData *)dataRepresentation
183 return [NSData dataWithBytes:&_textMarkerData length:sizeof(TextMarkerData)];
186 - (VisiblePosition)visiblePosition
188 return _cache->visiblePositionForTextMarkerData(_textMarkerData);
191 - (NSString *)description
193 return [NSString stringWithFormat:@"[AXTextMarker %p] = node: %p offset: %d", self, _textMarkerData.node, _textMarkerData.offset];
198 @implementation WebAccessibilityObjectWrapper
200 - (id)initWithAccessibilityObject:(AccessibilityObject*)axObject
202 self = [super initWithAccessibilityObject:axObject];
206 // Initialize to a sentinel value.
207 m_accessibilityTraitsFromAncestor = ULLONG_MAX;
208 m_isAccessibilityElement = -1;
215 // rdar://8798960 Make sure the object is gone early, so that anything _accessibilityUnregister
216 // does can't call back into the render tree.
219 if ([self respondsToSelector:@selector(_accessibilityUnregister)])
220 [self _accessibilityUnregister];
225 // We should have been detached before deallocated.
230 - (BOOL)_prepareAccessibilityCall
232 // rdar://7980318 if we start a call, then block in WebThreadLock(), then we're dealloced on another, thread, we could
233 // crash, so we should retain ourself for the duration of usage here.
234 [[self retain] autorelease];
238 // If we came back from our thread lock and we were detached, we will no longer have an m_object.
242 m_object->updateBackingStore();
249 // These are here so that we don't have to import AXRuntime.
250 // The methods will be swizzled when the accessibility bundle is loaded.
252 - (uint64_t)_axLinkTrait { return (1 << 0); }
253 - (uint64_t)_axVisitedTrait { return (1 << 1); }
254 - (uint64_t)_axHeaderTrait { return (1 << 2); }
255 - (uint64_t)_axContainedByListTrait { return (1 << 3); }
256 - (uint64_t)_axContainedByTableTrait { return (1 << 4); }
257 - (uint64_t)_axContainedByLandmarkTrait { return (1 << 5); }
258 - (uint64_t)_axWebContentTrait { return (1 << 6); }
259 - (uint64_t)_axSecureTextFieldTrait { return (1 << 7); }
260 - (uint64_t)_axTextEntryTrait { return (1 << 8); }
261 - (uint64_t)_axHasTextCursorTrait { return (1 << 9); }
262 - (uint64_t)_axTextOperationsAvailableTrait { return (1 << 10); }
263 - (uint64_t)_axImageTrait { return (1 << 11); }
264 - (uint64_t)_axTabButtonTrait { return (1 << 12); }
265 - (uint64_t)_axButtonTrait { return (1 << 13); }
266 - (uint64_t)_axToggleTrait { return (1 << 14); }
267 - (uint64_t)_axPopupButtonTrait { return (1 << 15); }
268 - (uint64_t)_axStaticTextTrait { return (1 << 16); }
269 - (uint64_t)_axAdjustableTrait { return (1 << 17); }
270 - (uint64_t)_axMenuItemTrait { return (1 << 18); }
271 - (uint64_t)_axSelectedTrait { return (1 << 19); }
272 - (uint64_t)_axNotEnabledTrait { return (1 << 20); }
273 - (uint64_t)_axRadioButtonTrait { return (1 << 21); }
275 - (BOOL)accessibilityCanFuzzyHitTest
277 if (![self _prepareAccessibilityCall])
280 AccessibilityRole role = m_object->roleValue();
281 // Elements that can be returned when performing fuzzy hit testing.
286 case DisclosureTriangleRole:
288 case ImageMapLinkRole:
292 case ListBoxOptionRole:
295 case MenuItemCheckboxRole:
296 case MenuItemRadioRole:
297 case PopUpButtonRole:
298 case RadioButtonRole:
304 case ToggleButtonRole:
305 return !m_object->accessibilityIsIgnored();
311 - (AccessibilityObjectWrapper *)accessibilityPostProcessHitTest:(CGPoint)point
314 // The UIKit accessibility wrapper will override this and perform the post process hit test.
318 - (id)accessibilityHitTest:(CGPoint)point
320 if (![self _prepareAccessibilityCall])
323 // Try a fuzzy hit test first to find an accessible element.
324 RefPtr<AccessibilityObject> axObject;
326 AXAttributeCacheEnabler enableCache(m_object->axObjectCache());
327 axObject = m_object->accessibilityHitTest(IntPoint(point));
333 // If this is a good accessible object to return, no extra work is required.
334 if ([axObject->wrapper() accessibilityCanFuzzyHitTest])
335 return AccessibilityUnignoredAncestor(axObject->wrapper());
337 // Check to see if we can post-process this hit test to find a better candidate.
338 AccessibilityObjectWrapper* wrapper = [axObject->wrapper() accessibilityPostProcessHitTest:point];
340 return AccessibilityUnignoredAncestor(wrapper);
342 // Fall back to default behavior.
343 return AccessibilityUnignoredAncestor(axObject->wrapper());
346 - (void)enableAttributeCaching
348 if (AXObjectCache* cache = m_object->axObjectCache())
349 cache->startCachingComputedObjectAttributesUntilTreeMutates();
352 - (void)disableAttributeCaching
354 if (AXObjectCache* cache = m_object->axObjectCache())
355 cache->stopCachingComputedObjectAttributes();
358 - (NSInteger)accessibilityElementCount
360 if (![self _prepareAccessibilityCall])
363 if ([self isAttachment] && [self attachmentView])
364 return [[self attachmentView] accessibilityElementCount];
366 return m_object->children().size();
369 - (id)accessibilityElementAtIndex:(NSInteger)index
371 if (![self _prepareAccessibilityCall])
374 if ([self isAttachment] && [self attachmentView])
375 return [[self attachmentView] accessibilityElementAtIndex:index];
377 const auto& children = m_object->children();
378 size_t elementIndex = static_cast<size_t>(index);
379 if (elementIndex >= children.size())
382 AccessibilityObjectWrapper* wrapper = children[elementIndex]->wrapper();
383 if (children[elementIndex]->isAttachment())
384 return [wrapper attachmentView];
389 - (NSInteger)indexOfAccessibilityElement:(id)element
391 if (![self _prepareAccessibilityCall])
394 if ([self isAttachment] && [self attachmentView])
395 return [[self attachmentView] indexOfAccessibilityElement:element];
397 const auto& children = m_object->children();
398 unsigned count = children.size();
399 for (unsigned k = 0; k < count; ++k) {
400 AccessibilityObjectWrapper* wrapper = children[k]->wrapper();
401 if (wrapper == element || (children[k]->isAttachment() && [wrapper attachmentView] == element))
408 - (CGPathRef)_accessibilityPath
410 if (![self _prepareAccessibilityCall])
413 if (!m_object->supportsPath())
416 Path path = m_object->elementPath();
420 return [self convertPathToScreenSpace:path];
423 - (NSString *)accessibilityLanguage
425 if (![self _prepareAccessibilityCall])
428 return m_object->language();
431 - (BOOL)_accessibilityIsLandmarkRole:(AccessibilityRole)role
434 case LandmarkApplicationRole:
435 case LandmarkBannerRole:
436 case LandmarkComplementaryRole:
437 case LandmarkContentInfoRole:
438 case LandmarkMainRole:
439 case LandmarkNavigationRole:
440 case LandmarkSearchRole:
447 - (AccessibilityObjectWrapper*)_accessibilityListAncestor
449 for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
450 AccessibilityRole role = parent->roleValue();
451 if (role == ListRole || role == ListBoxRole)
452 return parent->wrapper();
458 - (AccessibilityObjectWrapper*)_accessibilityLandmarkAncestor
460 for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
461 if ([self _accessibilityIsLandmarkRole:parent->roleValue()])
462 return parent->wrapper();
468 - (AccessibilityObjectWrapper*)_accessibilityTableAncestor
470 for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
471 if (parent->roleValue() == TableRole)
472 return parent->wrapper();
478 - (uint64_t)_accessibilityTraitsFromAncestors
481 AccessibilityRole role = m_object->roleValue();
483 // Trait information also needs to be gathered from the parents above the object.
484 // The parentObject is needed instead of the unignoredParentObject, because a table might be ignored, but information still needs to be gathered from it.
485 for (AccessibilityObject* parent = m_object->parentObject(); parent != nil; parent = parent->parentObject()) {
486 AccessibilityRole parentRole = parent->roleValue();
487 if (parentRole == WebAreaRole)
490 switch (parentRole) {
492 case WebCoreLinkRole:
493 traits |= [self _axLinkTrait];
494 if (parent->isVisited())
495 traits |= [self _axVisitedTrait];
499 traits |= [self _axHeaderTrait];
500 // If this object has the header trait, we should set the value
501 // to the heading level. If it was a static text element, we need to store
502 // the value as the label, because the heading level needs to the value.
503 AccessibilityObjectWrapper* wrapper = parent->wrapper();
504 if (role == StaticTextRole)
505 [self setAccessibilityLabel:m_object->stringValue()];
506 [self setAccessibilityValue:[wrapper accessibilityValue]];
511 traits |= [self _axContainedByListTrait];
514 traits |= [self _axContainedByTableTrait];
517 if ([self _accessibilityIsLandmarkRole:parentRole])
518 traits |= [self _axContainedByLandmarkTrait];
526 - (uint64_t)accessibilityTraits
528 if (![self _prepareAccessibilityCall])
531 AccessibilityRole role = m_object->roleValue();
532 uint64_t traits = [self _axWebContentTrait];
535 case WebCoreLinkRole:
536 traits |= [self _axLinkTrait];
537 if (m_object->isVisited())
538 traits |= [self _axVisitedTrait];
540 // TextFieldRole is intended to fall through to TextAreaRole, in order to pick up the text entry and text cursor traits.
542 if (m_object->isPasswordField())
543 traits |= [self _axSecureTextFieldTrait];
546 traits |= [self _axTextEntryTrait];
547 if (m_object->isFocused())
548 traits |= ([self _axHasTextCursorTrait] | [self _axTextOperationsAvailableTrait]);
551 traits |= [self _axImageTrait];
554 traits |= [self _axTabButtonTrait];
557 traits |= [self _axButtonTrait];
558 if (m_object->isPressed())
559 traits |= [self _axToggleTrait];
561 case PopUpButtonRole:
562 traits |= [self _axPopupButtonTrait];
564 case RadioButtonRole:
565 traits |= [self _axRadioButtonTrait] | [self _axToggleTrait];
567 case ToggleButtonRole:
569 traits |= ([self _axButtonTrait] | [self _axToggleTrait]);
572 traits |= [self _axHeaderTrait];
575 traits |= [self _axStaticTextTrait];
578 traits |= [self _axAdjustableTrait];
582 case MenuItemCheckboxRole:
583 case MenuItemRadioRole:
584 traits |= [self _axMenuItemTrait];
590 if (m_object->isSelected())
591 traits |= [self _axSelectedTrait];
593 if (!m_object->isEnabled())
594 traits |= [self _axNotEnabledTrait];
596 if (m_accessibilityTraitsFromAncestor == ULLONG_MAX)
597 m_accessibilityTraitsFromAncestor = [self _accessibilityTraitsFromAncestors];
599 traits |= m_accessibilityTraitsFromAncestor;
604 - (BOOL)isSVGGroupElement
606 // If an SVG group element has a title, it should be an accessible element on iOS.
607 Node* node = m_object->node();
608 if (node && node->hasTagName(SVGNames::gTag) && [[self accessibilityLabel] length] > 0)
614 - (BOOL)determineIsAccessibilityElement
619 // Honor when something explicitly makes this an element (super will contain that logic)
620 if ([super isAccessibilityElement])
623 m_object->updateBackingStore();
625 switch (m_object->roleValue()) {
629 case ToggleButtonRole:
630 case PopUpButtonRole:
632 case RadioButtonRole:
635 case ValueIndicatorRole:
637 case ProgressIndicatorRole:
639 case IncrementorRole:
641 case DisclosureTriangleRole:
644 case ListBoxOptionRole:
646 case DocumentMathRole:
647 case HorizontalRuleRole:
651 // Many text elements only contain a space.
652 if (![[[self accessibilityLabel] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length])
655 // Text elements that are just pieces of links or headers should not be exposed.
656 if ([AccessibilityUnignoredAncestor([self accessibilityContainer]) containsUnnaturallySegmentedChildren])
661 // Don't expose headers as elements; instead expose their children as elements, with the header trait (unless they have no children)
663 if (![self accessibilityElementCount])
667 // Links can sometimes be elements (when they only contain static text or don't contain anything).
668 // They should not be elements when containing text and other types.
669 case WebCoreLinkRole:
671 if ([self containsUnnaturallySegmentedChildren] || ![self accessibilityElementCount])
675 if ([self isSVGGroupElement])
678 // All other elements are ignored on the iphone.
684 case ApplicationRole:
694 case BusyIndicatorRole:
708 case RulerMarkerRole:
715 - (BOOL)isAccessibilityElement
717 if (![self _prepareAccessibilityCall])
720 if (m_isAccessibilityElement == -1)
721 m_isAccessibilityElement = [self determineIsAccessibilityElement];
723 return m_isAccessibilityElement;
726 - (BOOL)stringValueShouldBeUsedInLabel
728 if (m_object->isTextControl())
730 if (m_object->roleValue() == PopUpButtonRole)
732 if (m_object->isFileUploadButton())
738 - (BOOL)fileUploadButtonReturnsValueInTitle
743 static void appendStringToResult(NSMutableString *result, NSString *string)
746 if (![string length])
749 [result appendString:@", "];
750 [result appendString:string];
753 - (BOOL)_accessibilityValueIsAutofilled
755 if (![self _prepareAccessibilityCall])
758 return m_object->isValueAutofilled();
761 - (CGFloat)_accessibilityMinValue
763 return m_object->minValueForRange();
766 - (CGFloat)_accessibilityMaxValue
768 return m_object->maxValueForRange();
771 - (NSString *)accessibilityLabel
773 if (![self _prepareAccessibilityCall])
776 // check if the label was overriden
777 NSString *label = [super accessibilityLabel];
781 // iOS doesn't distinguish between a title and description field,
782 // so concatentation will yield the best result.
783 NSString *axTitle = [self baseAccessibilityTitle];
784 NSString *axDescription = [self baseAccessibilityDescription];
785 NSString *landmarkDescription = [self ariaLandmarkRoleDescription];
787 NSMutableString *result = [NSMutableString string];
788 if (m_object->roleValue() == HorizontalRuleRole)
789 appendStringToResult(result, AXHorizontalRuleDescriptionText());
791 appendStringToResult(result, axTitle);
792 appendStringToResult(result, axDescription);
793 if ([self stringValueShouldBeUsedInLabel]) {
794 NSString *valueLabel = m_object->stringValue();
795 valueLabel = [valueLabel stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
796 appendStringToResult(result, valueLabel);
798 appendStringToResult(result, landmarkDescription);
800 return [result length] ? result : nil;
803 - (AccessibilityTableCell*)tableCellParent
805 // Find if this element is in a table cell.
806 AccessibilityObject* cell = nullptr;
807 for (cell = m_object; cell && !cell->isTableCell(); cell = cell->parentObject())
813 return static_cast<AccessibilityTableCell*>(cell);
816 - (AccessibilityTable*)tableParent
818 // Find if the parent table for the table cell.
819 AccessibilityObject* parentTable = nullptr;
820 for (parentTable = m_object; parentTable && !parentTable->isDataTable(); parentTable = parentTable->parentObject())
826 return static_cast<AccessibilityTable*>(parentTable);
829 - (id)accessibilityTitleElement
831 if (![self _prepareAccessibilityCall])
834 AccessibilityObject* titleElement = m_object->titleUIElement();
836 return titleElement->wrapper();
841 // Meant to return row or column headers (or other things as the future permits).
842 - (NSArray *)accessibilityHeaderElements
844 if (![self _prepareAccessibilityCall])
847 AccessibilityTableCell* tableCell = [self tableCellParent];
851 AccessibilityTable* table = [self tableParent];
855 // Get the row and column range, so we can use them to find the headers.
856 std::pair<unsigned, unsigned> rowRange;
857 std::pair<unsigned, unsigned> columnRange;
858 tableCell->rowIndexRange(rowRange);
859 tableCell->columnIndexRange(columnRange);
861 AccessibilityObject::AccessibilityChildrenVector rowHeaders;
862 AccessibilityObject::AccessibilityChildrenVector columnHeaders;
863 table->rowHeaders(rowHeaders);
864 table->columnHeaders(columnHeaders);
866 NSMutableArray *headers = [NSMutableArray array];
868 unsigned columnRangeIndex = static_cast<unsigned>(columnRange.first);
869 if (columnRangeIndex < columnHeaders.size()) {
870 RefPtr<AccessibilityObject> columnHeader = columnHeaders[columnRange.first];
871 AccessibilityObjectWrapper* wrapper = columnHeader->wrapper();
873 [headers addObject:wrapper];
876 unsigned rowRangeIndex = static_cast<unsigned>(rowRange.first);
877 if (rowRangeIndex < rowHeaders.size()) {
878 RefPtr<AccessibilityObject> rowHeader = rowHeaders[rowRange.first];
879 AccessibilityObjectWrapper* wrapper = rowHeader->wrapper();
881 [headers addObject:wrapper];
887 - (id)accessibilityElementForRow:(NSInteger)row andColumn:(NSInteger)column
889 if (![self _prepareAccessibilityCall])
892 AccessibilityTable* table = [self tableParent];
896 AccessibilityTableCell* cell = table->cellForColumnAndRow(column, row);
899 return cell->wrapper();
902 - (NSRange)accessibilityRowRange
904 if (![self _prepareAccessibilityCall])
905 return NSMakeRange(NSNotFound, 0);
907 if (m_object->isRadioButton()) {
908 AccessibilityObject::AccessibilityChildrenVector radioButtonSiblings;
909 m_object->linkedUIElements(radioButtonSiblings);
910 if (radioButtonSiblings.size() <= 1)
911 return NSMakeRange(NSNotFound, 0);
913 return NSMakeRange(radioButtonSiblings.find(m_object), radioButtonSiblings.size());
916 AccessibilityTableCell* tableCell = [self tableCellParent];
918 return NSMakeRange(NSNotFound, 0);
920 std::pair<unsigned, unsigned> rowRange;
921 tableCell->rowIndexRange(rowRange);
922 return NSMakeRange(rowRange.first, rowRange.second);
925 - (NSRange)accessibilityColumnRange
927 if (![self _prepareAccessibilityCall])
928 return NSMakeRange(NSNotFound, 0);
930 AccessibilityTableCell* tableCell = [self tableCellParent];
932 return NSMakeRange(NSNotFound, 0);
934 std::pair<unsigned, unsigned> columnRange;
935 tableCell->columnIndexRange(columnRange);
936 return NSMakeRange(columnRange.first, columnRange.second);
939 - (NSString *)accessibilityPlaceholderValue
941 if (![self _prepareAccessibilityCall])
944 return m_object->placeholderValue();
947 - (NSString *)accessibilityValue
949 if (![self _prepareAccessibilityCall])
952 // check if the value was overriden
953 NSString *value = [super accessibilityValue];
957 if (m_object->isCheckboxOrRadio()) {
958 switch (m_object->checkboxOrRadioValue()) {
960 return [NSString stringWithFormat:@"%d", 0];
962 return [NSString stringWithFormat:@"%d", 1];
963 case ButtonStateMixed:
964 return [NSString stringWithFormat:@"%d", 2];
966 ASSERT_NOT_REACHED();
967 return [NSString stringWithFormat:@"%d", 0];
970 if (m_object->isButton() && m_object->isPressed())
971 return [NSString stringWithFormat:@"%d", 1];
973 // rdar://8131388 WebKit should expose the same info as UIKit for its password fields.
974 if (m_object->isPasswordField()) {
975 int passwordLength = m_object->accessibilityPasswordFieldLength();
976 NSMutableString* string = [NSMutableString string];
977 for (int k = 0; k < passwordLength; ++k)
978 [string appendString:@"•"];
982 // A text control should return its text data as the axValue (per iPhone AX API).
983 if (![self stringValueShouldBeUsedInLabel])
984 return m_object->stringValue();
986 if (m_object->isProgressIndicator() || m_object->isSlider()) {
987 // Prefer a valueDescription if provided by the author (through aria-valuetext).
988 String valueDescription = m_object->valueDescription();
989 if (!valueDescription.isEmpty())
990 return valueDescription;
992 return [NSString stringWithFormat:@"%.2f", m_object->valueForRange()];
995 if (m_object->isHeading())
996 return [NSString stringWithFormat:@"%d", m_object->headingLevel()];
1001 - (BOOL)accessibilityIsComboBox
1003 if (![self _prepareAccessibilityCall])
1006 return m_object->roleValue() == ComboBoxRole;
1009 - (NSString *)accessibilityHint
1011 if (![self _prepareAccessibilityCall])
1014 return [self baseAccessibilityHelpText];
1017 - (NSURL *)accessibilityURL
1019 if (![self _prepareAccessibilityCall])
1022 URL url = m_object->url();
1028 - (CGPoint)_accessibilityConvertPointToViewSpace:(CGPoint)point
1030 if (![self _prepareAccessibilityCall])
1033 FloatPoint floatPoint = FloatPoint(point);
1034 return [self convertPointToScreenSpace:floatPoint];
1037 - (BOOL)_accessibilityScrollToVisible
1039 if (![self _prepareAccessibilityCall])
1042 m_object->scrollToMakeVisible();
1047 - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction
1049 if (![self _prepareAccessibilityCall])
1052 ScrollView* scrollView = m_object->scrollViewAncestor();
1056 IntPoint scrollPosition = scrollView->scrollPosition();
1057 IntPoint newScrollPosition = scrollPosition;
1058 IntSize scrollSize = scrollView->contentsSize();
1059 IntRect scrollVisibleRect = scrollView->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
1060 switch (direction) {
1061 case UIAccessibilityScrollDirectionRight: {
1062 int scrollAmount = scrollVisibleRect.size().width();
1063 int newX = scrollPosition.x() - scrollAmount;
1064 newScrollPosition.setX(std::max(newX, 0));
1067 case UIAccessibilityScrollDirectionLeft: {
1068 int scrollAmount = scrollVisibleRect.size().width();
1069 int newX = scrollAmount + scrollPosition.x();
1070 int maxX = scrollSize.width() - scrollAmount;
1071 newScrollPosition.setX(std::min(newX, maxX));
1074 case UIAccessibilityScrollDirectionUp: {
1075 int scrollAmount = scrollVisibleRect.size().height();
1076 int newY = scrollPosition.y() - scrollAmount;
1077 newScrollPosition.setY(std::max(newY, 0));
1080 case UIAccessibilityScrollDirectionDown: {
1081 int scrollAmount = scrollVisibleRect.size().height();
1082 int newY = scrollAmount + scrollPosition.y();
1083 int maxY = scrollSize.height() - scrollAmount;
1084 newScrollPosition.setY(std::min(newY, maxY));
1091 if (newScrollPosition != scrollPosition) {
1092 scrollView->setScrollPosition(newScrollPosition);
1093 m_object->document()->updateLayoutIgnorePendingStylesheets();
1096 [self postScrollStatusChangeNotification];
1098 // This means that this object handled the scroll and no other ancestor should attempt scrolling.
1102 - (CGPoint)convertPointToScreenSpace:(FloatPoint &)point
1107 CGPoint cgPoint = CGPointMake(point.x(), point.y());
1109 FrameView* frameView = m_object->documentFrameView();
1110 WAKView* documentView = frameView ? frameView->documentView() : nullptr;
1112 cgPoint = [documentView convertPoint:cgPoint toView:nil];
1114 // we need the web document view to give us our final screen coordinates
1115 // because that can take account of the scroller
1116 id webDocument = [self _accessibilityWebDocumentView];
1118 cgPoint = [webDocument convertPoint:cgPoint toView:nil];
1121 // Find the appropriate scroll view to use to convert the contents to the window.
1122 ScrollView* scrollView = nullptr;
1123 AccessibilityObject* parent = nullptr;
1124 for (parent = m_object->parentObject(); parent; parent = parent->parentObject()) {
1125 if (is<AccessibilityScrollView>(*parent)) {
1126 scrollView = downcast<AccessibilityScrollView>(*parent).scrollView();
1131 IntPoint intPoint = flooredIntPoint(point);
1133 intPoint = scrollView->contentsToRootView(intPoint);
1135 Page* page = m_object->page();
1137 // If we have an empty chrome client (like SVG) then we should use the page
1138 // of the scroll view parent to help us get to the screen rect.
1139 if (parent && page && page->chrome().client().isEmptyChromeClient())
1140 page = parent->page();
1143 IntRect rect = IntRect(intPoint, IntSize(0, 0));
1144 intPoint = page->chrome().rootViewToAccessibilityScreen(rect).location();
1147 cgPoint = (CGPoint)intPoint;
1153 - (CGRect)convertRectToScreenSpace:(IntRect &)rect
1158 CGSize size = CGSizeMake(rect.size().width(), rect.size().height());
1159 CGPoint point = CGPointMake(rect.x(), rect.y());
1161 CGRect frame = CGRectMake(point.x, point.y, size.width, size.height);
1163 FrameView* frameView = m_object->documentFrameView();
1164 WAKView* documentView = frameView ? frameView->documentView() : nil;
1166 frame = [documentView convertRect:frame toView:nil];
1168 // we need the web document view to give us our final screen coordinates
1169 // because that can take account of the scroller
1170 id webDocument = [self _accessibilityWebDocumentView];
1172 frame = [webDocument convertRect:frame toView:nil];
1175 // Find the appropriate scroll view to use to convert the contents to the window.
1176 ScrollView* scrollView = nullptr;
1177 AccessibilityObject* parent = nullptr;
1178 for (parent = m_object->parentObject(); parent; parent = parent->parentObject()) {
1179 if (is<AccessibilityScrollView>(*parent)) {
1180 scrollView = downcast<AccessibilityScrollView>(*parent).scrollView();
1186 rect = scrollView->contentsToRootView(rect);
1188 Page* page = m_object->page();
1190 // If we have an empty chrome client (like SVG) then we should use the page
1191 // of the scroll view parent to help us get to the screen rect.
1192 if (parent && page && page->chrome().client().isEmptyChromeClient())
1193 page = parent->page();
1196 rect = page->chrome().rootViewToAccessibilityScreen(rect);
1198 frame = (CGRect)rect;
1204 // Used by UIKit accessibility bundle to help determine distance during a hit-test.
1205 - (CGRect)accessibilityElementRect
1207 if (![self _prepareAccessibilityCall])
1210 LayoutRect rect = m_object->elementRect();
1211 return CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
1214 // The "center point" is where VoiceOver will "press" an object. This may not be the actual
1215 // center of the accessibilityFrame
1216 - (CGPoint)accessibilityActivationPoint
1218 if (![self _prepareAccessibilityCall])
1221 IntRect rect = snappedIntRect(m_object->boundingBoxRect());
1222 CGRect cgRect = [self convertRectToScreenSpace:rect];
1223 return CGPointMake(CGRectGetMidX(cgRect), CGRectGetMidY(cgRect));
1226 - (CGRect)accessibilityFrame
1228 if (![self _prepareAccessibilityCall])
1231 IntRect rect = snappedIntRect(m_object->elementRect());
1232 return [self convertRectToScreenSpace:rect];
1235 // Checks whether a link contains only static text and images (and has been divided unnaturally by <spans> and other nefarious mechanisms).
1236 - (BOOL)containsUnnaturallySegmentedChildren
1241 AccessibilityRole role = m_object->roleValue();
1242 if (role != LinkRole && role != WebCoreLinkRole)
1245 const auto& children = m_object->children();
1246 unsigned childrenSize = children.size();
1248 // If there's only one child, then it doesn't have segmented children.
1249 if (childrenSize == 1)
1252 for (unsigned i = 0; i < childrenSize; ++i) {
1253 AccessibilityRole role = children[i]->roleValue();
1254 if (role != StaticTextRole && role != ImageRole && role != GroupRole)
1261 - (id)accessibilityContainer
1263 if (![self _prepareAccessibilityCall])
1266 AXAttributeCacheEnabler enableCache(m_object->axObjectCache());
1268 // As long as there's a parent wrapper, that's the correct chain to climb.
1269 AccessibilityObject* parent = m_object->parentObjectUnignored();
1271 return parent->wrapper();
1273 // Mock objects can have their parents detached but still exist in the cache.
1274 if (m_object->isDetachedFromParent())
1277 // The only object without a parent wrapper at this point should be a scroll view.
1278 ASSERT(m_object->isAccessibilityScrollView());
1280 // Verify this is the top document. If not, we might need to go through the platform widget.
1281 FrameView* frameView = m_object->documentFrameView();
1282 Document* document = m_object->document();
1283 if (document && frameView && document != &document->topDocument())
1284 return frameView->platformWidget();
1286 // The top scroll view's parent is the web document view.
1287 return [self _accessibilityWebDocumentView];
1290 - (id)accessibilityFocusedUIElement
1292 if (![self _prepareAccessibilityCall])
1295 AccessibilityObject* focusedObj = m_object->focusedUIElement();
1300 return focusedObj->wrapper();
1303 - (id)_accessibilityWebDocumentView
1305 if (![self _prepareAccessibilityCall])
1308 // This method performs the crucial task of connecting to the UIWebDocumentView.
1309 // This is needed to correctly calculate the screen position of the AX object.
1310 static Class webViewClass = nil;
1312 webViewClass = NSClassFromString(@"WebView");
1317 FrameView* frameView = m_object->documentFrameView();
1322 // If this is the top level frame, the UIWebDocumentView should be returned.
1323 id parentView = frameView->documentView();
1324 while (parentView && ![parentView isKindOfClass:webViewClass])
1325 parentView = [parentView superview];
1327 // The parentView should have an accessibilityContainer, if the UIKit accessibility bundle was loaded.
1328 // The exception is DRT, which tests accessibility without the entire system turning accessibility on. Hence,
1329 // this check should be valid for everything except DRT.
1330 ASSERT([parentView accessibilityContainer] || applicationIsDumpRenderTree());
1332 return [parentView accessibilityContainer];
1335 - (NSArray *)_accessibilityNextElementsWithCount:(UInt32)count
1337 if (![self _prepareAccessibilityCall])
1340 return [[self _accessibilityWebDocumentView] _accessibilityNextElementsWithCount:count];
1343 - (NSArray *)_accessibilityPreviousElementsWithCount:(UInt32)count
1345 if (![self _prepareAccessibilityCall])
1348 return [[self _accessibilityWebDocumentView] _accessibilityPreviousElementsWithCount:count];
1351 - (BOOL)accessibilityRequired
1353 if (![self _prepareAccessibilityCall])
1356 return m_object->isRequired();
1359 - (NSArray *)accessibilityFlowToElements
1361 if (![self _prepareAccessibilityCall])
1364 AccessibilityObject::AccessibilityChildrenVector children;
1365 m_object->ariaFlowToElements(children);
1367 unsigned length = children.size();
1368 NSMutableArray* array = [NSMutableArray arrayWithCapacity:length];
1369 for (unsigned i = 0; i < length; ++i) {
1370 AccessibilityObjectWrapper* wrapper = children[i]->wrapper();
1375 if (children[i]->isAttachment() && [wrapper attachmentView])
1376 [array addObject:[wrapper attachmentView]];
1378 [array addObject:wrapper];
1383 - (id)accessibilityLinkedElement
1385 if (![self _prepareAccessibilityCall])
1388 // If this static text inside of a link, it should use its parent's linked element.
1389 AccessibilityObject* element = m_object;
1390 if (m_object->roleValue() == StaticTextRole && m_object->parentObjectUnignored()->isLink())
1391 element = m_object->parentObjectUnignored();
1393 AccessibilityObject::AccessibilityChildrenVector children;
1394 element->linkedUIElements(children);
1395 if (children.size() == 0)
1398 return children[0]->wrapper();
1402 - (BOOL)isAttachment
1407 return m_object->isAttachment();
1410 - (void)_accessibilityActivate
1412 if (![self _prepareAccessibilityCall])
1418 - (id)attachmentView
1420 if (![self _prepareAccessibilityCall])
1423 ASSERT([self isAttachment]);
1424 Widget* widget = m_object->widgetForAttachmentView();
1427 return widget->platformWidget();
1430 static RenderObject* rendererForView(WAKView* view)
1432 if (![view conformsToProtocol:@protocol(WebCoreFrameView)])
1435 WAKView<WebCoreFrameView>* frameView = (WAKView<WebCoreFrameView>*)view;
1436 Frame* frame = [frameView _web_frame];
1440 Node* node = frame->document()->ownerElement();
1444 return node->renderer();
1447 - (id)_accessibilityParentForSubview:(id)subview
1449 RenderObject* renderer = rendererForView(subview);
1453 AccessibilityObject* obj = renderer->document().axObjectCache()->getOrCreate(renderer);
1455 return obj->parentObjectUnignored()->wrapper();
1459 - (void)postFocusChangeNotification
1461 // The UIKit accessibility wrapper will override and post appropriate notification.
1464 - (void)postSelectedTextChangeNotification
1466 // The UIKit accessibility wrapper will override and post appropriate notification.
1469 - (void)postLayoutChangeNotification
1471 // The UIKit accessibility wrapper will override and post appropriate notification.
1474 - (void)postLiveRegionChangeNotification
1476 // The UIKit accessibility wrapper will override and post appropriate notification.
1479 - (void)postLiveRegionCreatedNotification
1481 // The UIKit accessibility wrapper will override and post appropriate notification.
1484 - (void)postLoadCompleteNotification
1486 // The UIKit accessibility wrapper will override and post appropriate notification.
1489 - (void)postChildrenChangedNotification
1491 // The UIKit accessibility wrapper will override and post appropriate notification.
1494 - (void)postInvalidStatusChangedNotification
1496 // The UIKit accessibility wrapper will override and post appropriate notification.
1499 - (void)postValueChangedNotification
1501 // The UIKit accessibility wrapper will override and post appropriate notification.
1504 - (void)postScrollStatusChangeNotification
1506 // The UIKit accessibility wrapper will override and post appropriate notification.
1509 // These will be used by the UIKit wrapper to calculate an appropriate description of scroll status.
1510 - (CGPoint)_accessibilityScrollPosition
1512 if (![self _prepareAccessibilityCall])
1515 ScrollView* scrollView = m_object->scrollViewAncestor();
1518 return scrollView->scrollPosition();
1521 - (CGSize)_accessibilityScrollSize
1523 if (![self _prepareAccessibilityCall])
1526 ScrollView* scrollView = m_object->scrollViewAncestor();
1529 return scrollView->contentsSize();
1532 - (CGRect)_accessibilityScrollVisibleRect
1534 if (![self _prepareAccessibilityCall])
1537 ScrollView* scrollView = m_object->scrollViewAncestor();
1540 return scrollView->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
1543 - (void)accessibilityElementDidBecomeFocused
1545 if (![self _prepareAccessibilityCall])
1548 // The focused VoiceOver element might be the text inside a link.
1549 // In those cases we should focus on the link itself.
1550 for (AccessibilityObject* object = m_object; object != nil; object = object->parentObject()) {
1551 if (object->roleValue() == WebAreaRole)
1554 if (object->canSetFocusAttribute()) {
1555 object->setFocused(true);
1561 - (void)accessibilityModifySelection:(TextGranularity)granularity increase:(BOOL)increase
1563 if (![self _prepareAccessibilityCall])
1566 FrameSelection& frameSelection = m_object->document()->frame()->selection();
1567 VisibleSelection selection = m_object->selection();
1568 VisiblePositionRange range = m_object->visiblePositionRange();
1570 // Before a selection with length exists, the cursor position needs to move to the right starting place.
1571 // That should be the beginning of this element (range.start). However, if the cursor is already within the
1572 // range of this element (the cursor is represented by selection), then the cursor does not need to move.
1573 if (frameSelection.isNone() && (selection.visibleStart() < range.start || selection.visibleEnd() > range.end))
1574 frameSelection.moveTo(range.start, UserTriggered);
1576 frameSelection.modify(FrameSelection::AlterationExtend, (increase) ? DirectionRight : DirectionLeft, granularity, UserTriggered);
1579 - (void)accessibilityIncreaseSelection:(TextGranularity)granularity
1581 [self accessibilityModifySelection:granularity increase:YES];
1584 - (void)accessibilityDecreaseSelection:(TextGranularity)granularity
1586 [self accessibilityModifySelection:granularity increase:NO];
1589 - (void)accessibilityMoveSelectionToMarker:(WebAccessibilityTextMarker *)marker
1591 if (![self _prepareAccessibilityCall])
1594 VisiblePosition visiblePosition = [marker visiblePosition];
1595 if (visiblePosition.isNull())
1598 FrameSelection& frameSelection = m_object->document()->frame()->selection();
1599 frameSelection.moveTo(visiblePosition, UserTriggered);
1602 - (void)accessibilityIncrement
1604 if (![self _prepareAccessibilityCall])
1607 m_object->increment();
1610 - (void)accessibilityDecrement
1612 if (![self _prepareAccessibilityCall])
1615 m_object->decrement();
1618 #pragma mark Accessibility Text Marker Handlers
1620 - (BOOL)_addAccessibilityObject:(AccessibilityObject*)axObject toTextMarkerArray:(NSMutableArray *)array
1625 AccessibilityObjectWrapper* wrapper = axObject->wrapper();
1629 // Don't add the same object twice, but since this has already been added, we should return
1630 // YES because we want to inform that it's in the array
1631 if ([array containsObject:wrapper])
1634 // Explicity set that this is now an element (in case other logic tries to override).
1635 [wrapper setValue:[NSNumber numberWithBool:YES] forKey:@"isAccessibilityElement"];
1636 [array addObject:wrapper];
1640 - (void)_accessibilitySetValue:(NSString *)string
1642 if (![self _prepareAccessibilityCall])
1644 m_object->setValue(string);
1647 - (NSString *)stringForTextMarkers:(NSArray *)markers
1649 if (![self _prepareAccessibilityCall])
1652 if ([markers count] != 2)
1655 WebAccessibilityTextMarker* startMarker = [markers objectAtIndex:0];
1656 WebAccessibilityTextMarker* endMarker = [markers objectAtIndex:1];
1657 if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
1660 // extract the start and end VisiblePosition
1661 VisiblePosition startVisiblePosition = [startMarker visiblePosition];
1662 if (startVisiblePosition.isNull())
1665 VisiblePosition endVisiblePosition = [endMarker visiblePosition];
1666 if (endVisiblePosition.isNull())
1669 VisiblePositionRange visiblePosRange = VisiblePositionRange(startVisiblePosition, endVisiblePosition);
1670 return m_object->stringForVisiblePositionRange(visiblePosRange);
1673 static int blockquoteLevel(RenderObject* renderer)
1679 for (Node* node = renderer->node(); node; node = node->parentNode()) {
1680 if (node->hasTagName(blockquoteTag))
1687 static void AXAttributeStringSetLanguage(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1692 AccessibilityObject* axObject = renderer->document().axObjectCache()->getOrCreate(renderer);
1693 NSString *language = axObject->language();
1694 if ([language length])
1695 [attrString addAttribute:UIAccessibilityTokenLanguage value:language range:range];
1697 [attrString removeAttribute:UIAccessibilityTokenLanguage range:range];
1700 static void AXAttributeStringSetBlockquoteLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1702 int quoteLevel = blockquoteLevel(renderer);
1705 [attrString addAttribute:UIAccessibilityTokenBlockquoteLevel value:[NSNumber numberWithInt:quoteLevel] range:range];
1707 [attrString removeAttribute:UIAccessibilityTokenBlockquoteLevel range:range];
1710 static void AXAttributeStringSetHeadingLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1715 AccessibilityObject* parentObject = renderer->document().axObjectCache()->getOrCreate(renderer->parent());
1716 int parentHeadingLevel = parentObject->headingLevel();
1718 if (parentHeadingLevel)
1719 [attrString addAttribute:UIAccessibilityTokenHeadingLevel value:[NSNumber numberWithInt:parentHeadingLevel] range:range];
1721 [attrString removeAttribute:UIAccessibilityTokenHeadingLevel range:range];
1724 static void AXAttributeStringSetFont(NSMutableAttributedString* attrString, CTFontRef font, NSRange range)
1729 RetainPtr<CFStringRef> fullName = adoptCF(CTFontCopyFullName(font));
1730 RetainPtr<CFStringRef> familyName = adoptCF(CTFontCopyFamilyName(font));
1732 NSNumber* size = [NSNumber numberWithFloat:CTFontGetSize(font)];
1733 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
1734 NSNumber* bold = [NSNumber numberWithBool:(traits & kCTFontTraitBold)];
1736 [attrString addAttribute:UIAccessibilityTokenFontName value:(NSString*)fullName.get() range:range];
1738 [attrString addAttribute:UIAccessibilityTokenFontFamily value:(NSString*)familyName.get() range:range];
1739 if ([size boolValue])
1740 [attrString addAttribute:UIAccessibilityTokenFontSize value:size range:range];
1741 if ([bold boolValue] || (traits & kCTFontTraitBold))
1742 [attrString addAttribute:UIAccessibilityTokenBold value:[NSNumber numberWithBool:YES] range:range];
1743 if (traits & kCTFontTraitItalic)
1744 [attrString addAttribute:UIAccessibilityTokenItalic value:[NSNumber numberWithBool:YES] range:range];
1748 static void AXAttributeStringSetNumber(NSMutableAttributedString* attrString, NSString* attribute, NSNumber* number, NSRange range)
1751 [attrString addAttribute:attribute value:number range:range];
1753 [attrString removeAttribute:attribute range:range];
1756 static void AXAttributeStringSetStyle(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
1758 RenderStyle& style = renderer->style();
1760 // set basic font info
1761 AXAttributeStringSetFont(attrString, style.fontCascade().primaryFont().getCTFont(), range);
1763 int decor = style.textDecorationsInEffect();
1764 if ((decor & (TextDecorationUnderline | TextDecorationLineThrough)) != 0) {
1765 Color underlineColor, overlineColor, linethroughColor;
1766 TextDecorationStyle underlineStyle, overlineStyle, linethroughStyle;
1767 renderer->getTextDecorationColorsAndStyles(decor, underlineColor, overlineColor, linethroughColor, underlineStyle, overlineStyle, linethroughStyle);
1769 if (decor & TextDecorationUnderline)
1770 AXAttributeStringSetNumber(attrString, UIAccessibilityTokenUnderline, [NSNumber numberWithBool:YES], range);
1774 static void AXAttributedStringAppendText(NSMutableAttributedString* attrString, Node* node, NSString *text)
1776 // skip invisible text
1777 if (!node->renderer())
1780 // easier to calculate the range before appending the string
1781 NSRange attrStringRange = NSMakeRange([attrString length], [text length]);
1783 // append the string from this node
1784 [[attrString mutableString] appendString:text];
1786 // set new attributes
1787 AXAttributeStringSetStyle(attrString, node->renderer(), attrStringRange);
1788 AXAttributeStringSetHeadingLevel(attrString, node->renderer(), attrStringRange);
1789 AXAttributeStringSetBlockquoteLevel(attrString, node->renderer(), attrStringRange);
1790 AXAttributeStringSetLanguage(attrString, node->renderer(), attrStringRange);
1794 // This method is intended to return an array of strings and accessibility elements that
1795 // represent the objects on one line of rendered web content. The array of markers sent
1796 // in should be ordered and contain only a start and end marker.
1797 - (NSArray *)arrayOfTextForTextMarkers:(NSArray *)markers attributed:(BOOL)attributed
1799 if (![self _prepareAccessibilityCall])
1802 if ([markers count] != 2)
1805 WebAccessibilityTextMarker* startMarker = [markers objectAtIndex:0];
1806 WebAccessibilityTextMarker* endMarker = [markers objectAtIndex:1];
1807 if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
1810 // extract the start and end VisiblePosition
1811 VisiblePosition startVisiblePosition = [startMarker visiblePosition];
1812 if (startVisiblePosition.isNull())
1815 VisiblePosition endVisiblePosition = [endMarker visiblePosition];
1816 if (endVisiblePosition.isNull())
1819 // iterate over the range to build the AX attributed string
1820 NSMutableArray* array = [[NSMutableArray alloc] init];
1821 TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get());
1822 for (; !it.atEnd(); it.advance()) {
1823 // locate the node and starting offset for this range
1825 Node* node = it.range()->startContainer(exception);
1826 ASSERT(node == it.range()->endContainer(exception));
1827 int offset = it.range()->startOffset(exception);
1829 // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
1830 if (it.text().length() != 0) {
1832 // First check if this is represented by a link.
1833 AccessibilityObject* linkObject = AccessibilityObject::anchorElementForNode(node);
1834 if ([self _addAccessibilityObject:linkObject toTextMarkerArray:array])
1837 // Next check if this region is represented by a heading.
1838 AccessibilityObject* headingObject = AccessibilityObject::headingElementForNode(node);
1839 if ([self _addAccessibilityObject:headingObject toTextMarkerArray:array])
1842 String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition()));
1844 if (!listMarkerText.isEmpty())
1845 [array addObject:listMarkerText];
1846 // There was not an element representation, so just return the text.
1847 [array addObject:it.text().createNSString().get()];
1851 String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition()));
1853 if (!listMarkerText.isEmpty()) {
1854 NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init];
1855 AXAttributedStringAppendText(attrString, node, listMarkerText);
1856 [array addObject:attrString];
1857 [attrString release];
1860 NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
1861 AXAttributedStringAppendText(attrString, node, it.text().createNSStringWithoutCopying().get());
1862 [array addObject:attrString];
1863 [attrString release];
1866 Node* replacedNode = node->traverseToChildAt(offset);
1868 AccessibilityObject* obj = m_object->axObjectCache()->getOrCreate(replacedNode->renderer());
1869 if (obj && !obj->accessibilityIsIgnored())
1870 [self _addAccessibilityObject:obj toTextMarkerArray:array];
1875 return [array autorelease];
1878 - (NSRange)_convertToNSRange:(Range *)range
1880 if (!range || !range->startContainer())
1881 return NSMakeRange(NSNotFound, 0);
1883 Document* document = m_object->document();
1884 Element* selectionRoot = document->frame()->selection().selection().rootEditableElement();
1885 Element* scope = selectionRoot ? selectionRoot : document->documentElement();
1887 // Mouse events may cause TSM to attempt to create an NSRange for a portion of the view
1888 // that is not inside the current editable region. These checks ensure we don't produce
1889 // potentially invalid data when responding to such requests.
1890 if (range->startContainer() != scope && !range->startContainer()->isDescendantOf(scope))
1891 return NSMakeRange(NSNotFound, 0);
1892 if (range->endContainer() != scope && !range->endContainer()->isDescendantOf(scope))
1893 return NSMakeRange(NSNotFound, 0);
1895 RefPtr<Range> testRange = Range::create(scope->document(), scope, 0, range->startContainer(), range->startOffset());
1896 ASSERT(testRange->startContainer() == scope);
1897 int startPosition = TextIterator::rangeLength(testRange.get());
1900 testRange->setEnd(range->endContainer(), range->endOffset(), ec);
1901 ASSERT(testRange->startContainer() == scope);
1902 int endPosition = TextIterator::rangeLength(testRange.get());
1903 return NSMakeRange(startPosition, endPosition - startPosition);
1906 - (PassRefPtr<Range>)_convertToDOMRange:(NSRange)nsrange
1908 if (nsrange.location > INT_MAX)
1910 if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX)
1911 nsrange.length = INT_MAX - nsrange.location;
1913 // our critical assumption is that we are only called by input methods that
1914 // concentrate on a given area containing the selection
1915 // We have to do this because of text fields and textareas. The DOM for those is not
1916 // directly in the document DOM, so serialization is problematic. Our solution is
1917 // to use the root editable element of the selection start as the positional base.
1918 // That fits with AppKit's idea of an input context.
1919 Document* document = m_object->document();
1920 Element* selectionRoot = document->frame()->selection().selection().rootEditableElement();
1921 Element* scope = selectionRoot ? selectionRoot : document->documentElement();
1922 return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length);
1925 // This method is intended to take a text marker representing a VisiblePosition and convert it
1926 // into a normalized location within the document.
1927 - (NSInteger)positionForTextMarker:(WebAccessibilityTextMarker *)marker
1929 if (![self _prepareAccessibilityCall])
1935 VisibleSelection selection([marker visiblePosition]);
1936 RefPtr<Range> range = selection.toNormalizedRange();
1937 NSRange nsRange = [self _convertToNSRange:range.get()];
1938 return nsRange.location;
1941 - (NSArray *)textMarkerRange
1943 if (![self _prepareAccessibilityCall])
1946 VisiblePositionRange range = m_object->visiblePositionRange();
1947 VisiblePosition startPosition = range.start;
1948 VisiblePosition endPosition = range.end;
1949 WebAccessibilityTextMarker* start = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:startPosition cache:m_object->axObjectCache()];
1950 WebAccessibilityTextMarker* end = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:endPosition cache:m_object->axObjectCache()];
1952 return [NSArray arrayWithObjects:start, end, nil];
1955 // A method to get the normalized text cursor range of an element. Used in DumpRenderTree.
1956 - (NSRange)elementTextRange
1958 if (![self _prepareAccessibilityCall])
1959 return NSMakeRange(NSNotFound, 0);
1961 NSArray *markers = [self textMarkerRange];
1962 if ([markers count] != 2)
1963 return NSMakeRange(NSNotFound, 0);
1965 WebAccessibilityTextMarker *startMarker = [markers objectAtIndex:0];
1966 WebAccessibilityTextMarker *endMarker = [markers objectAtIndex:1];
1968 NSInteger startPosition = [self positionForTextMarker:startMarker];
1969 NSInteger endPosition = [self positionForTextMarker:endMarker];
1971 return NSMakeRange(startPosition, endPosition - startPosition);
1974 - (AccessibilityObjectWrapper *)accessibilityObjectForTextMarker:(WebAccessibilityTextMarker *)marker
1976 if (![self _prepareAccessibilityCall])
1982 VisiblePosition visiblePosition = [marker visiblePosition];
1983 AccessibilityObject* obj = m_object->accessibilityObjectForPosition(visiblePosition);
1987 return AccessibilityUnignoredAncestor(obj->wrapper());
1990 - (NSArray *)textMarkerRangeForSelection
1992 if (![self _prepareAccessibilityCall])
1995 VisibleSelection selection = m_object->selection();
1996 if (selection.isNone())
1998 VisiblePosition startPosition = selection.visibleStart();
1999 VisiblePosition endPosition = selection.visibleEnd();
2001 WebAccessibilityTextMarker* startMarker = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:startPosition cache:m_object->axObjectCache()];
2002 WebAccessibilityTextMarker* endMarker = [WebAccessibilityTextMarker textMarkerWithVisiblePosition:endPosition cache:m_object->axObjectCache()];
2003 if (!startMarker || !endMarker)
2006 return [NSArray arrayWithObjects:startMarker, endMarker, nil];
2009 - (WebAccessibilityTextMarker *)textMarkerForPosition:(NSInteger)position
2011 if (![self _prepareAccessibilityCall])
2014 PassRefPtr<Range> range = [self _convertToDOMRange:NSMakeRange(position, 0)];
2018 VisibleSelection selection = VisibleSelection(range.get(), DOWNSTREAM);
2020 VisiblePosition visiblePosition = selection.visibleStart();
2021 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:visiblePosition cache:m_object->axObjectCache()];
2024 - (id)_stringForRange:(NSRange)range attributed:(BOOL)attributed
2026 if (![self _prepareAccessibilityCall])
2029 WebAccessibilityTextMarker* startMarker = [self textMarkerForPosition:range.location];
2030 WebAccessibilityTextMarker* endMarker = [self textMarkerForPosition:NSMaxRange(range)];
2032 // Clients don't always know the exact range, rather than force them to compute it,
2033 // allow clients to overshoot and use the max text marker range.
2034 if (!startMarker || !endMarker) {
2035 NSArray *markers = [self textMarkerRange];
2036 if ([markers count] != 2)
2039 startMarker = [markers objectAtIndex:0];
2041 endMarker = [markers objectAtIndex:1];
2044 NSArray* array = [self arrayOfTextForTextMarkers:[NSArray arrayWithObjects:startMarker, endMarker, nil] attributed:attributed];
2045 Class returnClass = attributed ? [NSMutableAttributedString class] : [NSMutableString class];
2046 id returnValue = [[(NSString *)[returnClass alloc] init] autorelease];
2048 NSInteger count = [array count];
2049 for (NSInteger k = 0; k < count; ++k) {
2050 id object = [array objectAtIndex:k];
2052 if (![object isKindOfClass:returnClass])
2056 [(NSMutableAttributedString *)returnValue appendAttributedString:object];
2058 [(NSMutableString *)returnValue appendString:object];
2064 // A convenience method for getting the text of a NSRange. Currently used only by DRT.
2065 - (NSString *)stringForRange:(NSRange)range
2067 return [self _stringForRange:range attributed:NO];
2070 - (NSAttributedString *)attributedStringForRange:(NSRange)range
2072 return [self _stringForRange:range attributed:YES];
2075 - (NSRange)_accessibilitySelectedTextRange
2077 if (![self _prepareAccessibilityCall] || !m_object->isTextControl())
2078 return NSMakeRange(NSNotFound, 0);
2080 PlainTextRange textRange = m_object->selectedTextRange();
2081 if (textRange.isNull())
2082 return NSMakeRange(NSNotFound, 0);
2083 return NSMakeRange(textRange.start, textRange.length);
2086 - (void)_accessibilitySetSelectedTextRange:(NSRange)range
2088 if (![self _prepareAccessibilityCall] || !m_object->isTextControl())
2091 m_object->setSelectedTextRange(PlainTextRange(range.location, range.length));
2094 // A convenience method for getting the accessibility objects of a NSRange. Currently used only by DRT.
2095 - (NSArray *)elementsForRange:(NSRange)range
2097 if (![self _prepareAccessibilityCall])
2100 WebAccessibilityTextMarker* startMarker = [self textMarkerForPosition:range.location];
2101 WebAccessibilityTextMarker* endMarker = [self textMarkerForPosition:NSMaxRange(range)];
2102 if (!startMarker || !endMarker)
2105 NSArray* array = [self arrayOfTextForTextMarkers:[NSArray arrayWithObjects:startMarker, endMarker, nil] attributed:NO];
2106 NSMutableArray* elements = [NSMutableArray array];
2107 for (id element in array) {
2108 if (![element isKindOfClass:[AccessibilityObjectWrapper class]])
2110 [elements addObject:element];
2115 - (NSString *)selectionRangeString
2117 NSArray *markers = [self textMarkerRangeForSelection];
2118 return [self stringForTextMarkers:markers];
2121 - (WebAccessibilityTextMarker *)selectedTextMarker
2123 if (![self _prepareAccessibilityCall])
2126 VisibleSelection selection = m_object->selection();
2127 VisiblePosition position = selection.visibleStart();
2129 // if there's no selection, start at the top of the document
2130 if (position.isNull())
2131 position = startOfDocument(m_object->document());
2133 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:position cache:m_object->axObjectCache()];
2136 // This method is intended to return the marker at the end of the line starting at
2137 // the marker that is passed into the method.
2138 - (WebAccessibilityTextMarker *)lineEndMarkerForMarker:(WebAccessibilityTextMarker *)marker
2140 if (![self _prepareAccessibilityCall])
2146 VisiblePosition start = [marker visiblePosition];
2147 VisiblePosition lineEnd = m_object->nextLineEndPosition(start);
2149 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:lineEnd cache:m_object->axObjectCache()];
2152 // This method is intended to return the marker at the start of the line starting at
2153 // the marker that is passed into the method.
2154 - (WebAccessibilityTextMarker *)lineStartMarkerForMarker:(WebAccessibilityTextMarker *)marker
2156 if (![self _prepareAccessibilityCall])
2162 VisiblePosition start = [marker visiblePosition];
2163 VisiblePosition lineStart = m_object->previousLineStartPosition(start);
2165 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:lineStart cache:m_object->axObjectCache()];
2168 - (WebAccessibilityTextMarker *)nextMarkerForMarker:(WebAccessibilityTextMarker *)marker
2170 if (![self _prepareAccessibilityCall])
2176 VisiblePosition start = [marker visiblePosition];
2177 VisiblePosition nextMarker = m_object->nextVisiblePosition(start);
2179 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:nextMarker cache:m_object->axObjectCache()];
2182 // This method is intended to return the marker at the start of the line starting at
2183 // the marker that is passed into the method.
2184 - (WebAccessibilityTextMarker *)previousMarkerForMarker:(WebAccessibilityTextMarker *)marker
2186 if (![self _prepareAccessibilityCall])
2192 VisiblePosition start = [marker visiblePosition];
2193 VisiblePosition previousMarker = m_object->previousVisiblePosition(start);
2195 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:previousMarker cache:m_object->axObjectCache()];
2198 // This method is intended to return the bounds of a text marker range in screen coordinates.
2199 - (CGRect)frameForTextMarkers:(NSArray *)array
2201 if (![self _prepareAccessibilityCall])
2204 if ([array count] != 2)
2207 WebAccessibilityTextMarker* startMarker = [array objectAtIndex:0];
2208 WebAccessibilityTextMarker* endMarker = [array objectAtIndex:1];
2209 if (![startMarker isKindOfClass:[WebAccessibilityTextMarker class]] || ![endMarker isKindOfClass:[WebAccessibilityTextMarker class]])
2212 IntRect rect = m_object->boundsForVisiblePositionRange(VisiblePositionRange([startMarker visiblePosition], [endMarker visiblePosition]));
2213 return [self convertRectToScreenSpace:rect];
2216 - (WebAccessibilityTextMarker *)textMarkerForPoint:(CGPoint)point
2218 if (![self _prepareAccessibilityCall])
2221 VisiblePosition pos = m_object->visiblePositionForPoint(IntPoint(point));
2222 return [WebAccessibilityTextMarker textMarkerWithVisiblePosition:pos cache:m_object->axObjectCache()];
2225 - (NSString *)accessibilityIdentifier
2227 if (![self _prepareAccessibilityCall])
2230 return m_object->getAttribute(HTMLNames::idAttr);
2233 - (NSString *)accessibilitySpeechHint
2235 if (![self _prepareAccessibilityCall])
2238 switch (m_object->speakProperty()) {
2245 return @"spell-out";
2248 case SpeakLiteralPunctuation:
2249 return @"literal-punctuation";
2250 case SpeakNoPunctuation:
2251 return @"no-punctuation";
2257 - (BOOL)accessibilityARIAIsBusy
2259 if (![self _prepareAccessibilityCall])
2262 return m_object->ariaLiveRegionBusy();
2265 - (NSString *)accessibilityARIALiveRegionStatus
2267 if (![self _prepareAccessibilityCall])
2270 return m_object->ariaLiveRegionStatus();
2273 - (NSString *)accessibilityARIARelevantStatus
2275 if (![self _prepareAccessibilityCall])
2278 return m_object->ariaLiveRegionRelevant();
2281 - (BOOL)accessibilityARIALiveRegionIsAtomic
2283 if (![self _prepareAccessibilityCall])
2286 return m_object->ariaLiveRegionAtomic();
2289 - (BOOL)accessibilitySupportsARIAExpanded
2291 if (![self _prepareAccessibilityCall])
2294 return m_object->supportsARIAExpanded();
2297 - (BOOL)accessibilityIsExpanded
2299 if (![self _prepareAccessibilityCall])
2302 return m_object->isExpanded();
2305 - (NSString *)accessibilityInvalidStatus
2307 if (![self _prepareAccessibilityCall])
2310 return m_object->invalidStatus();
2313 - (WebAccessibilityObjectWrapper *)accessibilityMathRootIndexObject
2315 if (![self _prepareAccessibilityCall])
2318 return m_object->mathRootIndexObject() ? m_object->mathRootIndexObject()->wrapper() : 0;
2321 - (WebAccessibilityObjectWrapper *)accessibilityMathRadicandObject
2323 if (![self _prepareAccessibilityCall])
2326 return m_object->mathRadicandObject() ? m_object->mathRadicandObject()->wrapper() : 0;
2329 - (WebAccessibilityObjectWrapper *)accessibilityMathNumeratorObject
2331 if (![self _prepareAccessibilityCall])
2334 return m_object->mathNumeratorObject() ? m_object->mathNumeratorObject()->wrapper() : 0;
2337 - (WebAccessibilityObjectWrapper *)accessibilityMathDenominatorObject
2339 if (![self _prepareAccessibilityCall])
2342 return m_object->mathDenominatorObject() ? m_object->mathDenominatorObject()->wrapper() : 0;
2345 - (WebAccessibilityObjectWrapper *)accessibilityMathBaseObject
2347 if (![self _prepareAccessibilityCall])
2350 return m_object->mathBaseObject() ? m_object->mathBaseObject()->wrapper() : 0;
2353 - (WebAccessibilityObjectWrapper *)accessibilityMathSubscriptObject
2355 if (![self _prepareAccessibilityCall])
2358 return m_object->mathSubscriptObject() ? m_object->mathSubscriptObject()->wrapper() : 0;
2361 - (WebAccessibilityObjectWrapper *)accessibilityMathSuperscriptObject
2363 if (![self _prepareAccessibilityCall])
2366 return m_object->mathSuperscriptObject() ? m_object->mathSuperscriptObject()->wrapper() : 0;
2369 - (WebAccessibilityObjectWrapper *)accessibilityMathUnderObject
2371 if (![self _prepareAccessibilityCall])
2374 return m_object->mathUnderObject() ? m_object->mathUnderObject()->wrapper() : 0;
2377 - (WebAccessibilityObjectWrapper *)accessibilityMathOverObject
2379 if (![self _prepareAccessibilityCall])
2382 return m_object->mathOverObject() ? m_object->mathOverObject()->wrapper() : 0;
2385 - (NSString *)accessibilityPlatformMathSubscriptKey
2387 return @"AXMSubscriptObject";
2390 - (NSString *)accessibilityPlatformMathSuperscriptKey
2392 return @"AXMSuperscriptObject";
2395 - (NSArray *)accessibilityMathPostscripts
2397 if (![self _prepareAccessibilityCall])
2400 return [self accessibilityMathPostscriptPairs];
2403 - (NSArray *)accessibilityMathPrescripts
2405 if (![self _prepareAccessibilityCall])
2408 return [self accessibilityMathPrescriptPairs];
2411 - (NSString *)accessibilityMathFencedOpenString
2413 if (![self _prepareAccessibilityCall])
2416 return m_object->mathFencedOpenString();
2419 - (NSString *)accessibilityMathFencedCloseString
2421 if (![self _prepareAccessibilityCall])
2424 return m_object->mathFencedCloseString();
2427 - (BOOL)accessibilityIsMathTopObject
2429 if (![self _prepareAccessibilityCall])
2432 return m_object->roleValue() == DocumentMathRole;
2435 - (NSInteger)accessibilityMathLineThickness
2437 if (![self _prepareAccessibilityCall])
2440 return m_object->mathLineThickness();
2443 - (NSString *)accessibilityMathType
2445 if (![self _prepareAccessibilityCall])
2448 if (m_object->roleValue() == MathElementRole) {
2449 if (m_object->isMathFraction())
2450 return @"AXMathFraction";
2451 if (m_object->isMathFenced())
2452 return @"AXMathFenced";
2453 if (m_object->isMathSubscriptSuperscript())
2454 return @"AXMathSubscriptSuperscript";
2455 if (m_object->isMathRow())
2456 return @"AXMathRow";
2457 if (m_object->isMathUnderOver())
2458 return @"AXMathUnderOver";
2459 if (m_object->isMathSquareRoot())
2460 return @"AXMathSquareRoot";
2461 if (m_object->isMathRoot())
2462 return @"AXMathRoot";
2463 if (m_object->isMathText())
2464 return @"AXMathText";
2465 if (m_object->isMathNumber())
2466 return @"AXMathNumber";
2467 if (m_object->isMathIdentifier())
2468 return @"AXMathIdentifier";
2469 if (m_object->isMathTable())
2470 return @"AXMathTable";
2471 if (m_object->isMathTableRow())
2472 return @"AXMathTableRow";
2473 if (m_object->isMathTableCell())
2474 return @"AXMathTableCell";
2475 if (m_object->isMathFenceOperator())
2476 return @"AXMathFenceOperator";
2477 if (m_object->isMathSeparatorOperator())
2478 return @"AXMathSeparatorOperator";
2479 if (m_object->isMathOperator())
2480 return @"AXMathOperator";
2481 if (m_object->isMathMultiscript())
2482 return @"AXMathMultiscript";
2488 - (CGPoint)accessibilityClickPoint
2490 return m_object->clickPoint();
2493 // These are used by DRT so that it can know when notifications are sent.
2494 // Since they are static, only one callback can be installed at a time (that's all DRT should need).
2495 typedef void (*AXPostedNotificationCallback)(id element, NSString* notification, void* context);
2496 static AXPostedNotificationCallback AXNotificationCallback = nullptr;
2497 static void* AXPostedNotificationContext = nullptr;
2499 - (void)accessibilitySetPostedNotificationCallback:(AXPostedNotificationCallback)function withContext:(void*)context
2501 AXNotificationCallback = function;
2502 AXPostedNotificationContext = context;
2505 - (void)accessibilityPostedNotification:(NSString *)notificationName
2507 if (AXNotificationCallback && notificationName)
2508 AXNotificationCallback(self, notificationName, AXPostedNotificationContext);
2512 - (NSString *)description
2514 CGRect frame = [self accessibilityFrame];
2515 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];
2521 #endif // HAVE(ACCESSIBILITY) && PLATFORM(IOS)