| /* |
| * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of Apple Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #import "config.h" |
| #import "WebAccessibilityObjectWrapperBase.h" |
| |
| #if ENABLE(ACCESSIBILITY) |
| |
| #import "AXIsolatedTree.h" |
| #import "AXIsolatedTreeNode.h" |
| #import "AXObjectCache.h" |
| #import "AccessibilityARIAGridRow.h" |
| #import "AccessibilityList.h" |
| #import "AccessibilityListBox.h" |
| #import "AccessibilityObjectInterface.h" |
| #import "AccessibilityRenderObject.h" |
| #import "AccessibilityScrollView.h" |
| #import "AccessibilitySpinButton.h" |
| #import "AccessibilityTable.h" |
| #import "AccessibilityTableCell.h" |
| #import "AccessibilityTableColumn.h" |
| #import "AccessibilityTableRow.h" |
| #import "Chrome.h" |
| #import "ChromeClient.h" |
| #import "ColorMac.h" |
| #import "ContextMenuController.h" |
| #import "Editing.h" |
| #import "Font.h" |
| #import "FontCascade.h" |
| #import "Frame.h" |
| #import "FrameLoaderClient.h" |
| #import "FrameSelection.h" |
| #import "HTMLNames.h" |
| #import "LayoutRect.h" |
| #import "LocalizedStrings.h" |
| #import "Page.h" |
| #import "RenderTextControl.h" |
| #import "RenderView.h" |
| #import "RenderWidget.h" |
| #import "ScrollView.h" |
| #import "TextCheckerClient.h" |
| #import "TextCheckingHelper.h" |
| #import "VisibleUnits.h" |
| #import "WebCoreFrameView.h" |
| |
| #if PLATFORM(MAC) |
| #import <pal/spi/mac/HIServicesSPI.h> |
| #else |
| #import "WAKView.h" |
| #import "WAKWindow.h" |
| #endif |
| |
| using namespace WebCore; |
| using namespace HTMLNames; |
| |
| // Search Keys |
| #ifndef NSAccessibilityAnyTypeSearchKey |
| #define NSAccessibilityAnyTypeSearchKey @"AXAnyTypeSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityArticleSearchKey |
| #define NSAccessibilityArticleSearchKey @"AXArticleSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityBlockquoteSameLevelSearchKey |
| #define NSAccessibilityBlockquoteSameLevelSearchKey @"AXBlockquoteSameLevelSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityBlockquoteSearchKey |
| #define NSAccessibilityBlockquoteSearchKey @"AXBlockquoteSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityBoldFontSearchKey |
| #define NSAccessibilityBoldFontSearchKey @"AXBoldFontSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityButtonSearchKey |
| #define NSAccessibilityButtonSearchKey @"AXButtonSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityCheckBoxSearchKey |
| #define NSAccessibilityCheckBoxSearchKey @"AXCheckBoxSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityControlSearchKey |
| #define NSAccessibilityControlSearchKey @"AXControlSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityDifferentTypeSearchKey |
| #define NSAccessibilityDifferentTypeSearchKey @"AXDifferentTypeSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityFontChangeSearchKey |
| #define NSAccessibilityFontChangeSearchKey @"AXFontChangeSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityFontColorChangeSearchKey |
| #define NSAccessibilityFontColorChangeSearchKey @"AXFontColorChangeSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityFrameSearchKey |
| #define NSAccessibilityFrameSearchKey @"AXFrameSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityGraphicSearchKey |
| #define NSAccessibilityGraphicSearchKey @"AXGraphicSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityHeadingLevel1SearchKey |
| #define NSAccessibilityHeadingLevel1SearchKey @"AXHeadingLevel1SearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityHeadingLevel2SearchKey |
| #define NSAccessibilityHeadingLevel2SearchKey @"AXHeadingLevel2SearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityHeadingLevel3SearchKey |
| #define NSAccessibilityHeadingLevel3SearchKey @"AXHeadingLevel3SearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityHeadingLevel4SearchKey |
| #define NSAccessibilityHeadingLevel4SearchKey @"AXHeadingLevel4SearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityHeadingLevel5SearchKey |
| #define NSAccessibilityHeadingLevel5SearchKey @"AXHeadingLevel5SearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityHeadingLevel6SearchKey |
| #define NSAccessibilityHeadingLevel6SearchKey @"AXHeadingLevel6SearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityHeadingSameLevelSearchKey |
| #define NSAccessibilityHeadingSameLevelSearchKey @"AXHeadingSameLevelSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityHeadingSearchKey |
| #define NSAccessibilityHeadingSearchKey @"AXHeadingSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityHighlightedSearchKey |
| #define NSAccessibilityHighlightedSearchKey @"AXHighlightedSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityKeyboardFocusableSearchKey |
| #define NSAccessibilityKeyboardFocusableSearchKey @"AXKeyboardFocusableSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityItalicFontSearchKey |
| #define NSAccessibilityItalicFontSearchKey @"AXItalicFontSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityLandmarkSearchKey |
| #define NSAccessibilityLandmarkSearchKey @"AXLandmarkSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityLinkSearchKey |
| #define NSAccessibilityLinkSearchKey @"AXLinkSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityListSearchKey |
| #define NSAccessibilityListSearchKey @"AXListSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityLiveRegionSearchKey |
| #define NSAccessibilityLiveRegionSearchKey @"AXLiveRegionSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityMisspelledWordSearchKey |
| #define NSAccessibilityMisspelledWordSearchKey @"AXMisspelledWordSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityOutlineSearchKey |
| #define NSAccessibilityOutlineSearchKey @"AXOutlineSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityPlainTextSearchKey |
| #define NSAccessibilityPlainTextSearchKey @"AXPlainTextSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityRadioGroupSearchKey |
| #define NSAccessibilityRadioGroupSearchKey @"AXRadioGroupSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilitySameTypeSearchKey |
| #define NSAccessibilitySameTypeSearchKey @"AXSameTypeSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityStaticTextSearchKey |
| #define NSAccessibilityStaticTextSearchKey @"AXStaticTextSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityStyleChangeSearchKey |
| #define NSAccessibilityStyleChangeSearchKey @"AXStyleChangeSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityTableSameLevelSearchKey |
| #define NSAccessibilityTableSameLevelSearchKey @"AXTableSameLevelSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityTableSearchKey |
| #define NSAccessibilityTableSearchKey @"AXTableSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityTextFieldSearchKey |
| #define NSAccessibilityTextFieldSearchKey @"AXTextFieldSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityUnderlineSearchKey |
| #define NSAccessibilityUnderlineSearchKey @"AXUnderlineSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityUnvisitedLinkSearchKey |
| #define NSAccessibilityUnvisitedLinkSearchKey @"AXUnvisitedLinkSearchKey" |
| #endif |
| |
| #ifndef NSAccessibilityVisitedLinkSearchKey |
| #define NSAccessibilityVisitedLinkSearchKey @"AXVisitedLinkSearchKey" |
| #endif |
| |
| // Search |
| #ifndef NSAccessibilityImmediateDescendantsOnly |
| #define NSAccessibilityImmediateDescendantsOnly @"AXImmediateDescendantsOnly" |
| #endif |
| |
| #define _axBackingObject self.axBackingObject |
| |
| static NSArray *convertMathPairsToNSArray(const AccessibilityObject::AccessibilityMathMultiscriptPairs& pairs, NSString *subscriptKey, NSString *superscriptKey) |
| { |
| NSMutableArray *array = [NSMutableArray arrayWithCapacity:pairs.size()]; |
| for (const auto& pair : pairs) { |
| NSMutableDictionary *pairDictionary = [NSMutableDictionary dictionary]; |
| if (pair.first && pair.first->wrapper() && !pair.first->accessibilityIsIgnored()) |
| [pairDictionary setObject:pair.first->wrapper() forKey:subscriptKey]; |
| if (pair.second && pair.second->wrapper() && !pair.second->accessibilityIsIgnored()) |
| [pairDictionary setObject:pair.second->wrapper() forKey:superscriptKey]; |
| [array addObject:pairDictionary]; |
| } |
| return array; |
| } |
| |
| static void addChildToArray(AccessibilityObjectInterface& child, RetainPtr<NSMutableArray> array) |
| { |
| WebAccessibilityObjectWrapper *wrapper = child.wrapper(); |
| // We want to return the attachment view instead of the object representing the attachment, |
| // otherwise, we get palindrome errors in the AX hierarchy. |
| if (child.isAttachment() && [wrapper attachmentView]) |
| [array.get() addObject:[wrapper attachmentView]]; |
| else if (wrapper) |
| [array.get() addObject:wrapper]; |
| } |
| |
| #if ENABLE(ACCESSIBILITY_ISOLATED_TREE) |
| NSArray *convertToNSArray(const Vector<RefPtr<WebCore::AXIsolatedTreeNode>>& children) |
| { |
| NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:children.size()]; |
| for (auto& child : children) |
| addChildToArray(*child, result); |
| return [result autorelease]; |
| } |
| #endif |
| |
| NSArray *convertToNSArray(const WebCore::AccessibilityObject::AccessibilityChildrenVector& children) |
| { |
| NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:children.size()]; |
| for (auto& child : children) |
| addChildToArray(*child, result); |
| return [result autorelease]; |
| } |
| |
| @implementation WebAccessibilityObjectWrapperBase |
| |
| @synthesize identifier=_identifier; |
| #if ENABLE(ACCESSIBILITY_ISOLATED_TREE) |
| @synthesize isolatedTreeIdentifier=_isolatedTreeIdentifier; |
| #endif |
| |
| - (id)initWithAccessibilityObject:(AccessibilityObject*)axObject |
| { |
| if (!(self = [super init])) |
| return nil; |
| |
| m_object = axObject; |
| _identifier = m_object->axObjectID(); |
| |
| return self; |
| } |
| |
| #if ENABLE(ACCESSIBILITY_ISOLATED_TREE) |
| - (RefPtr<WebCore::AXIsolatedTreeNode>)isolatedTreeNode |
| { |
| RELEASE_ASSERT(!isMainThread()); |
| |
| if (!_identifier) |
| return nullptr; |
| |
| if (m_isolatedTreeNode) |
| return m_isolatedTreeNode; |
| |
| m_isolatedTreeNode = AXIsolatedTree::nodeInTreeForID(_isolatedTreeIdentifier, _identifier); |
| return m_isolatedTreeNode; |
| } |
| #endif |
| |
| - (void)detach |
| { |
| m_object = nullptr; |
| _identifier = 0; |
| |
| #if ENABLE(ACCESSIBILITY_ISOLATED_TREE) |
| m_isolatedTreeNode = nullptr; |
| _isolatedTreeIdentifier = 0; |
| #endif |
| } |
| |
| - (BOOL)updateObjectBackingStore |
| { |
| #if ENABLE(ACCESSIBILITY_ISOLATED_TREE) |
| if (_AXUIElementRequestServicedBySecondaryAXThread()) { |
| RELEASE_ASSERT(!isMainThread()); |
| if (auto treeNode = self.isolatedTreeNode) { |
| if (auto tree = treeNode->tree()) |
| tree->applyPendingChanges(); |
| } |
| return _identifier; |
| } |
| #endif |
| |
| // Calling updateBackingStore() can invalidate this element so self must be retained. |
| // If it does become invalidated, m_object will be nil. |
| CFRetain((__bridge CFTypeRef)self); |
| CFAutorelease((__bridge CFTypeRef)self); |
| |
| if (!m_object) |
| return NO; |
| |
| m_object->updateBackingStore(); |
| if (!m_object) |
| return NO; |
| |
| return YES; |
| } |
| |
| - (id)attachmentView |
| { |
| return nil; |
| } |
| |
| - (AccessibilityObject*)accessibilityObject |
| { |
| #if ENABLE(ACCESSIBILITY_ISOLATED_TREE) |
| ASSERT(!_AXUIElementRequestServicedBySecondaryAXThread()); |
| #endif |
| return m_object; |
| } |
| |
| // This should be the "visible" text that's actually on the screen if possible. |
| // If there's alternative text, that can override the title. |
| - (NSString *)baseAccessibilityTitle |
| { |
| return _axBackingObject->titleAttributeValue(); |
| } |
| |
| - (WebCore::AccessibilityObjectInterface*)axBackingObject |
| { |
| #if ENABLE(ACCESSIBILITY_ISOLATED_TREE) |
| if (_AXUIElementRequestServicedBySecondaryAXThread()) |
| return self.isolatedTreeNode.get(); |
| #endif |
| return m_object; |
| } |
| |
| - (NSString *)baseAccessibilityDescription |
| { |
| return _axBackingObject->descriptionAttributeValue(); |
| } |
| |
| - (NSArray<NSString *> *)baseAccessibilitySpeechHint |
| { |
| return [(NSString *)_axBackingObject->speechHintAttributeValue() componentsSeparatedByString:@" "]; |
| } |
| |
| - (NSString *)baseAccessibilityHelpText |
| { |
| return _axBackingObject->helpTextAttributeValue(); |
| } |
| |
| struct PathConversionInfo { |
| WebAccessibilityObjectWrapperBase *wrapper; |
| CGMutablePathRef path; |
| }; |
| |
| static void convertPathToScreenSpaceFunction(PathConversionInfo& conversion, const PathElement& element) |
| { |
| WebAccessibilityObjectWrapperBase *wrapper = conversion.wrapper; |
| CGMutablePathRef newPath = conversion.path; |
| FloatRect rect; |
| switch (element.type) { |
| case PathElementMoveToPoint: |
| { |
| rect = FloatRect(element.points[0], FloatSize()); |
| CGPoint newPoint = [wrapper convertRectToSpace:rect space:AccessibilityConversionSpace::Screen].origin; |
| CGPathMoveToPoint(newPath, nil, newPoint.x, newPoint.y); |
| break; |
| } |
| case PathElementAddLineToPoint: |
| { |
| rect = FloatRect(element.points[0], FloatSize()); |
| CGPoint newPoint = [wrapper convertRectToSpace:rect space:AccessibilityConversionSpace::Screen].origin; |
| CGPathAddLineToPoint(newPath, nil, newPoint.x, newPoint.y); |
| break; |
| } |
| case PathElementAddQuadCurveToPoint: |
| { |
| rect = FloatRect(element.points[0], FloatSize()); |
| CGPoint newPoint1 = [wrapper convertRectToSpace:rect space:AccessibilityConversionSpace::Screen].origin; |
| |
| rect = FloatRect(element.points[1], FloatSize()); |
| CGPoint newPoint2 = [wrapper convertRectToSpace:rect space:AccessibilityConversionSpace::Screen].origin; |
| CGPathAddQuadCurveToPoint(newPath, nil, newPoint1.x, newPoint1.y, newPoint2.x, newPoint2.y); |
| break; |
| } |
| case PathElementAddCurveToPoint: |
| { |
| rect = FloatRect(element.points[0], FloatSize()); |
| CGPoint newPoint1 = [wrapper convertRectToSpace:rect space:AccessibilityConversionSpace::Screen].origin; |
| |
| rect = FloatRect(element.points[1], FloatSize()); |
| CGPoint newPoint2 = [wrapper convertRectToSpace:rect space:AccessibilityConversionSpace::Screen].origin; |
| |
| rect = FloatRect(element.points[2], FloatSize()); |
| CGPoint newPoint3 = [wrapper convertRectToSpace:rect space:AccessibilityConversionSpace::Screen].origin; |
| CGPathAddCurveToPoint(newPath, nil, newPoint1.x, newPoint1.y, newPoint2.x, newPoint2.y, newPoint3.x, newPoint3.y); |
| break; |
| } |
| case PathElementCloseSubpath: |
| { |
| CGPathCloseSubpath(newPath); |
| break; |
| } |
| } |
| } |
| |
| - (CGPathRef)convertPathToScreenSpace:(Path &)path |
| { |
| PathConversionInfo conversion = { self, CGPathCreateMutable() }; |
| path.apply([&conversion](const PathElement& pathElement) { |
| convertPathToScreenSpaceFunction(conversion, pathElement); |
| }); |
| CFAutorelease(conversion.path); |
| return conversion.path; |
| } |
| |
| - (id)_accessibilityWebDocumentView |
| { |
| ASSERT_NOT_REACHED(); |
| // Overridden by sub-classes |
| return nil; |
| } |
| |
| - (CGRect)convertRectToSpace:(WebCore::FloatRect &)rect space:(AccessibilityConversionSpace)space |
| { |
| if (!m_object) |
| return CGRectZero; |
| |
| CGSize size = CGSizeMake(rect.size().width(), rect.size().height()); |
| CGPoint point = CGPointMake(rect.x(), rect.y()); |
| |
| CGRect cgRect = CGRectMake(point.x, point.y, size.width, size.height); |
| |
| // WebKit1 code path... platformWidget() exists. |
| FrameView* frameView = m_object->documentFrameView(); |
| #if PLATFORM(IOS_FAMILY) |
| WAKView* documentView = frameView ? frameView->documentView() : nullptr; |
| if (documentView) { |
| cgRect = [documentView convertRect:cgRect toView:nil]; |
| |
| // we need the web document view to give us our final screen coordinates |
| // because that can take account of the scroller |
| id webDocument = [self _accessibilityWebDocumentView]; |
| if (webDocument) |
| cgRect = [webDocument convertRect:cgRect toView:nil]; |
| return cgRect; |
| } |
| #else |
| if (frameView && frameView->platformWidget()) { |
| NSRect nsRect = NSRectFromCGRect(cgRect); |
| NSView* view = frameView->documentView(); |
| ALLOW_DEPRECATED_DECLARATIONS_BEGIN |
| nsRect = [[view window] convertRectToScreen:[view convertRect:nsRect toView:nil]]; |
| ALLOW_DEPRECATED_DECLARATIONS_END |
| return NSRectToCGRect(nsRect); |
| } |
| #endif |
| else |
| return static_cast<CGRect>(m_object->convertFrameToSpace(rect, space)); |
| } |
| |
| - (NSString *)ariaLandmarkRoleDescription |
| { |
| switch (_axBackingObject->roleValue()) { |
| case AccessibilityRole::LandmarkBanner: |
| return AXARIAContentGroupText(@"ARIALandmarkBanner"); |
| case AccessibilityRole::LandmarkComplementary: |
| return AXARIAContentGroupText(@"ARIALandmarkComplementary"); |
| case AccessibilityRole::LandmarkContentInfo: |
| return AXARIAContentGroupText(@"ARIALandmarkContentInfo"); |
| case AccessibilityRole::LandmarkMain: |
| return AXARIAContentGroupText(@"ARIALandmarkMain"); |
| case AccessibilityRole::LandmarkNavigation: |
| return AXARIAContentGroupText(@"ARIALandmarkNavigation"); |
| case AccessibilityRole::LandmarkDocRegion: |
| case AccessibilityRole::LandmarkRegion: |
| return AXARIAContentGroupText(@"ARIALandmarkRegion"); |
| case AccessibilityRole::LandmarkSearch: |
| return AXARIAContentGroupText(@"ARIALandmarkSearch"); |
| case AccessibilityRole::ApplicationAlert: |
| return AXARIAContentGroupText(@"ARIAApplicationAlert"); |
| case AccessibilityRole::ApplicationAlertDialog: |
| return AXARIAContentGroupText(@"ARIAApplicationAlertDialog"); |
| case AccessibilityRole::ApplicationDialog: |
| return AXARIAContentGroupText(@"ARIAApplicationDialog"); |
| case AccessibilityRole::ApplicationLog: |
| return AXARIAContentGroupText(@"ARIAApplicationLog"); |
| case AccessibilityRole::ApplicationMarquee: |
| return AXARIAContentGroupText(@"ARIAApplicationMarquee"); |
| case AccessibilityRole::ApplicationStatus: |
| return AXARIAContentGroupText(@"ARIAApplicationStatus"); |
| case AccessibilityRole::ApplicationTimer: |
| return AXARIAContentGroupText(@"ARIAApplicationTimer"); |
| case AccessibilityRole::Document: |
| return AXARIAContentGroupText(@"ARIADocument"); |
| case AccessibilityRole::DocumentArticle: |
| return AXARIAContentGroupText(@"ARIADocumentArticle"); |
| case AccessibilityRole::DocumentMath: |
| return AXARIAContentGroupText(@"ARIADocumentMath"); |
| case AccessibilityRole::DocumentNote: |
| return AXARIAContentGroupText(@"ARIADocumentNote"); |
| case AccessibilityRole::UserInterfaceTooltip: |
| return AXARIAContentGroupText(@"ARIAUserInterfaceTooltip"); |
| case AccessibilityRole::TabPanel: |
| return AXARIAContentGroupText(@"ARIATabPanel"); |
| case AccessibilityRole::WebApplication: |
| return AXARIAContentGroupText(@"ARIAWebApplication"); |
| default: |
| return nil; |
| } |
| } |
| |
| - (void)baseAccessibilitySetFocus:(BOOL)focus |
| { |
| // If focus is just set without making the view the first responder, then keyboard focus won't move to the right place. |
| if (focus && !m_object->document()->frame()->selection().isFocusedAndActive()) { |
| FrameView* frameView = m_object->documentFrameView(); |
| Page* page = m_object->page(); |
| if (page && frameView) { |
| ChromeClient& chromeClient = page->chrome().client(); |
| chromeClient.focus(); |
| |
| // Legacy WebKit1 case. |
| if (frameView->platformWidget()) |
| chromeClient.makeFirstResponder(frameView->platformWidget()); |
| else |
| chromeClient.assistiveTechnologyMakeFirstResponder(); |
| } |
| } |
| |
| m_object->setFocused(focus); |
| } |
| |
| - (NSString *)accessibilityPlatformMathSubscriptKey |
| { |
| ASSERT_NOT_REACHED(); |
| return nil; |
| } |
| |
| - (NSString *)accessibilityPlatformMathSuperscriptKey |
| { |
| ASSERT_NOT_REACHED(); |
| return nil; |
| } |
| |
| - (NSArray *)accessibilityMathPostscriptPairs |
| { |
| AccessibilityObject::AccessibilityMathMultiscriptPairs pairs; |
| m_object->mathPostscripts(pairs); |
| return convertMathPairsToNSArray(pairs, [self accessibilityPlatformMathSubscriptKey], [self accessibilityPlatformMathSuperscriptKey]); |
| } |
| |
| - (NSArray *)accessibilityMathPrescriptPairs |
| { |
| AccessibilityObject::AccessibilityMathMultiscriptPairs pairs; |
| m_object->mathPrescripts(pairs); |
| return convertMathPairsToNSArray(pairs, [self accessibilityPlatformMathSubscriptKey], [self accessibilityPlatformMathSuperscriptKey]); |
| } |
| |
| // This is set by DRT when it wants to listen for notifications. |
| static BOOL accessibilityShouldRepostNotifications; |
| + (void)accessibilitySetShouldRepostNotifications:(BOOL)repost |
| { |
| accessibilityShouldRepostNotifications = repost; |
| #if PLATFORM(MAC) |
| AXObjectCache::setShouldRepostNotificationsForTests(repost); |
| #endif |
| } |
| |
| - (void)accessibilityPostedNotification:(NSString *)notificationName |
| { |
| if (accessibilityShouldRepostNotifications) |
| [self accessibilityPostedNotification:notificationName userInfo:nil]; |
| } |
| |
| static bool isValueTypeSupported(id value) |
| { |
| return [value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]] || [value isKindOfClass:[WebAccessibilityObjectWrapperBase class]]; |
| } |
| |
| static NSArray *arrayRemovingNonSupportedTypes(NSArray *array) |
| { |
| ASSERT([array isKindOfClass:[NSArray class]]); |
| NSMutableArray *mutableArray = [array mutableCopy]; |
| for (NSUInteger i = 0; i < [mutableArray count];) { |
| id value = [mutableArray objectAtIndex:i]; |
| if ([value isKindOfClass:[NSDictionary class]]) |
| [mutableArray replaceObjectAtIndex:i withObject:dictionaryRemovingNonSupportedTypes(value)]; |
| else if ([value isKindOfClass:[NSArray class]]) |
| [mutableArray replaceObjectAtIndex:i withObject:arrayRemovingNonSupportedTypes(value)]; |
| else if (!isValueTypeSupported(value)) { |
| [mutableArray removeObjectAtIndex:i]; |
| continue; |
| } |
| i++; |
| } |
| return [mutableArray autorelease]; |
| } |
| |
| static NSDictionary *dictionaryRemovingNonSupportedTypes(NSDictionary *dictionary) |
| { |
| if (!dictionary) |
| return nil; |
| ASSERT([dictionary isKindOfClass:[NSDictionary class]]); |
| NSMutableDictionary *mutableDictionary = [dictionary mutableCopy]; |
| for (NSString *key in dictionary) { |
| id value = [dictionary objectForKey:key]; |
| if ([value isKindOfClass:[NSDictionary class]]) |
| [mutableDictionary setObject:dictionaryRemovingNonSupportedTypes(value) forKey:key]; |
| else if ([value isKindOfClass:[NSArray class]]) |
| [mutableDictionary setObject:arrayRemovingNonSupportedTypes(value) forKey:key]; |
| else if (!isValueTypeSupported(value)) |
| [mutableDictionary removeObjectForKey:key]; |
| } |
| return [mutableDictionary autorelease]; |
| } |
| |
| - (void)accessibilityPostedNotification:(NSString *)notificationName userInfo:(NSDictionary *)userInfo |
| { |
| if (accessibilityShouldRepostNotifications) { |
| ASSERT(notificationName); |
| userInfo = dictionaryRemovingNonSupportedTypes(userInfo); |
| NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:notificationName, @"notificationName", userInfo, @"userInfo", nil]; |
| [[NSNotificationCenter defaultCenter] postNotificationName:@"AXDRTNotification" object:self userInfo:info]; |
| } |
| } |
| |
| #pragma mark Search helpers |
| |
| typedef HashMap<String, AccessibilitySearchKey> AccessibilitySearchKeyMap; |
| |
| struct SearchKeyEntry { |
| String key; |
| AccessibilitySearchKey value; |
| }; |
| |
| static AccessibilitySearchKeyMap* createAccessibilitySearchKeyMap() |
| { |
| const SearchKeyEntry searchKeys[] = { |
| { NSAccessibilityAnyTypeSearchKey, AccessibilitySearchKey::AnyType }, |
| { NSAccessibilityArticleSearchKey, AccessibilitySearchKey::Article }, |
| { NSAccessibilityBlockquoteSameLevelSearchKey, AccessibilitySearchKey::BlockquoteSameLevel }, |
| { NSAccessibilityBlockquoteSearchKey, AccessibilitySearchKey::Blockquote }, |
| { NSAccessibilityBoldFontSearchKey, AccessibilitySearchKey::BoldFont }, |
| { NSAccessibilityButtonSearchKey, AccessibilitySearchKey::Button }, |
| { NSAccessibilityCheckBoxSearchKey, AccessibilitySearchKey::CheckBox }, |
| { NSAccessibilityControlSearchKey, AccessibilitySearchKey::Control }, |
| { NSAccessibilityDifferentTypeSearchKey, AccessibilitySearchKey::DifferentType }, |
| { NSAccessibilityFontChangeSearchKey, AccessibilitySearchKey::FontChange }, |
| { NSAccessibilityFontColorChangeSearchKey, AccessibilitySearchKey::FontColorChange }, |
| { NSAccessibilityFrameSearchKey, AccessibilitySearchKey::Frame }, |
| { NSAccessibilityGraphicSearchKey, AccessibilitySearchKey::Graphic }, |
| { NSAccessibilityHeadingLevel1SearchKey, AccessibilitySearchKey::HeadingLevel1 }, |
| { NSAccessibilityHeadingLevel2SearchKey, AccessibilitySearchKey::HeadingLevel2 }, |
| { NSAccessibilityHeadingLevel3SearchKey, AccessibilitySearchKey::HeadingLevel3 }, |
| { NSAccessibilityHeadingLevel4SearchKey, AccessibilitySearchKey::HeadingLevel4 }, |
| { NSAccessibilityHeadingLevel5SearchKey, AccessibilitySearchKey::HeadingLevel5 }, |
| { NSAccessibilityHeadingLevel6SearchKey, AccessibilitySearchKey::HeadingLevel6 }, |
| { NSAccessibilityHeadingSameLevelSearchKey, AccessibilitySearchKey::HeadingSameLevel }, |
| { NSAccessibilityHeadingSearchKey, AccessibilitySearchKey::Heading }, |
| { NSAccessibilityHighlightedSearchKey, AccessibilitySearchKey::Highlighted }, |
| { NSAccessibilityKeyboardFocusableSearchKey, AccessibilitySearchKey::KeyboardFocusable }, |
| { NSAccessibilityItalicFontSearchKey, AccessibilitySearchKey::ItalicFont }, |
| { NSAccessibilityLandmarkSearchKey, AccessibilitySearchKey::Landmark }, |
| { NSAccessibilityLinkSearchKey, AccessibilitySearchKey::Link }, |
| { NSAccessibilityListSearchKey, AccessibilitySearchKey::List }, |
| { NSAccessibilityLiveRegionSearchKey, AccessibilitySearchKey::LiveRegion }, |
| { NSAccessibilityMisspelledWordSearchKey, AccessibilitySearchKey::MisspelledWord }, |
| { NSAccessibilityOutlineSearchKey, AccessibilitySearchKey::Outline }, |
| { NSAccessibilityPlainTextSearchKey, AccessibilitySearchKey::PlainText }, |
| { NSAccessibilityRadioGroupSearchKey, AccessibilitySearchKey::RadioGroup }, |
| { NSAccessibilitySameTypeSearchKey, AccessibilitySearchKey::SameType }, |
| { NSAccessibilityStaticTextSearchKey, AccessibilitySearchKey::StaticText }, |
| { NSAccessibilityStyleChangeSearchKey, AccessibilitySearchKey::StyleChange }, |
| { NSAccessibilityTableSameLevelSearchKey, AccessibilitySearchKey::TableSameLevel }, |
| { NSAccessibilityTableSearchKey, AccessibilitySearchKey::Table }, |
| { NSAccessibilityTextFieldSearchKey, AccessibilitySearchKey::TextField }, |
| { NSAccessibilityUnderlineSearchKey, AccessibilitySearchKey::Underline }, |
| { NSAccessibilityUnvisitedLinkSearchKey, AccessibilitySearchKey::UnvisitedLink }, |
| { NSAccessibilityVisitedLinkSearchKey, AccessibilitySearchKey::VisitedLink } |
| }; |
| |
| AccessibilitySearchKeyMap* searchKeyMap = new AccessibilitySearchKeyMap; |
| for (size_t i = 0; i < WTF_ARRAY_LENGTH(searchKeys); i++) |
| searchKeyMap->set(searchKeys[i].key, searchKeys[i].value); |
| |
| return searchKeyMap; |
| } |
| |
| static AccessibilitySearchKey accessibilitySearchKeyForString(const String& value) |
| { |
| if (value.isEmpty()) |
| return AccessibilitySearchKey::AnyType; |
| |
| static const AccessibilitySearchKeyMap* searchKeyMap = createAccessibilitySearchKeyMap(); |
| AccessibilitySearchKey searchKey = searchKeyMap->get(value); |
| return static_cast<int>(searchKey) ? searchKey : AccessibilitySearchKey::AnyType; |
| } |
| |
| AccessibilitySearchCriteria accessibilitySearchCriteriaForSearchPredicateParameterizedAttribute(const NSDictionary *parameterizedAttribute) |
| { |
| NSString *directionParameter = [parameterizedAttribute objectForKey:@"AXDirection"]; |
| NSNumber *immediateDescendantsOnlyParameter = [parameterizedAttribute objectForKey:NSAccessibilityImmediateDescendantsOnly]; |
| NSNumber *resultsLimitParameter = [parameterizedAttribute objectForKey:@"AXResultsLimit"]; |
| NSString *searchTextParameter = [parameterizedAttribute objectForKey:@"AXSearchText"]; |
| WebAccessibilityObjectWrapperBase *startElementParameter = [parameterizedAttribute objectForKey:@"AXStartElement"]; |
| NSNumber *visibleOnlyParameter = [parameterizedAttribute objectForKey:@"AXVisibleOnly"]; |
| id searchKeyParameter = [parameterizedAttribute objectForKey:@"AXSearchKey"]; |
| |
| AccessibilitySearchDirection direction = AccessibilitySearchDirection::Next; |
| if ([directionParameter isKindOfClass:[NSString class]]) |
| direction = [directionParameter isEqualToString:@"AXDirectionNext"] ? AccessibilitySearchDirection::Next : AccessibilitySearchDirection::Previous; |
| |
| bool immediateDescendantsOnly = false; |
| if ([immediateDescendantsOnlyParameter isKindOfClass:[NSNumber class]]) |
| immediateDescendantsOnly = [immediateDescendantsOnlyParameter boolValue]; |
| |
| unsigned resultsLimit = 0; |
| if ([resultsLimitParameter isKindOfClass:[NSNumber class]]) |
| resultsLimit = [resultsLimitParameter unsignedIntValue]; |
| |
| String searchText; |
| if ([searchTextParameter isKindOfClass:[NSString class]]) |
| searchText = searchTextParameter; |
| |
| AccessibilityObject* startElement = nullptr; |
| if ([startElementParameter isKindOfClass:[WebAccessibilityObjectWrapperBase class]]) |
| startElement = [startElementParameter accessibilityObject]; |
| |
| bool visibleOnly = false; |
| if ([visibleOnlyParameter isKindOfClass:[NSNumber class]]) |
| visibleOnly = [visibleOnlyParameter boolValue]; |
| |
| AccessibilitySearchCriteria criteria = AccessibilitySearchCriteria(startElement, direction, searchText, resultsLimit, visibleOnly, immediateDescendantsOnly); |
| |
| if ([searchKeyParameter isKindOfClass:[NSString class]]) |
| criteria.searchKeys.append(accessibilitySearchKeyForString(searchKeyParameter)); |
| else if ([searchKeyParameter isKindOfClass:[NSArray class]]) { |
| size_t searchKeyCount = static_cast<size_t>([searchKeyParameter count]); |
| criteria.searchKeys.reserveInitialCapacity(searchKeyCount); |
| for (size_t i = 0; i < searchKeyCount; ++i) { |
| NSString *searchKey = [searchKeyParameter objectAtIndex:i]; |
| if ([searchKey isKindOfClass:[NSString class]]) |
| criteria.searchKeys.uncheckedAppend(accessibilitySearchKeyForString(searchKey)); |
| } |
| } |
| |
| return criteria; |
| } |
| |
| @end |
| |
| #endif // ENABLE(ACCESSIBILITY) |