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 Computer, 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 "TextIterator.h" 70#import "VisibleUnits.h" 71#import "WebCoreFrameView.h" 72#import "WebCoreObjCExtras.h" 73#import "WebCoreSystemInterface.h" 74#import "htmlediting.h" 75 76using namespace WebCore; 77using namespace HTMLNames; 78using namespace std; 79 80static NSArray *convertMathPairsToNSArray(const AccessibilityObject::AccessibilityMathMultiscriptPairs& pairs, NSString *subscriptKey, NSString *superscriptKey) 81{ 82 unsigned length = pairs.size(); 83 NSMutableArray *array = [NSMutableArray arrayWithCapacity:length]; 84 for (unsigned i = 0; i < length; ++i) { 85 NSMutableDictionary *pairDictionary = [NSMutableDictionary dictionary]; 86 pair<AccessibilityObject*, AccessibilityObject*> pair = pairs[i]; 87 if (pair.first && pair.first->wrapper() && !pair.first->accessibilityIsIgnored()) 88 [pairDictionary setObject:pair.first->wrapper() forKey:subscriptKey]; 89 if (pair.second && pair.second->wrapper() && !pair.second->accessibilityIsIgnored()) 90 [pairDictionary setObject:pair.second->wrapper() forKey:superscriptKey]; 91 [array addObject:pairDictionary]; 92 } 93 return array; 94} 95 96@implementation WebAccessibilityObjectWrapperBase 97 98- (id)initWithAccessibilityObject:(AccessibilityObject*)axObject 99{ 100 if (!(self = [super init])) 101 return nil; 102 103 m_object = axObject; 104 return self; 105} 106 107- (void)detach 108{ 109 m_object = 0; 110} 111 112- (BOOL)updateObjectBackingStore 113{ 114 // Calling updateBackingStore() can invalidate this element so self must be retained. 115 // If it does become invalidated, m_object will be nil. 116 [[self retain] autorelease]; 117 118 if (!m_object) 119 return NO; 120 121 m_object->updateBackingStore(); 122 if (!m_object) 123 return NO; 124 125 return YES; 126} 127 128- (id)attachmentView 129{ 130 return nil; 131} 132 133- (AccessibilityObject*)accessibilityObject 134{ 135 return m_object; 136} 137 138// FIXME: Different kinds of elements are putting the title tag to use in different 139// AX fields. This should be rectified, but in the initial patch I want to achieve 140// parity with existing behavior. 141- (BOOL)titleTagShouldBeUsedInDescriptionField 142{ 143 return (m_object->isLink() && !m_object->isImageMapLink()) || m_object->isImage(); 144} 145 146// On iOS, we don't have to return the value in the title. We can return the actual title, given the API. 147- (BOOL)fileUploadButtonReturnsValueInTitle 148{ 149 return YES; 150} 151 152// This should be the "visible" text that's actually on the screen if possible. 153// If there's alternative text, that can override the title. 154- (NSString *)accessibilityTitle 155{ 156 // Static text objects should not have a title. Its content is communicated in its AXValue. 157 if (m_object->roleValue() == StaticTextRole) 158 return [NSString string]; 159 160 // A file upload button presents a challenge because it has button text and a value, but the 161 // API doesn't support this paradigm. 162 // The compromise is to return the button type in the role description and the value of the file path in the title 163 if (m_object->isFileUploadButton() && [self fileUploadButtonReturnsValueInTitle]) 164 return m_object->stringValue(); 165 166 Vector<AccessibilityText> textOrder; 167 m_object->accessibilityText(textOrder); 168 169 unsigned length = textOrder.size(); 170 for (unsigned k = 0; k < length; k++) { 171 const AccessibilityText& text = textOrder[k]; 172 173 // If we have alternative text, then we should not expose a title. 174 if (text.textSource == AlternativeText) 175 break; 176 177 // Once we encounter visible text, or the text from our children that should be used foremost. 178 if (text.textSource == VisibleText || text.textSource == ChildrenText) 179 return text.text; 180 181 // If there's an element that labels this object and it's not exposed, then we should use 182 // that text as our title. 183 if (text.textSource == LabelByElementText && !m_object->exposesTitleUIElement()) 184 return text.text; 185 186 // FIXME: The title tag is used in certain cases for the title. This usage should 187 // probably be in the description field since it's not "visible". 188 if (text.textSource == TitleTagText && ![self titleTagShouldBeUsedInDescriptionField]) 189 return text.text; 190 } 191 192 return [NSString string]; 193} 194 195- (NSString *)accessibilityDescription 196{ 197 // Static text objects should not have a description. Its content is communicated in its AXValue. 198 // One exception is the media control labels that have a value and a description. Those are set programatically. 199 if (m_object->roleValue() == StaticTextRole && !m_object->isMediaControlLabel()) 200 return [NSString string]; 201 202 Vector<AccessibilityText> textOrder; 203 m_object->accessibilityText(textOrder); 204 205 unsigned length = textOrder.size(); 206 for (unsigned k = 0; k < length; k++) { 207 const AccessibilityText& text = textOrder[k]; 208 209 if (text.textSource == AlternativeText) 210 return text.text; 211 212 if (text.textSource == TitleTagText && [self titleTagShouldBeUsedInDescriptionField]) 213 return text.text; 214 } 215 216 return [NSString string]; 217} 218 219- (NSString *)accessibilityHelpText 220{ 221 Vector<AccessibilityText> textOrder; 222 m_object->accessibilityText(textOrder); 223 224 unsigned length = textOrder.size(); 225 bool descriptiveTextAvailable = false; 226 for (unsigned k = 0; k < length; k++) { 227 const AccessibilityText& text = textOrder[k]; 228 229 if (text.textSource == HelpText || text.textSource == SummaryText) 230 return text.text; 231 232 // If an element does NOT have other descriptive text the title tag should be used as its descriptive text. 233 // But, if those ARE available, then the title tag should be used for help text instead. 234 switch (text.textSource) { 235 case AlternativeText: 236 case VisibleText: 237 case ChildrenText: 238 case LabelByElementText: 239 descriptiveTextAvailable = true; 240 default: 241 break; 242 } 243 244 if (text.textSource == TitleTagText && descriptiveTextAvailable) 245 return text.text; 246 } 247 248 return [NSString string]; 249} 250 251struct PathConversionInfo { 252 WebAccessibilityObjectWrapperBase *wrapper; 253 CGMutablePathRef path; 254}; 255 256static void ConvertPathToScreenSpaceFunction(void* info, const PathElement* element) 257{ 258 PathConversionInfo* conversion = (PathConversionInfo*)info; 259 WebAccessibilityObjectWrapperBase *wrapper = conversion->wrapper; 260 CGMutablePathRef newPath = conversion->path; 261 switch (element->type) { 262 case PathElementMoveToPoint: 263 { 264 CGPoint newPoint = [wrapper convertPointToScreenSpace:element->points[0]]; 265 CGPathMoveToPoint(newPath, nil, newPoint.x, newPoint.y); 266 break; 267 } 268 case PathElementAddLineToPoint: 269 { 270 CGPoint newPoint = [wrapper convertPointToScreenSpace:element->points[0]]; 271 CGPathAddLineToPoint(newPath, nil, newPoint.x, newPoint.y); 272 break; 273 } 274 case PathElementAddQuadCurveToPoint: 275 { 276 CGPoint newPoint1 = [wrapper convertPointToScreenSpace:element->points[0]]; 277 CGPoint newPoint2 = [wrapper convertPointToScreenSpace:element->points[1]]; 278 CGPathAddQuadCurveToPoint(newPath, nil, newPoint1.x, newPoint1.y, newPoint2.x, newPoint2.y); 279 break; 280 } 281 case PathElementAddCurveToPoint: 282 { 283 CGPoint newPoint1 = [wrapper convertPointToScreenSpace:element->points[0]]; 284 CGPoint newPoint2 = [wrapper convertPointToScreenSpace:element->points[1]]; 285 CGPoint newPoint3 = [wrapper convertPointToScreenSpace:element->points[2]]; 286 CGPathAddCurveToPoint(newPath, nil, newPoint1.x, newPoint1.y, newPoint2.x, newPoint2.y, newPoint3.x, newPoint3.y); 287 break; 288 } 289 case PathElementCloseSubpath: 290 { 291 CGPathCloseSubpath(newPath); 292 break; 293 } 294 } 295} 296 297- (CGPathRef)convertPathToScreenSpace:(Path &)path 298{ 299 PathConversionInfo conversion = { self, CGPathCreateMutable() }; 300 path.apply(&conversion, ConvertPathToScreenSpaceFunction); 301 return (CGPathRef)[(id)conversion.path autorelease]; 302} 303 304- (CGPoint)convertPointToScreenSpace:(FloatPoint &)point 305{ 306 UNUSED_PARAM(point); 307 ASSERT_NOT_REACHED(); 308 return CGPointZero; 309} 310 311- (NSString *)ariaLandmarkRoleDescription 312{ 313 switch (m_object->roleValue()) { 314 case LandmarkApplicationRole: 315 return AXARIAContentGroupText(@"ARIALandmarkApplication"); 316 case LandmarkBannerRole: 317 return AXARIAContentGroupText(@"ARIALandmarkBanner"); 318 case LandmarkComplementaryRole: 319 return AXARIAContentGroupText(@"ARIALandmarkComplementary"); 320 case LandmarkContentInfoRole: 321 return AXARIAContentGroupText(@"ARIALandmarkContentInfo"); 322 case LandmarkMainRole: 323 return AXARIAContentGroupText(@"ARIALandmarkMain"); 324 case LandmarkNavigationRole: 325 return AXARIAContentGroupText(@"ARIALandmarkNavigation"); 326 case LandmarkSearchRole: 327 return AXARIAContentGroupText(@"ARIALandmarkSearch"); 328 case ApplicationAlertRole: 329 return AXARIAContentGroupText(@"ARIAApplicationAlert"); 330 case ApplicationAlertDialogRole: 331 return AXARIAContentGroupText(@"ARIAApplicationAlertDialog"); 332 case ApplicationDialogRole: 333 return AXARIAContentGroupText(@"ARIAApplicationDialog"); 334 case ApplicationLogRole: 335 return AXARIAContentGroupText(@"ARIAApplicationLog"); 336 case ApplicationMarqueeRole: 337 return AXARIAContentGroupText(@"ARIAApplicationMarquee"); 338 case ApplicationStatusRole: 339 return AXARIAContentGroupText(@"ARIAApplicationStatus"); 340 case ApplicationTimerRole: 341 return AXARIAContentGroupText(@"ARIAApplicationTimer"); 342 case DocumentRole: 343 return AXARIAContentGroupText(@"ARIADocument"); 344 case DocumentArticleRole: 345 return AXARIAContentGroupText(@"ARIADocumentArticle"); 346 case DocumentMathRole: 347 return AXARIAContentGroupText(@"ARIADocumentMath"); 348 case DocumentNoteRole: 349 return AXARIAContentGroupText(@"ARIADocumentNote"); 350 case DocumentRegionRole: 351 return AXARIAContentGroupText(@"ARIADocumentRegion"); 352 case UserInterfaceTooltipRole: 353 return AXARIAContentGroupText(@"ARIAUserInterfaceTooltip"); 354 case TabPanelRole: 355 return AXARIAContentGroupText(@"ARIATabPanel"); 356 default: 357 return nil; 358 } 359} 360 361- (NSString *)accessibilityPlatformMathSubscriptKey 362{ 363 ASSERT_NOT_REACHED(); 364 return nil; 365} 366 367- (NSString *)accessibilityPlatformMathSuperscriptKey 368{ 369 ASSERT_NOT_REACHED(); 370 return nil; 371} 372 373- (NSArray *)accessibilityMathPostscriptPairs 374{ 375 AccessibilityObject::AccessibilityMathMultiscriptPairs pairs; 376 m_object->mathPostscripts(pairs); 377 return convertMathPairsToNSArray(pairs, [self accessibilityPlatformMathSubscriptKey], [self accessibilityPlatformMathSuperscriptKey]); 378} 379 380- (NSArray *)accessibilityMathPrescriptPairs 381{ 382 AccessibilityObject::AccessibilityMathMultiscriptPairs pairs; 383 m_object->mathPrescripts(pairs); 384 return convertMathPairsToNSArray(pairs, [self accessibilityPlatformMathSubscriptKey], [self accessibilityPlatformMathSuperscriptKey]); 385} 386 387// This is set by DRT when it wants to listen for notifications. 388static BOOL accessibilityShouldRepostNotifications; 389+ (void)accessibilitySetShouldRepostNotifications:(BOOL)repost 390{ 391 accessibilityShouldRepostNotifications = repost; 392} 393 394- (void)accessibilityPostedNotification:(NSString *)notificationName 395{ 396 if (accessibilityShouldRepostNotifications) { 397 NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:notificationName, @"notificationName", nil]; 398 [[NSNotificationCenter defaultCenter] postNotificationName:@"AXDRTNotification" object:self userInfo:userInfo]; 399 } 400} 401 402@end 403 404#endif // HAVE(ACCESSIBILITY) 405