1/* 2 * Copyright (C) 2008 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 "AccessibilityObjectWrapper.h" 31 32#if HAVE(ACCESSIBILITY) 33 34#import "AXObjectCache.h" 35#import "AccessibilityListBox.h" 36#import "AccessibilityList.h" 37#import "AccessibilityRenderObject.h" 38#import "AccessibilityTable.h" 39#import "AccessibilityTableCell.h" 40#import "AccessibilityTableRow.h" 41#import "AccessibilityTableColumn.h" 42#import "ColorMac.h" 43#import "Frame.h" 44#import "HTMLAnchorElement.h" 45#import "HTMLAreaElement.h" 46#import "HTMLImageElement.h" 47#import "HTMLInputElement.h" 48#import "HTMLTextAreaElement.h" 49#import "LocalizedStrings.h" 50#import "RenderTextControl.h" 51#import "RenderView.h" 52#import "RenderWidget.h" 53#import "SelectionController.h" 54#import "SimpleFontData.h" 55#import "TextIterator.h" 56#import "WebCoreFrameView.h" 57#import "WebCoreObjCExtras.h" 58#import "WebCoreViewFactory.h" 59#import "htmlediting.h" 60#import "visible_units.h" 61#import <runtime/InitializeThreading.h> 62 63using namespace WebCore; 64using namespace HTMLNames; 65using namespace std; 66 67// Cell Tables 68#ifndef NSAccessibilitySelectedCellsAttribute 69#define NSAccessibilitySelectedCellsAttribute @"AXSelectedCells" 70#endif 71 72#ifndef NSAccessibilityVisibleCellsAttribute 73#define NSAccessibilityVisibleCellsAttribute @"AXVisibleCells" 74#endif 75 76#ifndef NSAccessibilityRowHeaderUIElementsAttribute 77#define NSAccessibilityRowHeaderUIElementsAttribute @"AXRowHeaderUIElements" 78#endif 79 80#ifndef NSAccessibilityRowIndexRangeAttribute 81#define NSAccessibilityRowIndexRangeAttribute @"AXRowIndexRange" 82#endif 83 84#ifndef NSAccessibilityColumnIndexRangeAttribute 85#define NSAccessibilityColumnIndexRangeAttribute @"AXColumnIndexRange" 86#endif 87 88#ifndef NSAccessibilityCellForColumnAndRowParameterizedAttribute 89#define NSAccessibilityCellForColumnAndRowParameterizedAttribute @"AXCellForColumnAndRow" 90#endif 91 92#ifndef NSAccessibilityCellRole 93#define NSAccessibilityCellRole @"AXCell" 94#endif 95 96// Lists 97#ifndef NSAccessibilityContentListSubrole 98#define NSAccessibilityContentListSubrole @"AXContentList" 99#endif 100 101#ifndef NSAccessibilityDefinitionListSubrole 102#define NSAccessibilityDefinitionListSubrole @"AXDefinitionList" 103#endif 104 105// Miscellaneous 106#ifndef NSAccessibilityBlockQuoteLevelAttribute 107#define NSAccessibilityBlockQuoteLevelAttribute @"AXBlockQuoteLevel" 108#endif 109 110#ifndef NSAccessibilityAccessKeyAttribute 111#define NSAccessibilityAccessKeyAttribute @"AXAccessKey" 112#endif 113 114#ifndef NSAccessibilityLanguageAttribute 115#define NSAccessibilityLanguageAttribute @"AXLanguage" 116#endif 117 118#ifndef NSAccessibilityRequiredAttribute 119#define NSAccessibilityRequiredAttribute @"AXRequired" 120#endif 121 122#ifdef BUILDING_ON_TIGER 123typedef unsigned NSUInteger; 124#define NSAccessibilityValueDescriptionAttribute @"AXValueDescription" 125#endif 126 127@interface NSObject (WebKitAccessibilityArrayCategory) 128 129- (NSUInteger)accessibilityIndexOfChild:(id)child; 130- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute; 131- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount; 132 133@end 134 135@implementation AccessibilityObjectWrapper 136 137+ (void)initialize 138{ 139 JSC::initializeThreading(); 140#ifndef BUILDING_ON_TIGER 141 WebCoreObjCFinalizeOnMainThread(self); 142#endif 143} 144 145- (id)initWithAccessibilityObject:(AccessibilityObject*)axObject 146{ 147 [super init]; 148 149 m_object = axObject; 150 return self; 151} 152 153- (void)unregisterUniqueIdForUIElement 154{ 155 [[WebCoreViewFactory sharedFactory] unregisterUniqueIdForUIElement:self]; 156} 157 158- (void)detach 159{ 160 // Send unregisterUniqueIdForUIElement unconditionally because if it is 161 // ever accidently not done (via other bugs in our AX implementation) you 162 // end up with a crash like <rdar://problem/4273149>. It is safe and not 163 // expensive to send even if the object is not registered. 164 [self unregisterUniqueIdForUIElement]; 165 m_object = 0; 166} 167 168- (AccessibilityObject*)accessibilityObject 169{ 170 return m_object; 171} 172 173- (NSView*)attachmentView 174{ 175 ASSERT(m_object->isAttachment()); 176 Widget* widget = m_object->widgetForAttachmentView(); 177 if (!widget) 178 return nil; 179 return NSAccessibilityUnignoredDescendant(widget->platformWidget()); 180} 181 182static WebCoreTextMarker* textMarkerForVisiblePosition(const VisiblePosition& visiblePos) 183{ 184 TextMarkerData textMarkerData; 185 AXObjectCache::textMarkerDataForVisiblePosition(textMarkerData, visiblePos); 186 if (!textMarkerData.axID) 187 return nil; 188 189 return [[WebCoreViewFactory sharedFactory] textMarkerWithBytes:&textMarkerData length:sizeof(textMarkerData)]; 190} 191 192static VisiblePosition visiblePositionForTextMarker(WebCoreTextMarker* textMarker) 193{ 194 TextMarkerData textMarkerData; 195 if (![[WebCoreViewFactory sharedFactory] getBytes:&textMarkerData fromTextMarker:textMarker length:sizeof(textMarkerData)]) 196 return VisiblePosition(); 197 198 return AXObjectCache::visiblePositionForTextMarkerData(textMarkerData); 199} 200 201static VisiblePosition visiblePositionForStartOfTextMarkerRange(WebCoreTextMarkerRange* textMarkerRange) 202{ 203 return visiblePositionForTextMarker([[WebCoreViewFactory sharedFactory] startOfTextMarkerRange:textMarkerRange]); 204} 205 206static VisiblePosition visiblePositionForEndOfTextMarkerRange(WebCoreTextMarkerRange* textMarkerRange) 207{ 208 return visiblePositionForTextMarker([[WebCoreViewFactory sharedFactory] endOfTextMarkerRange:textMarkerRange]); 209} 210 211static WebCoreTextMarkerRange* textMarkerRangeFromMarkers(WebCoreTextMarker* textMarker1, WebCoreTextMarker* textMarker2) 212{ 213 if (!textMarker1 || !textMarker2) 214 return nil; 215 216 return [[WebCoreViewFactory sharedFactory] textMarkerRangeWithStart:textMarker1 end:textMarker2]; 217} 218 219static void AXAttributeStringSetFont(NSMutableAttributedString* attrString, NSString* attribute, NSFont* font, NSRange range) 220{ 221 NSDictionary* dict; 222 223 if (font) { 224 dict = [NSDictionary dictionaryWithObjectsAndKeys: 225 [font fontName] , NSAccessibilityFontNameKey, 226 [font familyName] , NSAccessibilityFontFamilyKey, 227 [font displayName] , NSAccessibilityVisibleNameKey, 228 [NSNumber numberWithFloat:[font pointSize]] , NSAccessibilityFontSizeKey, 229 nil]; 230 231 [attrString addAttribute:attribute value:dict range:range]; 232 } else 233 [attrString removeAttribute:attribute range:range]; 234 235} 236 237static CGColorRef CreateCGColorIfDifferent(NSColor* nsColor, CGColorRef existingColor) 238{ 239 // get color information assuming NSDeviceRGBColorSpace 240 NSColor* rgbColor = [nsColor colorUsingColorSpaceName:NSDeviceRGBColorSpace]; 241 if (rgbColor == nil) 242 rgbColor = [NSColor blackColor]; 243 CGFloat components[4]; 244 [rgbColor getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]]; 245 246 // create a new CGColorRef to return 247 CGColorSpaceRef cgColorSpace = CGColorSpaceCreateDeviceRGB(); 248 CGColorRef cgColor = CGColorCreate(cgColorSpace, components); 249 CGColorSpaceRelease(cgColorSpace); 250 251 // check for match with existing color 252 if (existingColor && CGColorEqualToColor(cgColor, existingColor)) { 253 CGColorRelease(cgColor); 254 cgColor = 0; 255 } 256 257 return cgColor; 258} 259 260static void AXAttributeStringSetColor(NSMutableAttributedString* attrString, NSString* attribute, NSColor* color, NSRange range) 261{ 262 if (color) { 263 CGColorRef existingColor = (CGColorRef) [attrString attribute:attribute atIndex:range.location effectiveRange:nil]; 264 CGColorRef cgColor = CreateCGColorIfDifferent(color, existingColor); 265 if (cgColor) { 266 [attrString addAttribute:attribute value:(id)cgColor range:range]; 267 CGColorRelease(cgColor); 268 } 269 } else 270 [attrString removeAttribute:attribute range:range]; 271} 272 273static void AXAttributeStringSetNumber(NSMutableAttributedString* attrString, NSString* attribute, NSNumber* number, NSRange range) 274{ 275 if (number) 276 [attrString addAttribute:attribute value:number range:range]; 277 else 278 [attrString removeAttribute:attribute range:range]; 279} 280 281static void AXAttributeStringSetStyle(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range) 282{ 283 RenderStyle* style = renderer->style(); 284 285 // set basic font info 286 AXAttributeStringSetFont(attrString, NSAccessibilityFontTextAttribute, style->font().primaryFont()->getNSFont(), range); 287 288 // set basic colors 289 AXAttributeStringSetColor(attrString, NSAccessibilityForegroundColorTextAttribute, nsColor(style->color()), range); 290 AXAttributeStringSetColor(attrString, NSAccessibilityBackgroundColorTextAttribute, nsColor(style->backgroundColor()), range); 291 292 // set super/sub scripting 293 EVerticalAlign alignment = style->verticalAlign(); 294 if (alignment == SUB) 295 AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:(-1)], range); 296 else if (alignment == SUPER) 297 AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:1], range); 298 else 299 [attrString removeAttribute:NSAccessibilitySuperscriptTextAttribute range:range]; 300 301 // set shadow 302 if (style->textShadow()) 303 AXAttributeStringSetNumber(attrString, NSAccessibilityShadowTextAttribute, [NSNumber numberWithBool:YES], range); 304 else 305 [attrString removeAttribute:NSAccessibilityShadowTextAttribute range:range]; 306 307 // set underline and strikethrough 308 int decor = style->textDecorationsInEffect(); 309 if ((decor & UNDERLINE) == 0) { 310 [attrString removeAttribute:NSAccessibilityUnderlineTextAttribute range:range]; 311 [attrString removeAttribute:NSAccessibilityUnderlineColorTextAttribute range:range]; 312 } 313 314 if ((decor & LINE_THROUGH) == 0) { 315 [attrString removeAttribute:NSAccessibilityStrikethroughTextAttribute range:range]; 316 [attrString removeAttribute:NSAccessibilityStrikethroughColorTextAttribute range:range]; 317 } 318 319 if ((decor & (UNDERLINE | LINE_THROUGH)) != 0) { 320 // find colors using quirk mode approach (strict mode would use current 321 // color for all but the root line box, which would use getTextDecorationColors) 322 Color underline, overline, linethrough; 323 renderer->getTextDecorationColors(decor, underline, overline, linethrough); 324 325 if ((decor & UNDERLINE) != 0) { 326 AXAttributeStringSetNumber(attrString, NSAccessibilityUnderlineTextAttribute, [NSNumber numberWithBool:YES], range); 327 AXAttributeStringSetColor(attrString, NSAccessibilityUnderlineColorTextAttribute, nsColor(underline), range); 328 } 329 330 if ((decor & LINE_THROUGH) != 0) { 331 AXAttributeStringSetNumber(attrString, NSAccessibilityStrikethroughTextAttribute, [NSNumber numberWithBool:YES], range); 332 AXAttributeStringSetColor(attrString, NSAccessibilityStrikethroughColorTextAttribute, nsColor(linethrough), range); 333 } 334 } 335} 336 337static int blockquoteLevel(RenderObject* renderer) 338{ 339 if (!renderer) 340 return 0; 341 342 int result = 0; 343 for (Node* node = renderer->node(); node; node = node->parent()) { 344 if (node->hasTagName(blockquoteTag)) 345 result += 1; 346 } 347 348 return result; 349} 350 351static void AXAttributeStringSetBlockquoteLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range) 352{ 353 int quoteLevel = blockquoteLevel(renderer); 354 355 if (quoteLevel) 356 [attrString addAttribute:NSAccessibilityBlockQuoteLevelAttribute value:[NSNumber numberWithInt:quoteLevel] range:range]; 357 else 358 [attrString removeAttribute:NSAccessibilityBlockQuoteLevelAttribute range:range]; 359} 360 361static void AXAttributeStringSetSpelling(NSMutableAttributedString* attrString, Node* node, int offset, NSRange range) 362{ 363 Vector<DocumentMarker> markers = node->renderer()->document()->markersForNode(node); 364 Vector<DocumentMarker>::iterator markerIt = markers.begin(); 365 366 unsigned endOffset = (unsigned)offset + range.length; 367 for ( ; markerIt != markers.end(); markerIt++) { 368 DocumentMarker marker = *markerIt; 369 370 if (marker.type != DocumentMarker::Spelling) 371 continue; 372 373 if (marker.endOffset <= (unsigned)offset) 374 continue; 375 376 if (marker.startOffset > endOffset) 377 break; 378 379 // add misspelling attribute for the intersection of the marker and the range 380 int rStart = range.location + (marker.startOffset - offset); 381 int rLength = MIN(marker.endOffset, endOffset) - marker.startOffset; 382 NSRange spellRange = NSMakeRange(rStart, rLength); 383 AXAttributeStringSetNumber(attrString, NSAccessibilityMisspelledTextAttribute, [NSNumber numberWithBool:YES], spellRange); 384 385 if (marker.endOffset > endOffset + 1) 386 break; 387 } 388} 389 390static void AXAttributeStringSetHeadingLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range) 391{ 392 if (!renderer) 393 return; 394 395 AccessibilityObject* parentObject = renderer->document()->axObjectCache()->getOrCreate(renderer->parent()); 396 int parentHeadingLevel = parentObject->headingLevel(); 397 398 if (parentHeadingLevel) 399 [attrString addAttribute:@"AXHeadingLevel" value:[NSNumber numberWithInt:parentHeadingLevel] range:range]; 400 else 401 [attrString removeAttribute:@"AXHeadingLevel" range:range]; 402} 403 404static void AXAttributeStringSetElement(NSMutableAttributedString* attrString, NSString* attribute, AccessibilityObject* object, NSRange range) 405{ 406 if (object && object->isAccessibilityRenderObject()) { 407 // make a serialiazable AX object 408 409 RenderObject* renderer = static_cast<AccessibilityRenderObject*>(object)->renderer(); 410 if (!renderer) 411 return; 412 413 Document* doc = renderer->document(); 414 if (!doc) 415 return; 416 417 AXObjectCache* cache = doc->axObjectCache(); 418 if (!cache) 419 return; 420 421 AXUIElementRef axElement = [[WebCoreViewFactory sharedFactory] AXUIElementForElement:object->wrapper()]; 422 if (axElement) { 423 [attrString addAttribute:attribute value:(id)axElement range:range]; 424 CFRelease(axElement); 425 } 426 } else 427 [attrString removeAttribute:attribute range:range]; 428} 429 430static void AXAttributedStringAppendText(NSMutableAttributedString* attrString, Node* node, int offset, const UChar* chars, int length) 431{ 432 // skip invisible text 433 if (!node->renderer()) 434 return; 435 436 // easier to calculate the range before appending the string 437 NSRange attrStringRange = NSMakeRange([attrString length], length); 438 439 // append the string from this node 440 [[attrString mutableString] appendString:[NSString stringWithCharacters:chars length:length]]; 441 442 // add new attributes and remove irrelevant inherited ones 443 // NOTE: color attributes are handled specially because -[NSMutableAttributedString addAttribute: value: range:] does not merge 444 // identical colors. Workaround is to not replace an existing color attribute if it matches what we are adding. This also means 445 // we cannot just pre-remove all inherited attributes on the appended string, so we have to remove the irrelevant ones individually. 446 447 // remove inherited attachment from prior AXAttributedStringAppendReplaced 448 [attrString removeAttribute:NSAccessibilityAttachmentTextAttribute range:attrStringRange]; 449 450 // set new attributes 451 AXAttributeStringSetStyle(attrString, node->renderer(), attrStringRange); 452 AXAttributeStringSetHeadingLevel(attrString, node->renderer(), attrStringRange); 453 AXAttributeStringSetBlockquoteLevel(attrString, node->renderer(), attrStringRange); 454 AXAttributeStringSetElement(attrString, NSAccessibilityLinkTextAttribute, AccessibilityObject::anchorElementForNode(node), attrStringRange); 455 456 // do spelling last because it tends to break up the range 457 AXAttributeStringSetSpelling(attrString, node, offset, attrStringRange); 458} 459 460static NSString* nsStringForReplacedNode(Node* replacedNode) 461{ 462 // we should always be given a rendered node and a replaced node, but be safe 463 // replaced nodes are either attachments (widgets) or images 464 if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode()) { 465 ASSERT_NOT_REACHED(); 466 return nil; 467 } 468 469 // create an AX object, but skip it if it is not supposed to be seen 470 RefPtr<AccessibilityObject> obj = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode->renderer()); 471 if (obj->accessibilityIsIgnored()) 472 return nil; 473 474 // use the attachmentCharacter to represent the replaced node 475 const UniChar attachmentChar = NSAttachmentCharacter; 476 return [NSString stringWithCharacters:&attachmentChar length:1]; 477} 478 479- (NSAttributedString*)doAXAttributedStringForTextMarkerRange:(WebCoreTextMarkerRange*)textMarkerRange 480{ 481 // extract the start and end VisiblePosition 482 VisiblePosition startVisiblePosition = visiblePositionForStartOfTextMarkerRange(textMarkerRange); 483 if (startVisiblePosition.isNull()) 484 return nil; 485 486 VisiblePosition endVisiblePosition = visiblePositionForEndOfTextMarkerRange(textMarkerRange); 487 if (endVisiblePosition.isNull()) 488 return nil; 489 490 // iterate over the range to build the AX attributed string 491 NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init]; 492 TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get()); 493 while (!it.atEnd()) { 494 // locate the node and starting offset for this range 495 int exception = 0; 496 Node* node = it.range()->startContainer(exception); 497 ASSERT(node == it.range()->endContainer(exception)); 498 int offset = it.range()->startOffset(exception); 499 500 // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX) 501 if (it.length() != 0) { 502 AXAttributedStringAppendText(attrString, node, offset, it.characters(), it.length()); 503 } else { 504 Node* replacedNode = node->childNode(offset); 505 NSString *attachmentString = nsStringForReplacedNode(replacedNode); 506 if (attachmentString) { 507 NSRange attrStringRange = NSMakeRange([attrString length], [attachmentString length]); 508 509 // append the placeholder string 510 [[attrString mutableString] appendString:attachmentString]; 511 512 // remove all inherited attributes 513 [attrString setAttributes:nil range:attrStringRange]; 514 515 // add the attachment attribute 516 AccessibilityObject* obj = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode->renderer()); 517 AXAttributeStringSetElement(attrString, NSAccessibilityAttachmentTextAttribute, obj, attrStringRange); 518 } 519 } 520 it.advance(); 521 } 522 523 return [attrString autorelease]; 524} 525 526static WebCoreTextMarkerRange* textMarkerRangeFromVisiblePositions(VisiblePosition startPosition, VisiblePosition endPosition) 527{ 528 WebCoreTextMarker* startTextMarker = textMarkerForVisiblePosition(startPosition); 529 WebCoreTextMarker* endTextMarker = textMarkerForVisiblePosition(endPosition); 530 return textMarkerRangeFromMarkers(startTextMarker, endTextMarker); 531} 532 533- (NSArray*)accessibilityActionNames 534{ 535 if (!m_object) 536 return nil; 537 538 m_object->updateBackingStore(); 539 540 static NSArray* actionElementActions = [[NSArray alloc] initWithObjects: NSAccessibilityPressAction, NSAccessibilityShowMenuAction, nil]; 541 static NSArray* defaultElementActions = [[NSArray alloc] initWithObjects: NSAccessibilityShowMenuAction, nil]; 542 static NSArray* menuElementActions = [[NSArray alloc] initWithObjects: NSAccessibilityCancelAction, NSAccessibilityPressAction, nil]; 543 static NSArray* sliderActions = [[NSArray alloc] initWithObjects: NSAccessibilityIncrementAction, NSAccessibilityDecrementAction, nil]; 544 545 NSArray *actions; 546 if (m_object->actionElement()) 547 actions = actionElementActions; 548 else if (m_object->isMenuRelated()) 549 actions = menuElementActions; 550 else if (m_object->isSlider()) 551 actions = sliderActions; 552 else if (m_object->isAttachment()) 553 actions = [[self attachmentView] accessibilityActionNames]; 554 else 555 actions = defaultElementActions; 556 557 return actions; 558} 559 560- (NSArray*)accessibilityAttributeNames 561{ 562 if (!m_object) 563 return nil; 564 565 m_object->updateBackingStore(); 566 567 if (m_object->isAttachment()) 568 return [[self attachmentView] accessibilityAttributeNames]; 569 570 static NSArray* attributes = nil; 571 static NSArray* anchorAttrs = nil; 572 static NSArray* webAreaAttrs = nil; 573 static NSArray* textAttrs = nil; 574 static NSArray* listBoxAttrs = nil; 575 static NSArray* rangeAttrs = nil; 576 static NSArray* commonMenuAttrs = nil; 577 static NSArray* menuAttrs = nil; 578 static NSArray* menuBarAttrs = nil; 579 static NSArray* menuItemAttrs = nil; 580 static NSArray* menuButtonAttrs = nil; 581 static NSArray* controlAttrs = nil; 582 static NSArray* tableAttrs = nil; 583 static NSArray* tableRowAttrs = nil; 584 static NSArray* tableColAttrs = nil; 585 static NSArray* tableCellAttrs = nil; 586 static NSArray* groupAttrs = nil; 587 static NSArray* inputImageAttrs = nil; 588 static NSArray* passwordFieldAttrs = nil; 589 NSMutableArray* tempArray; 590 if (attributes == nil) { 591 attributes = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute, 592 NSAccessibilitySubroleAttribute, 593 NSAccessibilityRoleDescriptionAttribute, 594 NSAccessibilityChildrenAttribute, 595 NSAccessibilityHelpAttribute, 596 NSAccessibilityParentAttribute, 597 NSAccessibilityPositionAttribute, 598 NSAccessibilitySizeAttribute, 599 NSAccessibilityTitleAttribute, 600 NSAccessibilityDescriptionAttribute, 601 NSAccessibilityValueAttribute, 602 NSAccessibilityFocusedAttribute, 603 NSAccessibilityEnabledAttribute, 604 NSAccessibilityWindowAttribute, 605 @"AXSelectedTextMarkerRange", 606 @"AXStartTextMarker", 607 @"AXEndTextMarker", 608 @"AXVisited", 609 NSAccessibilityLinkedUIElementsAttribute, 610 NSAccessibilitySelectedAttribute, 611 NSAccessibilityBlockQuoteLevelAttribute, 612 NSAccessibilityTopLevelUIElementAttribute, 613 nil]; 614 } 615 if (commonMenuAttrs == nil) { 616 commonMenuAttrs = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute, 617 NSAccessibilityRoleDescriptionAttribute, 618 NSAccessibilityChildrenAttribute, 619 NSAccessibilityParentAttribute, 620 NSAccessibilityEnabledAttribute, 621 NSAccessibilityPositionAttribute, 622 NSAccessibilitySizeAttribute, 623 nil]; 624 } 625 if (anchorAttrs == nil) { 626 tempArray = [[NSMutableArray alloc] initWithArray:attributes]; 627 [tempArray addObject:NSAccessibilityURLAttribute]; 628 [tempArray addObject:NSAccessibilityAccessKeyAttribute]; 629 anchorAttrs = [[NSArray alloc] initWithArray:tempArray]; 630 [tempArray release]; 631 } 632 if (webAreaAttrs == nil) { 633 tempArray = [[NSMutableArray alloc] initWithArray:attributes]; 634 [tempArray addObject:@"AXLinkUIElements"]; 635 [tempArray addObject:@"AXLoaded"]; 636 [tempArray addObject:@"AXLayoutCount"]; 637 [tempArray addObject:NSAccessibilityURLAttribute]; 638 webAreaAttrs = [[NSArray alloc] initWithArray:tempArray]; 639 [tempArray release]; 640 } 641 if (textAttrs == nil) { 642 tempArray = [[NSMutableArray alloc] initWithArray:attributes]; 643 [tempArray addObject:NSAccessibilityNumberOfCharactersAttribute]; 644 [tempArray addObject:NSAccessibilitySelectedTextAttribute]; 645 [tempArray addObject:NSAccessibilitySelectedTextRangeAttribute]; 646 [tempArray addObject:NSAccessibilityVisibleCharacterRangeAttribute]; 647 [tempArray addObject:NSAccessibilityInsertionPointLineNumberAttribute]; 648 [tempArray addObject:NSAccessibilityTitleUIElementAttribute]; 649 [tempArray addObject:NSAccessibilityAccessKeyAttribute]; 650 [tempArray addObject:NSAccessibilityRequiredAttribute]; 651 textAttrs = [[NSArray alloc] initWithArray:tempArray]; 652 [tempArray release]; 653 } 654 if (listBoxAttrs == nil) { 655 tempArray = [[NSMutableArray alloc] initWithArray:attributes]; 656 [tempArray addObject:NSAccessibilitySelectedChildrenAttribute]; 657 [tempArray addObject:NSAccessibilityVisibleChildrenAttribute]; 658 [tempArray addObject:NSAccessibilityOrientationAttribute]; 659 [tempArray addObject:NSAccessibilityTitleUIElementAttribute]; 660 [tempArray addObject:NSAccessibilityAccessKeyAttribute]; 661 [tempArray addObject:NSAccessibilityRequiredAttribute]; 662 listBoxAttrs = [[NSArray alloc] initWithArray:tempArray]; 663 [tempArray release]; 664 } 665 if (rangeAttrs == nil) { 666 tempArray = [[NSMutableArray alloc] initWithArray:attributes]; 667 [tempArray addObject:NSAccessibilityTopLevelUIElementAttribute]; 668 [tempArray addObject:NSAccessibilityValueAttribute]; 669 [tempArray addObject:NSAccessibilityMinValueAttribute]; 670 [tempArray addObject:NSAccessibilityMaxValueAttribute]; 671 [tempArray addObject:NSAccessibilityOrientationAttribute]; 672 rangeAttrs = [[NSArray alloc] initWithArray:tempArray]; 673 [tempArray release]; 674 } 675 if (menuBarAttrs == nil) { 676 tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs]; 677 [tempArray addObject:NSAccessibilitySelectedChildrenAttribute]; 678 [tempArray addObject:NSAccessibilityVisibleChildrenAttribute]; 679 [tempArray addObject:NSAccessibilityTitleUIElementAttribute]; 680 menuBarAttrs = [[NSArray alloc] initWithArray:tempArray]; 681 [tempArray release]; 682 } 683 if (menuAttrs == nil) { 684 tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs]; 685 [tempArray addObject:NSAccessibilitySelectedChildrenAttribute]; 686 [tempArray addObject:NSAccessibilityVisibleChildrenAttribute]; 687 [tempArray addObject:NSAccessibilityTitleUIElementAttribute]; 688 menuAttrs = [[NSArray alloc] initWithArray:tempArray]; 689 [tempArray release]; 690 } 691 if (menuItemAttrs == nil) { 692 tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs]; 693 [tempArray addObject:NSAccessibilityTitleAttribute]; 694 [tempArray addObject:NSAccessibilityHelpAttribute]; 695 [tempArray addObject:NSAccessibilitySelectedAttribute]; 696 [tempArray addObject:(NSString*)kAXMenuItemCmdCharAttribute]; 697 [tempArray addObject:(NSString*)kAXMenuItemCmdVirtualKeyAttribute]; 698 [tempArray addObject:(NSString*)kAXMenuItemCmdGlyphAttribute]; 699 [tempArray addObject:(NSString*)kAXMenuItemCmdModifiersAttribute]; 700 [tempArray addObject:(NSString*)kAXMenuItemMarkCharAttribute]; 701 [tempArray addObject:(NSString*)kAXMenuItemPrimaryUIElementAttribute]; 702 [tempArray addObject:NSAccessibilityServesAsTitleForUIElementsAttribute]; 703 menuItemAttrs = [[NSArray alloc] initWithArray:tempArray]; 704 [tempArray release]; 705 } 706 if (menuButtonAttrs == nil) { 707 menuButtonAttrs = [[NSArray alloc] initWithObjects:NSAccessibilityRoleAttribute, 708 NSAccessibilityRoleDescriptionAttribute, 709 NSAccessibilityParentAttribute, 710 NSAccessibilityPositionAttribute, 711 NSAccessibilitySizeAttribute, 712 NSAccessibilityWindowAttribute, 713 NSAccessibilityTopLevelUIElementAttribute, 714 NSAccessibilityEnabledAttribute, 715 NSAccessibilityFocusedAttribute, 716 NSAccessibilityTitleAttribute, 717 NSAccessibilityChildrenAttribute, nil]; 718 } 719 if (controlAttrs == nil) { 720 tempArray = [[NSMutableArray alloc] initWithArray:attributes]; 721 [tempArray addObject:NSAccessibilityTitleUIElementAttribute]; 722 [tempArray addObject:NSAccessibilityAccessKeyAttribute]; 723 [tempArray addObject:NSAccessibilityRequiredAttribute]; 724 controlAttrs = [[NSArray alloc] initWithArray:tempArray]; 725 [tempArray release]; 726 } 727 if (tableAttrs == nil) { 728 tempArray = [[NSMutableArray alloc] initWithArray:attributes]; 729 [tempArray addObject:NSAccessibilityRowsAttribute]; 730 [tempArray addObject:NSAccessibilityVisibleRowsAttribute]; 731 [tempArray addObject:NSAccessibilityColumnsAttribute]; 732 [tempArray addObject:NSAccessibilityVisibleColumnsAttribute]; 733 [tempArray addObject:NSAccessibilityVisibleCellsAttribute]; 734 [tempArray addObject:(NSString *)kAXColumnHeaderUIElementsAttribute]; 735 [tempArray addObject:NSAccessibilityRowHeaderUIElementsAttribute]; 736 [tempArray addObject:NSAccessibilityHeaderAttribute]; 737 tableAttrs = [[NSArray alloc] initWithArray:tempArray]; 738 [tempArray release]; 739 } 740 if (tableRowAttrs == nil) { 741 tempArray = [[NSMutableArray alloc] initWithArray:attributes]; 742 [tempArray addObject:NSAccessibilityIndexAttribute]; 743 tableRowAttrs = [[NSArray alloc] initWithArray:tempArray]; 744 [tempArray release]; 745 } 746 if (tableColAttrs == nil) { 747 tempArray = [[NSMutableArray alloc] initWithArray:attributes]; 748 [tempArray addObject:NSAccessibilityIndexAttribute]; 749 [tempArray addObject:NSAccessibilityHeaderAttribute]; 750 [tempArray addObject:NSAccessibilityRowsAttribute]; 751 [tempArray addObject:NSAccessibilityVisibleRowsAttribute]; 752 tableColAttrs = [[NSArray alloc] initWithArray:tempArray]; 753 [tempArray release]; 754 } 755 if (tableCellAttrs == nil) { 756 tempArray = [[NSMutableArray alloc] initWithArray:attributes]; 757 [tempArray addObject:NSAccessibilityRowIndexRangeAttribute]; 758 [tempArray addObject:NSAccessibilityColumnIndexRangeAttribute]; 759 tableCellAttrs = [[NSArray alloc] initWithArray:tempArray]; 760 [tempArray release]; 761 } 762 if (groupAttrs == nil) { 763 tempArray = [[NSMutableArray alloc] initWithArray:attributes]; 764 [tempArray addObject:NSAccessibilityTitleUIElementAttribute]; 765 groupAttrs = [[NSArray alloc] initWithArray:tempArray]; 766 [tempArray release]; 767 } 768 if (inputImageAttrs == nil) { 769 tempArray = [[NSMutableArray alloc] initWithArray:controlAttrs]; 770 [tempArray addObject:NSAccessibilityURLAttribute]; 771 [tempArray addObject:NSAccessibilityAccessKeyAttribute]; 772 inputImageAttrs = [[NSArray alloc] initWithArray:tempArray]; 773 [tempArray release]; 774 } 775 if (passwordFieldAttrs == nil) { 776 tempArray = [[NSMutableArray alloc] initWithArray:attributes]; 777 [tempArray addObject:NSAccessibilityTitleUIElementAttribute]; 778 [tempArray addObject:NSAccessibilityRequiredAttribute]; 779 passwordFieldAttrs = [[NSArray alloc] initWithArray:tempArray]; 780 [tempArray release]; 781 } 782 783 if (m_object->isPasswordField()) 784 return passwordFieldAttrs; 785 786 if (m_object->isWebArea()) 787 return webAreaAttrs; 788 789 if (m_object->isTextControl()) 790 return textAttrs; 791 792 if (m_object->isAnchor() || m_object->isImage() || m_object->isLink()) 793 return anchorAttrs; 794 795 if (m_object->isDataTable()) 796 return tableAttrs; 797 if (m_object->isTableRow()) 798 return tableRowAttrs; 799 if (m_object->isTableColumn()) 800 return tableColAttrs; 801 if (m_object->isTableCell()) 802 return tableCellAttrs; 803 804 if (m_object->isListBox() || m_object->isList()) 805 return listBoxAttrs; 806 807 if (m_object->isProgressIndicator() || m_object->isSlider()) 808 return rangeAttrs; 809 810 if (m_object->isInputImage()) 811 return inputImageAttrs; 812 813 if (m_object->isControl()) 814 return controlAttrs; 815 816 if (m_object->isGroup()) 817 return groupAttrs; 818 819 if (m_object->isMenu()) 820 return menuAttrs; 821 if (m_object->isMenuBar()) 822 return menuBarAttrs; 823 if (m_object->isMenuButton()) 824 return menuButtonAttrs; 825 if (m_object->isMenuItem()) 826 return menuItemAttrs; 827 828 return attributes; 829} 830 831- (VisiblePositionRange)visiblePositionRangeForTextMarkerRange:(WebCoreTextMarkerRange*) textMarkerRange 832{ 833 return VisiblePositionRange(visiblePositionForStartOfTextMarkerRange(textMarkerRange), visiblePositionForEndOfTextMarkerRange(textMarkerRange)); 834} 835 836- (NSArray*)renderWidgetChildren 837{ 838 Widget* widget = m_object->widget(); 839 if (!widget) 840 return nil; 841 return [(widget->platformWidget()) accessibilityAttributeValue: NSAccessibilityChildrenAttribute]; 842} 843 844static void convertToVector(NSArray* array, AccessibilityObject::AccessibilityChildrenVector& vector) 845{ 846 unsigned length = [array count]; 847 vector.reserveInitialCapacity(length); 848 for (unsigned i = 0; i < length; ++i) { 849 AccessibilityObject* obj = [[array objectAtIndex:i] accessibilityObject]; 850 if (obj) 851 vector.append(obj); 852 } 853} 854 855static NSMutableArray* convertToNSArray(const AccessibilityObject::AccessibilityChildrenVector& vector) 856{ 857 unsigned length = vector.size(); 858 NSMutableArray* array = [NSMutableArray arrayWithCapacity: length]; 859 for (unsigned i = 0; i < length; ++i) { 860 AccessibilityObjectWrapper* wrapper = vector[i]->wrapper(); 861 ASSERT(wrapper); 862 if (wrapper) { 863 // we want to return the attachment view instead of the object representing the attachment. 864 // otherwise, we get palindrome errors in the AX hierarchy 865 if (vector[i]->isAttachment() && [wrapper attachmentView]) 866 [array addObject:[wrapper attachmentView]]; 867 else 868 [array addObject:wrapper]; 869 } 870 } 871 return array; 872} 873 874- (WebCoreTextMarkerRange*)textMarkerRangeForSelection 875{ 876 VisibleSelection selection = m_object->selection(); 877 if (selection.isNone()) 878 return nil; 879 return textMarkerRangeFromVisiblePositions(selection.visibleStart(), selection.visibleEnd()); 880} 881 882- (NSValue*)position 883{ 884 IntRect rect = m_object->elementRect(); 885 886 // The Cocoa accessibility API wants the lower-left corner. 887 NSPoint point = NSMakePoint(rect.x(), rect.bottom()); 888 FrameView* frameView = m_object->documentFrameView(); 889 if (frameView) { 890 NSView* view = frameView->documentView(); 891 point = [[view window] convertBaseToScreen: [view convertPoint: point toView:nil]]; 892 } 893 894 return [NSValue valueWithPoint: point]; 895} 896 897typedef HashMap<int, NSString*> AccessibilityRoleMap; 898 899static const AccessibilityRoleMap& createAccessibilityRoleMap() 900{ 901 struct RoleEntry { 902 AccessibilityRole value; 903 NSString* string; 904 }; 905 906 static const RoleEntry roles[] = { 907 { UnknownRole, NSAccessibilityUnknownRole }, 908 { ButtonRole, NSAccessibilityButtonRole }, 909 { RadioButtonRole, NSAccessibilityRadioButtonRole }, 910 { CheckBoxRole, NSAccessibilityCheckBoxRole }, 911 { SliderRole, NSAccessibilitySliderRole }, 912 { TabGroupRole, NSAccessibilityTabGroupRole }, 913 { TextFieldRole, NSAccessibilityTextFieldRole }, 914 { StaticTextRole, NSAccessibilityStaticTextRole }, 915 { TextAreaRole, NSAccessibilityTextAreaRole }, 916 { ScrollAreaRole, NSAccessibilityScrollAreaRole }, 917 { PopUpButtonRole, NSAccessibilityPopUpButtonRole }, 918 { MenuButtonRole, NSAccessibilityMenuButtonRole }, 919 { TableRole, NSAccessibilityTableRole }, 920 { ApplicationRole, NSAccessibilityApplicationRole }, 921 { GroupRole, NSAccessibilityGroupRole }, 922 { RadioGroupRole, NSAccessibilityRadioGroupRole }, 923 { ListRole, NSAccessibilityListRole }, 924 { ScrollBarRole, NSAccessibilityScrollBarRole }, 925 { ValueIndicatorRole, NSAccessibilityValueIndicatorRole }, 926 { ImageRole, NSAccessibilityImageRole }, 927 { MenuBarRole, NSAccessibilityMenuBarRole }, 928 { MenuRole, NSAccessibilityMenuRole }, 929 { MenuItemRole, NSAccessibilityMenuItemRole }, 930 { ColumnRole, NSAccessibilityColumnRole }, 931 { RowRole, NSAccessibilityRowRole }, 932 { ToolbarRole, NSAccessibilityToolbarRole }, 933 { BusyIndicatorRole, NSAccessibilityBusyIndicatorRole }, 934 { ProgressIndicatorRole, NSAccessibilityProgressIndicatorRole }, 935 { WindowRole, NSAccessibilityWindowRole }, 936 { DrawerRole, NSAccessibilityDrawerRole }, 937 { SystemWideRole, NSAccessibilitySystemWideRole }, 938 { OutlineRole, NSAccessibilityOutlineRole }, 939 { IncrementorRole, NSAccessibilityIncrementorRole }, 940 { BrowserRole, NSAccessibilityBrowserRole }, 941 { ComboBoxRole, NSAccessibilityComboBoxRole }, 942 { SplitGroupRole, NSAccessibilitySplitGroupRole }, 943 { SplitterRole, NSAccessibilitySplitterRole }, 944 { ColorWellRole, NSAccessibilityColorWellRole }, 945 { GrowAreaRole, NSAccessibilityGrowAreaRole }, 946 { SheetRole, NSAccessibilitySheetRole }, 947 { HelpTagRole, NSAccessibilityHelpTagRole }, 948 { MatteRole, NSAccessibilityMatteRole }, 949 { RulerRole, NSAccessibilityRulerRole }, 950 { RulerMarkerRole, NSAccessibilityRulerMarkerRole }, 951 { LinkRole, NSAccessibilityLinkRole }, 952#ifndef BUILDING_ON_TIGER 953 { DisclosureTriangleRole, NSAccessibilityDisclosureTriangleRole }, 954 { GridRole, NSAccessibilityGridRole }, 955#endif 956 { WebCoreLinkRole, NSAccessibilityLinkRole }, 957 { ImageMapLinkRole, NSAccessibilityLinkRole }, 958 { ImageMapRole, @"AXImageMap" }, 959 { ListMarkerRole, @"AXListMarker" }, 960 { WebAreaRole, @"AXWebArea" }, 961 { HeadingRole, @"AXHeading" }, 962 { ListBoxRole, NSAccessibilityListRole }, 963 { ListBoxOptionRole, NSAccessibilityStaticTextRole }, 964#if ACCESSIBILITY_TABLES 965 { CellRole, NSAccessibilityCellRole }, 966#else 967 { CellRole, NSAccessibilityGroupRole }, 968#endif 969 { TableHeaderContainerRole, NSAccessibilityGroupRole }, 970 { DefinitionListDefinitionRole, NSAccessibilityGroupRole }, 971 { DefinitionListTermRole, NSAccessibilityGroupRole }, 972 973 { SliderThumbRole, NSAccessibilityValueIndicatorRole }, 974 975 976 }; 977 AccessibilityRoleMap& roleMap = *new AccessibilityRoleMap; 978 979 const unsigned numRoles = sizeof(roles) / sizeof(roles[0]); 980 for (unsigned i = 0; i < numRoles; ++i) 981 roleMap.set(roles[i].value, roles[i].string); 982 return roleMap; 983} 984 985static NSString* roleValueToNSString(AccessibilityRole value) 986{ 987 ASSERT(value); 988 static const AccessibilityRoleMap& roleMap = createAccessibilityRoleMap(); 989 return roleMap.get(value); 990} 991 992- (NSString*)role 993{ 994 if (m_object->isAttachment()) 995 return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleAttribute]; 996 NSString* string = roleValueToNSString(m_object->roleValue()); 997 if (string != nil) 998 return string; 999 return NSAccessibilityUnknownRole; 1000} 1001 1002- (NSString*)subrole 1003{ 1004 if (m_object->isPasswordField()) 1005 return NSAccessibilitySecureTextFieldSubrole; 1006 1007 if (m_object->isAttachment()) { 1008 NSView* attachView = [self attachmentView]; 1009 if ([[attachView accessibilityAttributeNames] containsObject:NSAccessibilitySubroleAttribute]) { 1010 return [attachView accessibilityAttributeValue:NSAccessibilitySubroleAttribute]; 1011 } 1012 } 1013 1014 if (m_object->isList()) { 1015 AccessibilityList* listObject = static_cast<AccessibilityList*>(m_object); 1016 if (listObject->isUnorderedList() || listObject->isOrderedList()) 1017 return NSAccessibilityContentListSubrole; 1018 if (listObject->isDefinitionList()) 1019 return NSAccessibilityDefinitionListSubrole; 1020 } 1021 1022 return nil; 1023} 1024 1025- (NSString*)roleDescription 1026{ 1027 if (!m_object) 1028 return nil; 1029 1030 // attachments have the AXImage role, but a different subrole 1031 if (m_object->isAttachment()) 1032 return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleDescriptionAttribute]; 1033 1034 // FIXME 3447564: It would be better to call some AppKit API to get these strings 1035 // (which would be the best way to localize them) 1036 1037 NSString* axRole = [self role]; 1038 if ([axRole isEqualToString:NSAccessibilityButtonRole]) 1039 return NSAccessibilityRoleDescription(NSAccessibilityButtonRole, [self subrole]); 1040 1041 if ([axRole isEqualToString:NSAccessibilityPopUpButtonRole]) 1042 return NSAccessibilityRoleDescription(NSAccessibilityPopUpButtonRole, [self subrole]); 1043 1044 if ([axRole isEqualToString:NSAccessibilityStaticTextRole]) 1045 return NSAccessibilityRoleDescription(NSAccessibilityStaticTextRole, [self subrole]); 1046 1047 if ([axRole isEqualToString:NSAccessibilityImageRole]) 1048 return NSAccessibilityRoleDescription(NSAccessibilityImageRole, [self subrole]); 1049 1050 if ([axRole isEqualToString:NSAccessibilityGroupRole]) 1051 return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, [self subrole]); 1052 1053 if ([axRole isEqualToString:NSAccessibilityCheckBoxRole]) 1054 return NSAccessibilityRoleDescription(NSAccessibilityCheckBoxRole, [self subrole]); 1055 1056 if ([axRole isEqualToString:NSAccessibilityRadioButtonRole]) 1057 return NSAccessibilityRoleDescription(NSAccessibilityRadioButtonRole, [self subrole]); 1058 1059 if ([axRole isEqualToString:NSAccessibilityRadioGroupRole]) 1060 return NSAccessibilityRoleDescription(NSAccessibilityRadioGroupRole, [self subrole]); 1061 1062 if ([axRole isEqualToString:NSAccessibilityTextFieldRole]) 1063 return NSAccessibilityRoleDescription(NSAccessibilityTextFieldRole, [self subrole]); 1064 1065 if ([axRole isEqualToString:NSAccessibilityTextAreaRole]) 1066 return NSAccessibilityRoleDescription(NSAccessibilityTextAreaRole, [self subrole]); 1067 1068 if ([axRole isEqualToString:NSAccessibilityListRole]) 1069 return NSAccessibilityRoleDescription(NSAccessibilityListRole, [self subrole]); 1070 1071 if ([axRole isEqualToString:NSAccessibilityTableRole]) 1072 return NSAccessibilityRoleDescription(NSAccessibilityTableRole, [self subrole]); 1073 1074 if ([axRole isEqualToString:NSAccessibilityRowRole]) 1075 return NSAccessibilityRoleDescription(NSAccessibilityRowRole, [self subrole]); 1076 1077 if ([axRole isEqualToString:NSAccessibilityColumnRole]) 1078 return NSAccessibilityRoleDescription(NSAccessibilityColumnRole, [self subrole]); 1079 1080 if ([axRole isEqualToString:NSAccessibilityCellRole]) 1081 return NSAccessibilityRoleDescription(NSAccessibilityCellRole, [self subrole]); 1082 1083 if ([axRole isEqualToString:NSAccessibilitySliderRole]) 1084 return NSAccessibilityRoleDescription(NSAccessibilitySliderRole, [self subrole]); 1085 1086 if ([axRole isEqualToString:NSAccessibilityValueIndicatorRole]) 1087 return NSAccessibilityRoleDescription(NSAccessibilityValueIndicatorRole, [self subrole]); 1088 1089 if ([axRole isEqualToString:@"AXWebArea"]) 1090 return AXWebAreaText(); 1091 1092 if ([axRole isEqualToString:@"AXLink"]) 1093 return AXLinkText(); 1094 1095 if ([axRole isEqualToString:@"AXListMarker"]) 1096 return AXListMarkerText(); 1097 1098 if ([axRole isEqualToString:@"AXImageMap"]) 1099 return AXImageMapText(); 1100 1101 if ([axRole isEqualToString:@"AXHeading"]) 1102 return AXHeadingText(); 1103 1104 if ([axRole isEqualToString:(NSString*)kAXMenuBarItemRole] || 1105 [axRole isEqualToString:NSAccessibilityMenuRole]) 1106 return nil; 1107 1108 if ([axRole isEqualToString:NSAccessibilityMenuButtonRole]) 1109 return NSAccessibilityRoleDescription(NSAccessibilityMenuButtonRole, [self subrole]); 1110 1111 if ([axRole isEqualToString:NSAccessibilityToolbarRole]) 1112 return NSAccessibilityRoleDescription(NSAccessibilityToolbarRole, [self subrole]); 1113 1114 return NSAccessibilityRoleDescription(NSAccessibilityUnknownRole, nil); 1115} 1116 1117// FIXME: split up this function in a better way. 1118// suggestions: Use a hash table that maps attribute names to function calls, 1119// or maybe pointers to member functions 1120- (id)accessibilityAttributeValue:(NSString*)attributeName 1121{ 1122 if (!m_object) 1123 return nil; 1124 1125 m_object->updateBackingStore(); 1126 1127 if ([attributeName isEqualToString: NSAccessibilityRoleAttribute]) 1128 return [self role]; 1129 1130 if ([attributeName isEqualToString: NSAccessibilitySubroleAttribute]) 1131 return [self subrole]; 1132 1133 if ([attributeName isEqualToString: NSAccessibilityRoleDescriptionAttribute]) 1134 return [self roleDescription]; 1135 1136 if ([attributeName isEqualToString: NSAccessibilityParentAttribute]) { 1137 if (m_object->isAccessibilityRenderObject()) { 1138 FrameView* fv = static_cast<AccessibilityRenderObject*>(m_object)->frameViewIfRenderView(); 1139 if (fv) 1140 return fv->platformWidget(); 1141 } 1142 1143 return m_object->parentObjectUnignored()->wrapper(); 1144 } 1145 1146 if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) { 1147 if (m_object->children().isEmpty()) { 1148 NSArray* children = [self renderWidgetChildren]; 1149 if (children != nil) 1150 return children; 1151 } 1152 return convertToNSArray(m_object->children()); 1153 } 1154 1155 if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute]) { 1156 if (m_object->isListBox()) { 1157 AccessibilityObject::AccessibilityChildrenVector selectedChildrenCopy; 1158 m_object->selectedChildren(selectedChildrenCopy); 1159 return convertToNSArray(selectedChildrenCopy); 1160 } 1161 return nil; 1162 } 1163 1164 if ([attributeName isEqualToString: NSAccessibilityVisibleChildrenAttribute]) { 1165 if (m_object->isListBox()) { 1166 AccessibilityObject::AccessibilityChildrenVector visibleChildrenCopy; 1167 m_object->visibleChildren(visibleChildrenCopy); 1168 return convertToNSArray(visibleChildrenCopy); 1169 } 1170 else if (m_object->isList()) 1171 return [self accessibilityAttributeValue:NSAccessibilityChildrenAttribute]; 1172 1173 return nil; 1174 } 1175 1176 1177 if (m_object->isWebArea()) { 1178 if ([attributeName isEqualToString: @"AXLinkUIElements"]) { 1179 AccessibilityObject::AccessibilityChildrenVector links; 1180 static_cast<AccessibilityRenderObject*>(m_object)->getDocumentLinks(links); 1181 return convertToNSArray(links); 1182 } 1183 if ([attributeName isEqualToString: @"AXLoaded"]) 1184 return [NSNumber numberWithBool: m_object->isLoaded()]; 1185 if ([attributeName isEqualToString: @"AXLayoutCount"]) 1186 return [NSNumber numberWithInt: m_object->layoutCount()]; 1187 } 1188 1189 if (m_object->isTextControl()) { 1190 if ([attributeName isEqualToString: NSAccessibilityNumberOfCharactersAttribute]) { 1191 int length = m_object->textLength(); 1192 if (length < 0) 1193 return nil; 1194 return [NSNumber numberWithUnsignedInt:length]; 1195 } 1196 if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) { 1197 String selectedText = m_object->selectedText(); 1198 if (selectedText.isNull()) 1199 return nil; 1200 return (NSString*)selectedText; 1201 } 1202 if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) { 1203 PlainTextRange textRange = m_object->selectedTextRange(); 1204 if (textRange.isNull()) 1205 return nil; 1206 return [NSValue valueWithRange:NSMakeRange(textRange.start, textRange.length)]; 1207 } 1208 // TODO: Get actual visible range. <rdar://problem/4712101> 1209 if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) 1210 return m_object->isPasswordField() ? nil : [NSValue valueWithRange: NSMakeRange(0, m_object->textLength())]; 1211 if ([attributeName isEqualToString: NSAccessibilityInsertionPointLineNumberAttribute]) { 1212 // if selectionEnd > 0, then there is selected text and this question should not be answered 1213 if (m_object->isPasswordField() || m_object->selectionEnd() > 0) 1214 return nil; 1215 int lineNumber = m_object->lineForPosition(m_object->visiblePositionForIndex(m_object->selectionStart(), true)); 1216 if (lineNumber < 0) 1217 return nil; 1218 return [NSNumber numberWithInt:lineNumber]; 1219 } 1220 } 1221 1222 if ([attributeName isEqualToString: NSAccessibilityURLAttribute]) { 1223 KURL url = m_object->url(); 1224 if (url.isNull()) 1225 return nil; 1226 return (NSURL*)url; 1227 } 1228 1229 if ([attributeName isEqualToString: @"AXVisited"]) 1230 return [NSNumber numberWithBool: m_object->isVisited()]; 1231 1232 if ([attributeName isEqualToString: NSAccessibilityTitleAttribute]) { 1233 if (m_object->isAttachment()) { 1234 if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityTitleAttribute]) 1235 return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityTitleAttribute]; 1236 } 1237 return m_object->title(); 1238 } 1239 1240 if ([attributeName isEqualToString: NSAccessibilityDescriptionAttribute]) { 1241 if (m_object->isAttachment()) { 1242 if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityDescriptionAttribute]) 1243 return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityDescriptionAttribute]; 1244 } 1245 return m_object->accessibilityDescription(); 1246 } 1247 1248 if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) { 1249 if (m_object->isAttachment()) { 1250 if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityValueAttribute]) 1251 return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityValueAttribute]; 1252 } 1253 if (m_object->isProgressIndicator() || m_object->isSlider()) 1254 return [NSNumber numberWithFloat:m_object->valueForRange()]; 1255 if (m_object->hasIntValue()) 1256 return [NSNumber numberWithInt:m_object->intValue()]; 1257 1258 // radio groups return the selected radio button as the AXValue 1259 if (m_object->isRadioGroup()) { 1260 AccessibilityObject* radioButton = m_object->selectedRadioButton(); 1261 if (!radioButton) 1262 return nil; 1263 return radioButton->wrapper(); 1264 } 1265 1266 return m_object->stringValue(); 1267 } 1268 1269 if ([attributeName isEqualToString: NSAccessibilityMinValueAttribute]) 1270 return [NSNumber numberWithFloat:m_object->minValueForRange()]; 1271 1272 if ([attributeName isEqualToString: NSAccessibilityMaxValueAttribute]) 1273 return [NSNumber numberWithFloat:m_object->maxValueForRange()]; 1274 1275 if ([attributeName isEqualToString: NSAccessibilityHelpAttribute]) 1276 return m_object->helpText(); 1277 1278 if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) 1279 return [NSNumber numberWithBool: m_object->isFocused()]; 1280 1281 if ([attributeName isEqualToString: NSAccessibilityEnabledAttribute]) 1282 return [NSNumber numberWithBool: m_object->isEnabled()]; 1283 1284 if ([attributeName isEqualToString: NSAccessibilitySizeAttribute]) { 1285 IntSize s = m_object->size(); 1286 return [NSValue valueWithSize: NSMakeSize(s.width(), s.height())]; 1287 } 1288 1289 if ([attributeName isEqualToString: NSAccessibilityPositionAttribute]) 1290 return [self position]; 1291 1292 if ([attributeName isEqualToString: NSAccessibilityWindowAttribute] || 1293 [attributeName isEqualToString: NSAccessibilityTopLevelUIElementAttribute]) { 1294 FrameView* fv = m_object->documentFrameView(); 1295 if (fv) 1296 return [fv->platformWidget() window]; 1297 return nil; 1298 } 1299 1300 if ([attributeName isEqualToString:NSAccessibilityAccessKeyAttribute]) { 1301 AtomicString accessKey = m_object->accessKey(); 1302 if (accessKey.isNull()) 1303 return nil; 1304 return accessKey; 1305 } 1306 1307 if (m_object->isDataTable()) { 1308 // TODO: distinguish between visible and non-visible rows 1309 if ([attributeName isEqualToString:NSAccessibilityRowsAttribute] || 1310 [attributeName isEqualToString:NSAccessibilityVisibleRowsAttribute]) { 1311 return convertToNSArray(static_cast<AccessibilityTable*>(m_object)->rows()); 1312 } 1313 // TODO: distinguish between visible and non-visible columns 1314 if ([attributeName isEqualToString:NSAccessibilityColumnsAttribute] || 1315 [attributeName isEqualToString:NSAccessibilityVisibleColumnsAttribute]) { 1316 return convertToNSArray(static_cast<AccessibilityTable*>(m_object)->columns()); 1317 } 1318 1319 // HTML tables don't support these 1320 if ([attributeName isEqualToString:NSAccessibilitySelectedColumnsAttribute] || 1321 [attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute] || 1322 [attributeName isEqualToString:NSAccessibilitySelectedCellsAttribute]) 1323 return nil; 1324 1325 if ([attributeName isEqualToString:(NSString *)kAXColumnHeaderUIElementsAttribute]) { 1326 AccessibilityObject::AccessibilityChildrenVector columnHeaders; 1327 static_cast<AccessibilityTable*>(m_object)->columnHeaders(columnHeaders); 1328 return convertToNSArray(columnHeaders); 1329 } 1330 1331 if ([attributeName isEqualToString:NSAccessibilityHeaderAttribute]) { 1332 AccessibilityObject* headerContainer = static_cast<AccessibilityTable*>(m_object)->headerContainer(); 1333 if (headerContainer) 1334 return headerContainer->wrapper(); 1335 return nil; 1336 } 1337 1338 if ([attributeName isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) { 1339 AccessibilityObject::AccessibilityChildrenVector rowHeaders; 1340 static_cast<AccessibilityTable*>(m_object)->rowHeaders(rowHeaders); 1341 return convertToNSArray(rowHeaders); 1342 } 1343 1344 if ([attributeName isEqualToString:NSAccessibilityVisibleCellsAttribute]) { 1345 AccessibilityObject::AccessibilityChildrenVector cells; 1346 static_cast<AccessibilityTable*>(m_object)->cells(cells); 1347 return convertToNSArray(cells); 1348 } 1349 } 1350 1351 if (m_object->isTableRow()) { 1352 if ([attributeName isEqualToString:NSAccessibilityIndexAttribute]) 1353 return [NSNumber numberWithInt:static_cast<AccessibilityTableRow*>(m_object)->rowIndex()]; 1354 } 1355 1356 if (m_object->isTableColumn()) { 1357 if ([attributeName isEqualToString:NSAccessibilityIndexAttribute]) 1358 return [NSNumber numberWithInt:static_cast<AccessibilityTableColumn*>(m_object)->columnIndex()]; 1359 1360 // rows attribute for a column is the list of all the elements in that column at each row 1361 if ([attributeName isEqualToString:NSAccessibilityRowsAttribute] || 1362 [attributeName isEqualToString:NSAccessibilityVisibleRowsAttribute]) { 1363 return convertToNSArray(static_cast<AccessibilityTableColumn*>(m_object)->children()); 1364 } 1365 if ([attributeName isEqualToString:NSAccessibilityHeaderAttribute]) { 1366 AccessibilityObject* header = static_cast<AccessibilityTableColumn*>(m_object)->headerObject(); 1367 if (!header) 1368 return nil; 1369 return header->wrapper(); 1370 } 1371 } 1372 1373 if (m_object->isTableCell()) { 1374 if ([attributeName isEqualToString:NSAccessibilityRowIndexRangeAttribute]) { 1375 pair<int, int> rowRange; 1376 static_cast<AccessibilityTableCell*>(m_object)->rowIndexRange(rowRange); 1377 return [NSValue valueWithRange:NSMakeRange(rowRange.first, rowRange.second)]; 1378 } 1379 if ([attributeName isEqualToString:NSAccessibilityColumnIndexRangeAttribute]) { 1380 pair<int, int> columnRange; 1381 static_cast<AccessibilityTableCell*>(m_object)->columnIndexRange(columnRange); 1382 return [NSValue valueWithRange:NSMakeRange(columnRange.first, columnRange.second)]; 1383 } 1384 } 1385 1386 if ((m_object->isListBox() || m_object->isList()) && [attributeName isEqualToString:NSAccessibilityOrientationAttribute]) 1387 return NSAccessibilityVerticalOrientationValue; 1388 1389 if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) 1390 return [self textMarkerRangeForSelection]; 1391 1392 if (m_object->isAccessibilityRenderObject()) { 1393 RenderObject* renderer = static_cast<AccessibilityRenderObject*>(m_object)->renderer(); 1394 if (!renderer) 1395 return nil; 1396 1397 if ([attributeName isEqualToString: @"AXStartTextMarker"]) 1398 return textMarkerForVisiblePosition(startOfDocument(renderer->document())); 1399 if ([attributeName isEqualToString: @"AXEndTextMarker"]) 1400 return textMarkerForVisiblePosition(endOfDocument(renderer->document())); 1401 1402 if ([attributeName isEqualToString:NSAccessibilityBlockQuoteLevelAttribute]) 1403 return [NSNumber numberWithInt:blockquoteLevel(renderer)]; 1404 } else { 1405 if ([attributeName isEqualToString:NSAccessibilityBlockQuoteLevelAttribute]) { 1406 AccessibilityObject* parent = m_object->parentObjectUnignored(); 1407 if (!parent) 1408 return [NSNumber numberWithInt:0]; 1409 return [parent->wrapper() accessibilityAttributeValue:NSAccessibilityBlockQuoteLevelAttribute]; 1410 } 1411 } 1412 1413 if ([attributeName isEqualToString: NSAccessibilityLinkedUIElementsAttribute]) { 1414 AccessibilityObject::AccessibilityChildrenVector linkedUIElements; 1415 m_object->linkedUIElements(linkedUIElements); 1416 if (linkedUIElements.size() == 0) 1417 return nil; 1418 return convertToNSArray(linkedUIElements); 1419 } 1420 1421 if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute]) 1422 return [NSNumber numberWithBool:m_object->isSelected()]; 1423 1424 if ([attributeName isEqualToString: NSAccessibilityServesAsTitleForUIElementsAttribute] && m_object->isMenuButton()) { 1425 AccessibilityObject* uiElement = static_cast<AccessibilityRenderObject*>(m_object)->menuForMenuButton(); 1426 if (uiElement) 1427 return [NSArray arrayWithObject:uiElement->wrapper()]; 1428 } 1429 1430 if ([attributeName isEqualToString:NSAccessibilityTitleUIElementAttribute]) { 1431 AccessibilityObject* obj = m_object->titleUIElement(); 1432 if (obj) 1433 return obj->wrapper(); 1434 return nil; 1435 } 1436 1437 if ([attributeName isEqualToString:NSAccessibilityValueDescriptionAttribute]) 1438 return m_object->valueDescription(); 1439 1440 if ([attributeName isEqualToString:NSAccessibilityOrientationAttribute]) { 1441 AccessibilityOrientation elementOrientation = m_object->orientation(); 1442 if (elementOrientation == AccessibilityOrientationVertical) 1443 return NSAccessibilityVerticalOrientationValue; 1444 if (elementOrientation == AccessibilityOrientationHorizontal) 1445 return NSAccessibilityHorizontalOrientationValue; 1446 return nil; 1447 } 1448 1449 if ([attributeName isEqualToString:NSAccessibilityLanguageAttribute]) 1450 return m_object->language(); 1451 1452 if ([attributeName isEqualToString:NSAccessibilityRequiredAttribute]) 1453 return [NSNumber numberWithBool:m_object->isRequired()]; 1454 1455 // this is used only by DumpRenderTree for testing 1456 if ([attributeName isEqualToString:@"AXClickPoint"]) 1457 return [NSValue valueWithPoint:m_object->clickPoint()]; 1458 1459 return nil; 1460} 1461 1462- (id)accessibilityFocusedUIElement 1463{ 1464 if (!m_object) 1465 return nil; 1466 1467 m_object->updateBackingStore(); 1468 1469 RefPtr<AccessibilityObject> focusedObj = m_object->focusedUIElement(); 1470 1471 if (!focusedObj) 1472 return nil; 1473 1474 return focusedObj->wrapper(); 1475} 1476 1477- (id)accessibilityHitTest:(NSPoint)point 1478{ 1479 if (!m_object) 1480 return nil; 1481 1482 m_object->updateBackingStore(); 1483 1484 RefPtr<AccessibilityObject> axObject = m_object->doAccessibilityHitTest(IntPoint(point)); 1485 if (axObject) 1486 return NSAccessibilityUnignoredAncestor(axObject->wrapper()); 1487 return NSAccessibilityUnignoredAncestor(self); 1488} 1489 1490- (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName 1491{ 1492 if (!m_object) 1493 return nil; 1494 1495 m_object->updateBackingStore(); 1496 1497 if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) 1498 return YES; 1499 1500 if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) 1501 return m_object->canSetFocusAttribute(); 1502 1503 if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) 1504 return m_object->canSetValueAttribute(); 1505 1506 if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute]) 1507 return m_object->canSetSelectedAttribute(); 1508 1509 if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute]) 1510 return m_object->canSetSelectedChildrenAttribute(); 1511 1512 if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute] || 1513 [attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute] || 1514 [attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) 1515 return m_object->canSetTextRangeAttributes(); 1516 1517 return NO; 1518} 1519 1520// accessibilityShouldUseUniqueId is an AppKit method we override so that 1521// objects will be given a unique ID, and therefore allow AppKit to know when they 1522// become obsolete (e.g. when the user navigates to a new web page, making this one 1523// unrendered but not deallocated because it is in the back/forward cache). 1524// It is important to call NSAccessibilityUnregisterUniqueIdForUIElement in the 1525// appropriate place (e.g. dealloc) to remove these non-retained references from 1526// AppKit's id mapping tables. We do this in detach by calling unregisterUniqueIdForUIElement. 1527// 1528// Registering an object is also required for observing notifications. Only registered objects can be observed. 1529- (BOOL)accessibilityIsIgnored 1530{ 1531 if (!m_object) 1532 return nil; 1533 1534 m_object->updateBackingStore(); 1535 1536 if (m_object->isAttachment()) 1537 return [[self attachmentView] accessibilityIsIgnored]; 1538 return m_object->accessibilityIsIgnored(); 1539} 1540 1541- (NSArray* )accessibilityParameterizedAttributeNames 1542{ 1543 if (!m_object) 1544 return nil; 1545 1546 m_object->updateBackingStore(); 1547 1548 if (m_object->isAttachment()) 1549 return nil; 1550 1551 static NSArray* paramAttrs = nil; 1552 static NSArray* textParamAttrs = nil; 1553 static NSArray* tableParamAttrs = nil; 1554 if (paramAttrs == nil) { 1555 paramAttrs = [[NSArray alloc] initWithObjects: 1556 @"AXUIElementForTextMarker", 1557 @"AXTextMarkerRangeForUIElement", 1558 @"AXLineForTextMarker", 1559 @"AXTextMarkerRangeForLine", 1560 @"AXStringForTextMarkerRange", 1561 @"AXTextMarkerForPosition", 1562 @"AXBoundsForTextMarkerRange", 1563 @"AXAttributedStringForTextMarkerRange", 1564 @"AXTextMarkerRangeForUnorderedTextMarkers", 1565 @"AXNextTextMarkerForTextMarker", 1566 @"AXPreviousTextMarkerForTextMarker", 1567 @"AXLeftWordTextMarkerRangeForTextMarker", 1568 @"AXRightWordTextMarkerRangeForTextMarker", 1569 @"AXLeftLineTextMarkerRangeForTextMarker", 1570 @"AXRightLineTextMarkerRangeForTextMarker", 1571 @"AXSentenceTextMarkerRangeForTextMarker", 1572 @"AXParagraphTextMarkerRangeForTextMarker", 1573 @"AXNextWordEndTextMarkerForTextMarker", 1574 @"AXPreviousWordStartTextMarkerForTextMarker", 1575 @"AXNextLineEndTextMarkerForTextMarker", 1576 @"AXPreviousLineStartTextMarkerForTextMarker", 1577 @"AXNextSentenceEndTextMarkerForTextMarker", 1578 @"AXPreviousSentenceStartTextMarkerForTextMarker", 1579 @"AXNextParagraphEndTextMarkerForTextMarker", 1580 @"AXPreviousParagraphStartTextMarkerForTextMarker", 1581 @"AXStyleTextMarkerRangeForTextMarker", 1582 @"AXLengthForTextMarkerRange", 1583 NSAccessibilityBoundsForRangeParameterizedAttribute, 1584 nil]; 1585 } 1586 1587 if (textParamAttrs == nil) { 1588 NSMutableArray* tempArray = [[NSMutableArray alloc] initWithArray:paramAttrs]; 1589 [tempArray addObject:(NSString*)kAXLineForIndexParameterizedAttribute]; 1590 [tempArray addObject:(NSString*)kAXRangeForLineParameterizedAttribute]; 1591 [tempArray addObject:(NSString*)kAXStringForRangeParameterizedAttribute]; 1592 [tempArray addObject:(NSString*)kAXRangeForPositionParameterizedAttribute]; 1593 [tempArray addObject:(NSString*)kAXRangeForIndexParameterizedAttribute]; 1594 [tempArray addObject:(NSString*)kAXBoundsForRangeParameterizedAttribute]; 1595 [tempArray addObject:(NSString*)kAXRTFForRangeParameterizedAttribute]; 1596 [tempArray addObject:(NSString*)kAXAttributedStringForRangeParameterizedAttribute]; 1597 [tempArray addObject:(NSString*)kAXStyleRangeForIndexParameterizedAttribute]; 1598 textParamAttrs = [[NSArray alloc] initWithArray:tempArray]; 1599 [tempArray release]; 1600 } 1601 if (tableParamAttrs == nil) { 1602 NSMutableArray* tempArray = [[NSMutableArray alloc] initWithArray:paramAttrs]; 1603 [tempArray addObject:NSAccessibilityCellForColumnAndRowParameterizedAttribute]; 1604 tableParamAttrs = [[NSArray alloc] initWithArray:tempArray]; 1605 [tempArray release]; 1606 } 1607 1608 if (m_object->isPasswordField()) 1609 return [NSArray array]; 1610 1611 if (!m_object->isAccessibilityRenderObject()) 1612 return paramAttrs; 1613 1614 if (m_object->isTextControl()) 1615 return textParamAttrs; 1616 1617 if (m_object->isDataTable()) 1618 return tableParamAttrs; 1619 1620 if (m_object->isMenuRelated()) 1621 return nil; 1622 1623 return paramAttrs; 1624} 1625 1626- (void)accessibilityPerformPressAction 1627{ 1628 if (!m_object) 1629 return; 1630 1631 m_object->updateBackingStore(); 1632 1633 if (m_object->isAttachment()) 1634 [[self attachmentView] accessibilityPerformAction:NSAccessibilityPressAction]; 1635 else 1636 m_object->press(); 1637} 1638 1639- (void)accessibilityPerformIncrementAction 1640{ 1641 if (!m_object) 1642 return; 1643 1644 m_object->updateBackingStore(); 1645 1646 if (m_object->isAttachment()) 1647 [[self attachmentView] accessibilityPerformAction:NSAccessibilityIncrementAction]; 1648 else 1649 m_object->increment(); 1650} 1651 1652- (void)accessibilityPerformDecrementAction 1653{ 1654 if (!m_object) 1655 return; 1656 1657 m_object->updateBackingStore(); 1658 1659 if (m_object->isAttachment()) 1660 [[self attachmentView] accessibilityPerformAction:NSAccessibilityDecrementAction]; 1661 else 1662 m_object->decrement(); 1663} 1664 1665- (void)accessibilityPerformShowMenuAction 1666{ 1667 // This needs to be performed in an iteration of the run loop that did not start from an AX call. 1668 // If it's the same run loop iteration, the menu open notification won't be sent 1669 [self performSelector:@selector(accessibilityShowContextMenu) withObject:nil afterDelay:0.0]; 1670} 1671 1672- (void)accessibilityShowContextMenu 1673{ 1674 FrameView* frameView = m_object->documentFrameView(); 1675 if (!frameView) 1676 return; 1677 1678 // simulate a click in the middle of the object 1679 IntPoint clickPoint = m_object->clickPoint(); 1680 NSPoint nsClickPoint = NSMakePoint(clickPoint.x(), clickPoint.y()); 1681 1682 NSView* view = nil; 1683 if (m_object->isAttachment()) 1684 view = [self attachmentView]; 1685 else 1686 view = frameView->documentView(); 1687 1688 if (!view) 1689 return; 1690 1691 NSPoint nsScreenPoint = [view convertPoint:nsClickPoint toView:nil]; 1692 1693 // Show the contextual menu for this event. 1694 NSEvent* event = [NSEvent mouseEventWithType:NSRightMouseDown location:nsScreenPoint modifierFlags:0 timestamp:0 windowNumber:[[view window] windowNumber] context:0 eventNumber:0 clickCount:1 pressure:1]; 1695 NSMenu* menu = [view menuForEvent:event]; 1696 1697 if (menu) 1698 [NSMenu popUpContextMenu:menu withEvent:event forView:view]; 1699} 1700 1701- (void)accessibilityPerformAction:(NSString*)action 1702{ 1703 if (!m_object) 1704 return; 1705 1706 m_object->updateBackingStore(); 1707 1708 if ([action isEqualToString:NSAccessibilityPressAction]) 1709 [self accessibilityPerformPressAction]; 1710 1711 else if ([action isEqualToString:NSAccessibilityShowMenuAction]) 1712 [self accessibilityPerformShowMenuAction]; 1713 1714 else if ([action isEqualToString:NSAccessibilityIncrementAction]) 1715 [self accessibilityPerformIncrementAction]; 1716 1717 else if ([action isEqualToString:NSAccessibilityDecrementAction]) 1718 [self accessibilityPerformDecrementAction]; 1719} 1720 1721- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attributeName 1722{ 1723 if (!m_object) 1724 return; 1725 1726 m_object->updateBackingStore(); 1727 1728 WebCoreTextMarkerRange* textMarkerRange = nil; 1729 NSNumber* number = nil; 1730 NSString* string = nil; 1731 NSRange range = {0, 0}; 1732 NSArray* array = nil; 1733 1734 // decode the parameter 1735 if ([[WebCoreViewFactory sharedFactory] objectIsTextMarkerRange:value]) 1736 textMarkerRange = (WebCoreTextMarkerRange*) value; 1737 1738 else if ([value isKindOfClass:[NSNumber self]]) 1739 number = value; 1740 1741 else if ([value isKindOfClass:[NSString self]]) 1742 string = value; 1743 1744 else if ([value isKindOfClass:[NSValue self]]) 1745 range = [value rangeValue]; 1746 1747 else if ([value isKindOfClass:[NSArray self]]) 1748 array = value; 1749 1750 // handle the command 1751 if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) { 1752 ASSERT(textMarkerRange); 1753 m_object->setSelectedVisiblePositionRange([self visiblePositionRangeForTextMarkerRange:textMarkerRange]); 1754 } else if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) { 1755 ASSERT(number); 1756 m_object->setFocused([number intValue] != 0); 1757 } else if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) { 1758 if (!string) 1759 return; 1760 m_object->setValue(string); 1761 } else if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute]) { 1762 if (!number) 1763 return; 1764 m_object->setSelected([number boolValue]); 1765 } else if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute]) { 1766 if (!array || m_object->roleValue() != ListBoxRole) 1767 return; 1768 AccessibilityObject::AccessibilityChildrenVector selectedChildren; 1769 convertToVector(array, selectedChildren); 1770 static_cast<AccessibilityListBox*>(m_object)->setSelectedChildren(selectedChildren); 1771 } else if (m_object->isTextControl()) { 1772 if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) { 1773 m_object->setSelectedText(string); 1774 } else if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) { 1775 m_object->setSelectedTextRange(PlainTextRange(range.location, range.length)); 1776 } else if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) { 1777 m_object->makeRangeVisible(PlainTextRange(range.location, range.length)); 1778 } 1779 } 1780} 1781 1782static RenderObject* rendererForView(NSView* view) 1783{ 1784 if (![view conformsToProtocol:@protocol(WebCoreFrameView)]) 1785 return 0; 1786 1787 NSView<WebCoreFrameView>* frameView = (NSView<WebCoreFrameView>*)view; 1788 Frame* frame = [frameView _web_frame]; 1789 if (!frame) 1790 return 0; 1791 1792 Node* node = frame->document()->ownerElement(); 1793 if (!node) 1794 return 0; 1795 1796 return node->renderer(); 1797} 1798 1799- (id)_accessibilityParentForSubview:(NSView*)subview 1800{ 1801 RenderObject* renderer = rendererForView(subview); 1802 if (!renderer) 1803 return nil; 1804 1805 AccessibilityObject* obj = renderer->document()->axObjectCache()->getOrCreate(renderer); 1806 if (obj) 1807 return obj->parentObjectUnignored()->wrapper(); 1808 return nil; 1809} 1810 1811- (NSString*)accessibilityActionDescription:(NSString*)action 1812{ 1813 // we have no custom actions 1814 return NSAccessibilityActionDescription(action); 1815} 1816 1817// The CFAttributedStringType representation of the text associated with this accessibility 1818// object that is specified by the given range. 1819- (NSAttributedString*)doAXAttributedStringForRange:(NSRange)range 1820{ 1821 PlainTextRange textRange = PlainTextRange(range.location, range.length); 1822 VisiblePositionRange visiblePosRange = m_object->visiblePositionRangeForRange(textRange); 1823 return [self doAXAttributedStringForTextMarkerRange:textMarkerRangeFromVisiblePositions(visiblePosRange.start, visiblePosRange.end)]; 1824} 1825 1826// The RTF representation of the text associated with this accessibility object that is 1827// specified by the given range. 1828- (NSData*)doAXRTFForRange:(NSRange)range 1829{ 1830 NSAttributedString* attrString = [self doAXAttributedStringForRange:range]; 1831 return [attrString RTFFromRange: NSMakeRange(0, [attrString length]) documentAttributes: nil]; 1832} 1833 1834- (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter 1835{ 1836 WebCoreTextMarker* textMarker = nil; 1837 WebCoreTextMarkerRange* textMarkerRange = nil; 1838 NSNumber* number = nil; 1839 NSArray* array = nil; 1840 RefPtr<AccessibilityObject> uiElement = 0; 1841 NSPoint point = NSZeroPoint; 1842 bool pointSet = false; 1843 NSRange range = {0, 0}; 1844 bool rangeSet = false; 1845 1846 // basic parameter validation 1847 if (!m_object || !attribute || !parameter) 1848 return nil; 1849 1850 m_object->updateBackingStore(); 1851 1852 // common parameter type check/casting. Nil checks in handlers catch wrong type case. 1853 // NOTE: This assumes nil is not a valid parameter, because it is indistinguishable from 1854 // a parameter of the wrong type. 1855 if ([[WebCoreViewFactory sharedFactory] objectIsTextMarker:parameter]) 1856 textMarker = (WebCoreTextMarker*) parameter; 1857 1858 else if ([[WebCoreViewFactory sharedFactory] objectIsTextMarkerRange:parameter]) 1859 textMarkerRange = (WebCoreTextMarkerRange*) parameter; 1860 1861 else if ([parameter isKindOfClass:[AccessibilityObjectWrapper self]]) 1862 uiElement = [(AccessibilityObjectWrapper*)parameter accessibilityObject]; 1863 1864 else if ([parameter isKindOfClass:[NSNumber self]]) 1865 number = parameter; 1866 1867 else if ([parameter isKindOfClass:[NSArray self]]) 1868 array = parameter; 1869 1870 else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSPoint)) == 0) { 1871 pointSet = true; 1872 point = [(NSValue*)parameter pointValue]; 1873 1874 } else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSRange)) == 0) { 1875 rangeSet = true; 1876 range = [(NSValue*)parameter rangeValue]; 1877 1878 } else { 1879 // got a parameter of a type we never use 1880 // NOTE: No ASSERT_NOT_REACHED because this can happen accidentally 1881 // while using accesstool (e.g.), forcing you to start over 1882 return nil; 1883 } 1884 1885 // Convert values to WebCore types 1886 // FIXME: prepping all of these values as WebCore types is unnecessary in many 1887 // cases. Re-organization of this function or performing the conversion on a 1888 // need basis are possible improvements. 1889 VisiblePosition visiblePos; 1890 if (textMarker) 1891 visiblePos = visiblePositionForTextMarker(textMarker); 1892 int intNumber = [number intValue]; 1893 VisiblePositionRange visiblePosRange; 1894 if (textMarkerRange) 1895 visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange]; 1896 IntPoint webCorePoint = IntPoint(point); 1897 PlainTextRange plainTextRange = PlainTextRange(range.location, range.length); 1898 1899 // dispatch 1900 if ([attribute isEqualToString: @"AXUIElementForTextMarker"]) 1901 return m_object->accessibilityObjectForPosition(visiblePos)->wrapper(); 1902 1903 if ([attribute isEqualToString: @"AXTextMarkerRangeForUIElement"]) { 1904 VisiblePositionRange vpRange = uiElement.get()->visiblePositionRange(); 1905 return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); 1906 } 1907 1908 if ([attribute isEqualToString: @"AXLineForTextMarker"]) 1909 return [NSNumber numberWithUnsignedInt:m_object->lineForPosition(visiblePos)]; 1910 1911 if ([attribute isEqualToString: @"AXTextMarkerRangeForLine"]) { 1912 VisiblePositionRange vpRange = m_object->visiblePositionRangeForLine(intNumber); 1913 return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); 1914 } 1915 1916 if ([attribute isEqualToString: @"AXStringForTextMarkerRange"]) 1917 return m_object->stringForVisiblePositionRange(visiblePosRange); 1918 1919 if ([attribute isEqualToString: @"AXTextMarkerForPosition"]) 1920 return pointSet ? textMarkerForVisiblePosition(m_object->visiblePositionForPoint(webCorePoint)) : nil; 1921 1922 if ([attribute isEqualToString: @"AXBoundsForTextMarkerRange"]) { 1923 NSRect rect = m_object->boundsForVisiblePositionRange(visiblePosRange); 1924 return [NSValue valueWithRect:rect]; 1925 } 1926 1927 if ([attribute isEqualToString:NSAccessibilityBoundsForRangeParameterizedAttribute]) { 1928 VisiblePosition start = m_object->visiblePositionForIndex(range.location); 1929 VisiblePosition end = m_object->visiblePositionForIndex(range.location+range.length); 1930 if (start.isNull() || end.isNull()) 1931 return nil; 1932 NSRect rect = m_object->boundsForVisiblePositionRange(VisiblePositionRange(start, end)); 1933 return [NSValue valueWithRect:rect]; 1934 } 1935 1936 if ([attribute isEqualToString: @"AXAttributedStringForTextMarkerRange"]) 1937 return [self doAXAttributedStringForTextMarkerRange:textMarkerRange]; 1938 1939 if ([attribute isEqualToString: @"AXTextMarkerRangeForUnorderedTextMarkers"]) { 1940 if ([array count] < 2) 1941 return nil; 1942 1943 WebCoreTextMarker* textMarker1 = (WebCoreTextMarker*) [array objectAtIndex:0]; 1944 WebCoreTextMarker* textMarker2 = (WebCoreTextMarker*) [array objectAtIndex:1]; 1945 if (![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker1] 1946 || ![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker2]) 1947 return nil; 1948 1949 VisiblePosition visiblePos1 = visiblePositionForTextMarker(textMarker1); 1950 VisiblePosition visiblePos2 = visiblePositionForTextMarker(textMarker2); 1951 VisiblePositionRange vpRange = m_object->visiblePositionRangeForUnorderedPositions(visiblePos1, visiblePos2); 1952 return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); 1953 } 1954 1955 if ([attribute isEqualToString: @"AXNextTextMarkerForTextMarker"]) 1956 return textMarkerForVisiblePosition(m_object->nextVisiblePosition(visiblePos)); 1957 1958 if ([attribute isEqualToString: @"AXPreviousTextMarkerForTextMarker"]) 1959 return textMarkerForVisiblePosition(m_object->previousVisiblePosition(visiblePos)); 1960 1961 if ([attribute isEqualToString: @"AXLeftWordTextMarkerRangeForTextMarker"]) { 1962 VisiblePositionRange vpRange = m_object->positionOfLeftWord(visiblePos); 1963 return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); 1964 } 1965 1966 if ([attribute isEqualToString: @"AXRightWordTextMarkerRangeForTextMarker"]) { 1967 VisiblePositionRange vpRange = m_object->positionOfRightWord(visiblePos); 1968 return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); 1969 } 1970 1971 if ([attribute isEqualToString: @"AXLeftLineTextMarkerRangeForTextMarker"]) { 1972 VisiblePositionRange vpRange = m_object->leftLineVisiblePositionRange(visiblePos); 1973 return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); 1974 } 1975 1976 if ([attribute isEqualToString: @"AXRightLineTextMarkerRangeForTextMarker"]) { 1977 VisiblePositionRange vpRange = m_object->rightLineVisiblePositionRange(visiblePos); 1978 return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); 1979 } 1980 1981 if ([attribute isEqualToString: @"AXSentenceTextMarkerRangeForTextMarker"]) { 1982 VisiblePositionRange vpRange = m_object->sentenceForPosition(visiblePos); 1983 return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); 1984 } 1985 1986 if ([attribute isEqualToString: @"AXParagraphTextMarkerRangeForTextMarker"]) { 1987 VisiblePositionRange vpRange = m_object->paragraphForPosition(visiblePos); 1988 return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); 1989 } 1990 1991 if ([attribute isEqualToString: @"AXNextWordEndTextMarkerForTextMarker"]) 1992 return textMarkerForVisiblePosition(m_object->nextWordEnd(visiblePos)); 1993 1994 if ([attribute isEqualToString: @"AXPreviousWordStartTextMarkerForTextMarker"]) 1995 return textMarkerForVisiblePosition(m_object->previousWordStart(visiblePos)); 1996 1997 if ([attribute isEqualToString: @"AXNextLineEndTextMarkerForTextMarker"]) 1998 return textMarkerForVisiblePosition(m_object->nextLineEndPosition(visiblePos)); 1999 2000 if ([attribute isEqualToString: @"AXPreviousLineStartTextMarkerForTextMarker"]) 2001 return textMarkerForVisiblePosition(m_object->previousLineStartPosition(visiblePos)); 2002 2003 if ([attribute isEqualToString: @"AXNextSentenceEndTextMarkerForTextMarker"]) 2004 return textMarkerForVisiblePosition(m_object->nextSentenceEndPosition(visiblePos)); 2005 2006 if ([attribute isEqualToString: @"AXPreviousSentenceStartTextMarkerForTextMarker"]) 2007 return textMarkerForVisiblePosition(m_object->previousSentenceStartPosition(visiblePos)); 2008 2009 if ([attribute isEqualToString: @"AXNextParagraphEndTextMarkerForTextMarker"]) 2010 return textMarkerForVisiblePosition(m_object->nextParagraphEndPosition(visiblePos)); 2011 2012 if ([attribute isEqualToString: @"AXPreviousParagraphStartTextMarkerForTextMarker"]) 2013 return textMarkerForVisiblePosition(m_object->previousParagraphStartPosition(visiblePos)); 2014 2015 if ([attribute isEqualToString: @"AXStyleTextMarkerRangeForTextMarker"]) { 2016 VisiblePositionRange vpRange = m_object->styleRangeForPosition(visiblePos); 2017 return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); 2018 } 2019 2020 if ([attribute isEqualToString: @"AXLengthForTextMarkerRange"]) { 2021 int length = m_object->lengthForVisiblePositionRange(visiblePosRange); 2022 if (length < 0) 2023 return nil; 2024 return [NSNumber numberWithInt:length]; 2025 } 2026 2027 if (m_object->isDataTable()) { 2028 if ([attribute isEqualToString:NSAccessibilityCellForColumnAndRowParameterizedAttribute]) { 2029 if (array == nil || [array count] != 2) 2030 return nil; 2031 AccessibilityTableCell* cell = static_cast<AccessibilityTable*>(m_object)->cellForColumnAndRow([[array objectAtIndex:0] unsignedIntValue], [[array objectAtIndex:1] unsignedIntValue]); 2032 if (!cell) 2033 return nil; 2034 2035 return cell->wrapper(); 2036 } 2037 } 2038 2039 if (m_object->isTextControl()) { 2040 if ([attribute isEqualToString: (NSString *)kAXLineForIndexParameterizedAttribute]) { 2041 int lineNumber = m_object->doAXLineForIndex(intNumber); 2042 if (lineNumber < 0) 2043 return nil; 2044 return [NSNumber numberWithUnsignedInt:lineNumber]; 2045 } 2046 2047 if ([attribute isEqualToString: (NSString *)kAXRangeForLineParameterizedAttribute]) { 2048 PlainTextRange textRange = m_object->doAXRangeForLine(intNumber); 2049 return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)]; 2050 } 2051 2052 if ([attribute isEqualToString: (NSString*)kAXStringForRangeParameterizedAttribute]) 2053 return rangeSet ? (id)(m_object->doAXStringForRange(plainTextRange)) : nil; 2054 2055 if ([attribute isEqualToString: (NSString*)kAXRangeForPositionParameterizedAttribute]) { 2056 if (!pointSet) 2057 return nil; 2058 PlainTextRange textRange = m_object->doAXRangeForPosition(webCorePoint); 2059 return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)]; 2060 } 2061 2062 if ([attribute isEqualToString: (NSString*)kAXRangeForIndexParameterizedAttribute]) { 2063 PlainTextRange textRange = m_object->doAXRangeForIndex(intNumber); 2064 return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)]; 2065 } 2066 2067 if ([attribute isEqualToString: (NSString*)kAXBoundsForRangeParameterizedAttribute]) { 2068 if (!rangeSet) 2069 return nil; 2070 NSRect rect = m_object->doAXBoundsForRange(plainTextRange); 2071 return [NSValue valueWithRect:rect]; 2072 } 2073 2074 if ([attribute isEqualToString: (NSString*)kAXRTFForRangeParameterizedAttribute]) 2075 return rangeSet ? [self doAXRTFForRange:range] : nil; 2076 2077 if ([attribute isEqualToString: (NSString*)kAXAttributedStringForRangeParameterizedAttribute]) 2078 return rangeSet ? [self doAXAttributedStringForRange:range] : nil; 2079 2080 if ([attribute isEqualToString: (NSString*)kAXStyleRangeForIndexParameterizedAttribute]) { 2081 PlainTextRange textRange = m_object->doAXStyleRangeForIndex(intNumber); 2082 return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)]; 2083 } 2084 } 2085 2086 return nil; 2087} 2088 2089- (BOOL)accessibilityShouldUseUniqueId 2090{ 2091 return m_object->accessibilityShouldUseUniqueId(); 2092} 2093 2094// API that AppKit uses for faster access 2095- (NSUInteger)accessibilityIndexOfChild:(id)child 2096{ 2097 if (!m_object) 2098 return NSNotFound; 2099 2100 m_object->updateBackingStore(); 2101 2102 const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children(); 2103 2104 if (children.isEmpty()) 2105 return [[self renderWidgetChildren] indexOfObject:child]; 2106 2107 unsigned count = children.size(); 2108 for (unsigned k = 0; k < count; ++k) { 2109 AccessibilityObjectWrapper* wrapper = children[k]->wrapper(); 2110 if (wrapper == child || (children[k]->isAttachment() && [wrapper attachmentView] == child)) 2111 return k; 2112 } 2113 2114 return NSNotFound; 2115} 2116 2117- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute 2118{ 2119 if (!m_object) 2120 return 0; 2121 2122 m_object->updateBackingStore(); 2123 2124 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { 2125 const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children(); 2126 if (children.isEmpty()) 2127 return [[self renderWidgetChildren] count]; 2128 2129 return children.size(); 2130 } 2131 2132 return [super accessibilityArrayAttributeCount:attribute]; 2133} 2134 2135- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount 2136{ 2137 if (!m_object) 2138 return nil; 2139 2140 m_object->updateBackingStore(); 2141 2142 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { 2143 if (m_object->children().isEmpty()) { 2144 NSArray *children = [self renderWidgetChildren]; 2145 if (!children) 2146 return nil; 2147 2148 NSUInteger childCount = [children count]; 2149 if (index >= childCount) 2150 return nil; 2151 2152 NSUInteger arrayLength = min(childCount - index, maxCount); 2153 return [children subarrayWithRange:NSMakeRange(index, arrayLength)]; 2154 } 2155 2156 const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children(); 2157 unsigned childCount = children.size(); 2158 if (index >= childCount) 2159 return nil; 2160 2161 unsigned available = min(childCount - index, maxCount); 2162 2163 NSMutableArray *subarray = [NSMutableArray arrayWithCapacity:available]; 2164 for (unsigned added = 0; added < available; ++index, ++added) { 2165 AccessibilityObjectWrapper* wrapper = children[index]->wrapper(); 2166 if (wrapper) { 2167 // The attachment view should be returned, otherwise AX palindrome errors occur. 2168 if (children[index]->isAttachment() && [wrapper attachmentView]) 2169 [subarray addObject:[wrapper attachmentView]]; 2170 else 2171 [subarray addObject:wrapper]; 2172 } 2173 } 2174 2175 return subarray; 2176 } 2177 2178 return [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount]; 2179} 2180 2181@end 2182 2183#endif // HAVE(ACCESSIBILITY) 2184