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