1/* 2 * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#import "config.h" 30#import "WebAccessibilityObjectWrapperBase.h" 31 32#if HAVE(ACCESSIBILITY) 33 34#import "AXObjectCache.h" 35#import "AccessibilityARIAGridRow.h" 36#import "AccessibilityList.h" 37#import "AccessibilityListBox.h" 38#import "AccessibilityRenderObject.h" 39#import "AccessibilityScrollView.h" 40#import "AccessibilitySpinButton.h" 41#import "AccessibilityTable.h" 42#import "AccessibilityTableCell.h" 43#import "AccessibilityTableColumn.h" 44#import "AccessibilityTableRow.h" 45#import "Chrome.h" 46#import "ChromeClient.h" 47#import "ColorMac.h" 48#import "ContextMenuController.h" 49#import "Font.h" 50#import "Frame.h" 51#import "FrameLoaderClient.h" 52#import "FrameSelection.h" 53#import "HTMLAnchorElement.h" 54#import "HTMLAreaElement.h" 55#import "HTMLFrameOwnerElement.h" 56#import "HTMLImageElement.h" 57#import "HTMLInputElement.h" 58#import "HTMLNames.h" 59#import "HTMLTextAreaElement.h" 60#import "LocalizedStrings.h" 61#import "Page.h" 62#import "RenderTextControl.h" 63#import "RenderView.h" 64#import "RenderWidget.h" 65#import "ScrollView.h" 66#import "SimpleFontData.h" 67#import "TextCheckerClient.h" 68#import "TextCheckingHelper.h" 69#import "VisibleUnits.h" 70#import "WebCoreFrameView.h" 71#import "WebCoreObjCExtras.h" 72#import "WebCoreSystemInterface.h" 73#import "htmlediting.h" 74 75using namespace WebCore; 76using namespace HTMLNames; 77 78static NSArray *convertMathPairsToNSArray(const AccessibilityObject::AccessibilityMathMultiscriptPairs& pairs, NSString *subscriptKey, NSString *superscriptKey) 79{ 80 NSMutableArray *array = [NSMutableArray arrayWithCapacity:pairs.size()]; 81 for (const auto& pair : pairs) { 82 NSMutableDictionary *pairDictionary = [NSMutableDictionary dictionary]; 83 if (pair.first && pair.first->wrapper() && !pair.first->accessibilityIsIgnored()) 84 [pairDictionary setObject:pair.first->wrapper() forKey:subscriptKey]; 85 if (pair.second && pair.second->wrapper() && !pair.second->accessibilityIsIgnored()) 86 [pairDictionary setObject:pair.second->wrapper() forKey:superscriptKey]; 87 [array addObject:pairDictionary]; 88 } 89 return array; 90} 91 92@implementation WebAccessibilityObjectWrapperBase 93 94- (id)initWithAccessibilityObject:(AccessibilityObject*)axObject 95{ 96 if (!(self = [super init])) 97 return nil; 98 99 m_object = axObject; 100 return self; 101} 102 103- (void)detach 104{ 105 m_object = 0; 106} 107 108- (BOOL)updateObjectBackingStore 109{ 110 // Calling updateBackingStore() can invalidate this element so self must be retained. 111 // If it does become invalidated, m_object will be nil. 112 [[self retain] autorelease]; 113 114 if (!m_object) 115 return NO; 116 117 m_object->updateBackingStore(); 118 if (!m_object) 119 return NO; 120 121 return YES; 122} 123 124- (id)attachmentView 125{ 126 return nil; 127} 128 129- (AccessibilityObject*)accessibilityObject 130{ 131 return m_object; 132} 133 134// FIXME: Different kinds of elements are putting the title tag to use in different 135// AX fields. This should be rectified, but in the initial patch I want to achieve 136// parity with existing behavior. 137- (BOOL)titleTagShouldBeUsedInDescriptionField 138{ 139 return (m_object->isLink() && !m_object->isImageMapLink()) || m_object->isImage(); 140} 141 142// On iOS, we don't have to return the value in the title. We can return the actual title, given the API. 143- (BOOL)fileUploadButtonReturnsValueInTitle 144{ 145 return YES; 146} 147 148// This should be the "visible" text that's actually on the screen if possible. 149// If there's alternative text, that can override the title. 150- (NSString *)accessibilityTitle 151{ 152 // Static text objects should not have a title. Its content is communicated in its AXValue. 153 if (m_object->roleValue() == StaticTextRole) 154 return [NSString string]; 155 156 // A file upload button presents a challenge because it has button text and a value, but the 157 // API doesn't support this paradigm. 158 // The compromise is to return the button type in the role description and the value of the file path in the title 159 if (m_object->isFileUploadButton() && [self fileUploadButtonReturnsValueInTitle]) 160 return m_object->stringValue(); 161 162 Vector<AccessibilityText> textOrder; 163 m_object->accessibilityText(textOrder); 164 165 for (const auto& text : textOrder) { 166 // If we have alternative text, then we should not expose a title. 167 if (text.textSource == AlternativeText) 168 break; 169 170 // Once we encounter visible text, or the text from our children that should be used foremost. 171 if (text.textSource == VisibleText || text.textSource == ChildrenText) 172 return text.text; 173 174 // If there's an element that labels this object and it's not exposed, then we should use 175 // that text as our title. 176 if (text.textSource == LabelByElementText && !m_object->exposesTitleUIElement()) 177 return text.text; 178 } 179 180 return [NSString string]; 181} 182 183- (NSString *)accessibilityDescription 184{ 185 // Static text objects should not have a description. Its content is communicated in its AXValue. 186 // One exception is the media control labels that have a value and a description. Those are set programatically. 187 if (m_object->roleValue() == StaticTextRole && !m_object->isMediaControlLabel()) 188 return [NSString string]; 189 190 Vector<AccessibilityText> textOrder; 191 m_object->accessibilityText(textOrder); 192 193 bool visibleTextAvailable = false; 194 for (const auto& text : textOrder) { 195 if (text.textSource == AlternativeText) 196 return text.text; 197 198 switch (text.textSource) { 199 case VisibleText: 200 case ChildrenText: 201 case LabelByElementText: 202 visibleTextAvailable = true; 203 break; 204 default: 205 break; 206 } 207 208 if (text.textSource == TitleTagText && !visibleTextAvailable) 209 return text.text; 210 } 211 212 return [NSString string]; 213} 214 215- (NSString *)accessibilityHelpText 216{ 217 Vector<AccessibilityText> textOrder; 218 m_object->accessibilityText(textOrder); 219 220 bool descriptiveTextAvailable = false; 221 for (const auto& text : textOrder) { 222 if (text.textSource == HelpText || text.textSource == SummaryText) 223 return text.text; 224 225 // If an element does NOT have other descriptive text the title tag should be used as its descriptive text. 226 // But, if those ARE available, then the title tag should be used for help text instead. 227 switch (text.textSource) { 228 case AlternativeText: 229 case VisibleText: 230 case ChildrenText: 231 case LabelByElementText: 232 descriptiveTextAvailable = true; 233 break; 234 default: 235 break; 236 } 237 238 if (text.textSource == TitleTagText && descriptiveTextAvailable) 239 return text.text; 240 } 241 242 return [NSString string]; 243} 244 245struct PathConversionInfo { 246 WebAccessibilityObjectWrapperBase *wrapper; 247 CGMutablePathRef path; 248}; 249 250static void ConvertPathToScreenSpaceFunction(void* info, const PathElement* element) 251{ 252 PathConversionInfo* conversion = (PathConversionInfo*)info; 253 WebAccessibilityObjectWrapperBase *wrapper = conversion->wrapper; 254 CGMutablePathRef newPath = conversion->path; 255 switch (element->type) { 256 case PathElementMoveToPoint: 257 { 258 CGPoint newPoint = [wrapper convertPointToScreenSpace:element->points[0]]; 259 CGPathMoveToPoint(newPath, nil, newPoint.x, newPoint.y); 260 break; 261 } 262 case PathElementAddLineToPoint: 263 { 264 CGPoint newPoint = [wrapper convertPointToScreenSpace:element->points[0]]; 265 CGPathAddLineToPoint(newPath, nil, newPoint.x, newPoint.y); 266 break; 267 } 268 case PathElementAddQuadCurveToPoint: 269 { 270 CGPoint newPoint1 = [wrapper convertPointToScreenSpace:element->points[0]]; 271 CGPoint newPoint2 = [wrapper convertPointToScreenSpace:element->points[1]]; 272 CGPathAddQuadCurveToPoint(newPath, nil, newPoint1.x, newPoint1.y, newPoint2.x, newPoint2.y); 273 break; 274 } 275 case PathElementAddCurveToPoint: 276 { 277 CGPoint newPoint1 = [wrapper convertPointToScreenSpace:element->points[0]]; 278 CGPoint newPoint2 = [wrapper convertPointToScreenSpace:element->points[1]]; 279 CGPoint newPoint3 = [wrapper convertPointToScreenSpace:element->points[2]]; 280 CGPathAddCurveToPoint(newPath, nil, newPoint1.x, newPoint1.y, newPoint2.x, newPoint2.y, newPoint3.x, newPoint3.y); 281 break; 282 } 283 case PathElementCloseSubpath: 284 { 285 CGPathCloseSubpath(newPath); 286 break; 287 } 288 } 289} 290 291- (CGPathRef)convertPathToScreenSpace:(Path &)path 292{ 293 PathConversionInfo conversion = { self, CGPathCreateMutable() }; 294 path.apply(&conversion, ConvertPathToScreenSpaceFunction); 295 return (CGPathRef)[(id)conversion.path autorelease]; 296} 297 298- (CGPoint)convertPointToScreenSpace:(FloatPoint &)point 299{ 300 UNUSED_PARAM(point); 301 ASSERT_NOT_REACHED(); 302 return CGPointZero; 303} 304 305- (NSString *)ariaLandmarkRoleDescription 306{ 307 switch (m_object->roleValue()) { 308 case LandmarkApplicationRole: 309 return AXARIAContentGroupText(@"ARIALandmarkApplication"); 310 case LandmarkBannerRole: 311 return AXARIAContentGroupText(@"ARIALandmarkBanner"); 312 case LandmarkComplementaryRole: 313 return AXARIAContentGroupText(@"ARIALandmarkComplementary"); 314 case LandmarkContentInfoRole: 315 return AXARIAContentGroupText(@"ARIALandmarkContentInfo"); 316 case LandmarkMainRole: 317 return AXARIAContentGroupText(@"ARIALandmarkMain"); 318 case LandmarkNavigationRole: 319 return AXARIAContentGroupText(@"ARIALandmarkNavigation"); 320 case LandmarkSearchRole: 321 return AXARIAContentGroupText(@"ARIALandmarkSearch"); 322 case ApplicationAlertRole: 323 return AXARIAContentGroupText(@"ARIAApplicationAlert"); 324 case ApplicationAlertDialogRole: 325 return AXARIAContentGroupText(@"ARIAApplicationAlertDialog"); 326 case ApplicationDialogRole: 327 return AXARIAContentGroupText(@"ARIAApplicationDialog"); 328 case ApplicationLogRole: 329 return AXARIAContentGroupText(@"ARIAApplicationLog"); 330 case ApplicationMarqueeRole: 331 return AXARIAContentGroupText(@"ARIAApplicationMarquee"); 332 case ApplicationStatusRole: 333 return AXARIAContentGroupText(@"ARIAApplicationStatus"); 334 case ApplicationTimerRole: 335 return AXARIAContentGroupText(@"ARIAApplicationTimer"); 336 case DocumentRole: 337 return AXARIAContentGroupText(@"ARIADocument"); 338 case DocumentArticleRole: 339 return AXARIAContentGroupText(@"ARIADocumentArticle"); 340 case DocumentMathRole: 341 return AXARIAContentGroupText(@"ARIADocumentMath"); 342 case DocumentNoteRole: 343 return AXARIAContentGroupText(@"ARIADocumentNote"); 344 case DocumentRegionRole: 345 return AXARIAContentGroupText(@"ARIADocumentRegion"); 346 case UserInterfaceTooltipRole: 347 return AXARIAContentGroupText(@"ARIAUserInterfaceTooltip"); 348 case TabPanelRole: 349 return AXARIAContentGroupText(@"ARIATabPanel"); 350 default: 351 return nil; 352 } 353} 354 355- (NSString *)accessibilityPlatformMathSubscriptKey 356{ 357 ASSERT_NOT_REACHED(); 358 return nil; 359} 360 361- (NSString *)accessibilityPlatformMathSuperscriptKey 362{ 363 ASSERT_NOT_REACHED(); 364 return nil; 365} 366 367- (NSArray *)accessibilityMathPostscriptPairs 368{ 369 AccessibilityObject::AccessibilityMathMultiscriptPairs pairs; 370 m_object->mathPostscripts(pairs); 371 return convertMathPairsToNSArray(pairs, [self accessibilityPlatformMathSubscriptKey], [self accessibilityPlatformMathSuperscriptKey]); 372} 373 374- (NSArray *)accessibilityMathPrescriptPairs 375{ 376 AccessibilityObject::AccessibilityMathMultiscriptPairs pairs; 377 m_object->mathPrescripts(pairs); 378 return convertMathPairsToNSArray(pairs, [self accessibilityPlatformMathSubscriptKey], [self accessibilityPlatformMathSuperscriptKey]); 379} 380 381// This is set by DRT when it wants to listen for notifications. 382static BOOL accessibilityShouldRepostNotifications; 383+ (void)accessibilitySetShouldRepostNotifications:(BOOL)repost 384{ 385 accessibilityShouldRepostNotifications = repost; 386} 387 388- (void)accessibilityPostedNotification:(NSString *)notificationName 389{ 390 if (accessibilityShouldRepostNotifications) { 391 NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:notificationName, @"notificationName", nil]; 392 [[NSNotificationCenter defaultCenter] postNotificationName:@"AXDRTNotification" object:self userInfo:userInfo]; 393 } 394} 395 396@end 397 398#endif // HAVE(ACCESSIBILITY) 399