2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #import "DumpRenderTree.h"
28 #import "AccessibilityUIElement.h"
30 #import <Foundation/Foundation.h>
31 #import <JavaScriptCore/JSStringRef.h>
32 #import <JavaScriptCore/JSStringRefCF.h>
33 #import <WebKit/WebFrame.h>
34 #import <WebKit/WebHTMLView.h>
35 #import <WebKit/WebTypesInternal.h>
36 #import <wtf/RetainPtr.h>
37 #import <wtf/Vector.h>
39 #ifdef BUILDING_ON_TIGER
40 #define NSAccessibilityValueDescriptionAttribute @"AXValueDescription"
43 @interface NSObject (WebKitAccessibilityArrayCategory)
44 - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount;
47 AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element)
53 AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other)
54 : m_element(other.m_element)
59 AccessibilityUIElement::~AccessibilityUIElement()
64 @interface NSString (JSStringRefAdditions)
65 + (NSString *)stringWithJSStringRef:(JSStringRef)jsStringRef;
66 - (JSStringRef)createJSStringRef;
69 @implementation NSString (JSStringRefAdditions)
71 + (NSString *)stringWithJSStringRef:(JSStringRef)jsStringRef
76 CFStringRef cfString = JSStringCopyCFString(kCFAllocatorDefault, jsStringRef);
77 return [(NSString *)cfString autorelease];
80 - (JSStringRef)createJSStringRef
82 return JSStringCreateWithCFString((CFStringRef)self);
87 static NSString* descriptionOfValue(id valueObject, id focusedAccessibilityObject)
92 if ([valueObject isKindOfClass:[NSArray class]])
93 return [NSString stringWithFormat:@"<array of size %d>", [(NSArray*)valueObject count]];
95 if ([valueObject isKindOfClass:[NSNumber class]])
96 return [(NSNumber*)valueObject stringValue];
98 if ([valueObject isKindOfClass:[NSValue class]]) {
99 NSString* type = [NSString stringWithCString:[valueObject objCType] encoding:NSASCIIStringEncoding];
100 NSValue* value = (NSValue*)valueObject;
101 if ([type rangeOfString:@"NSRect"].length > 0)
102 return [NSString stringWithFormat:@"NSRect: %@", NSStringFromRect([value rectValue])];
103 if ([type rangeOfString:@"NSPoint"].length > 0)
104 return [NSString stringWithFormat:@"NSPoint: %@", NSStringFromPoint([value pointValue])];
105 if ([type rangeOfString:@"NSSize"].length > 0)
106 return [NSString stringWithFormat:@"NSSize: %@", NSStringFromSize([value sizeValue])];
107 if ([type rangeOfString:@"NSRange"].length > 0)
108 return [NSString stringWithFormat:@"NSRange: %@", NSStringFromRange([value rangeValue])];
111 // Strip absolute URL paths
112 NSString* description = [valueObject description];
113 NSRange range = [description rangeOfString:@"LayoutTests"];
115 return [description substringFromIndex:range.location];
117 // Strip pointer locations
118 if ([description rangeOfString:@"0x"].length) {
119 NSString* role = [focusedAccessibilityObject accessibilityAttributeValue:NSAccessibilityRoleAttribute];
120 NSString* title = [focusedAccessibilityObject accessibilityAttributeValue:NSAccessibilityTitleAttribute];
122 return [NSString stringWithFormat:@"<%@: '%@'>", role, title];
123 return [NSString stringWithFormat:@"<%@>", role];
126 return [valueObject description];
129 static NSString* attributesOfElement(id accessibilityObject)
131 NSArray* supportedAttributes = [accessibilityObject accessibilityAttributeNames];
133 NSMutableString* attributesString = [NSMutableString string];
134 for (NSUInteger i = 0; i < [supportedAttributes count]; ++i) {
135 NSString* attribute = [supportedAttributes objectAtIndex:i];
137 // Right now, position provides useless and screen-specific information, so we do not
138 // want to include it for the sake of universally passing tests.
139 if ([attribute isEqualToString:@"AXPosition"])
142 // accessibilityAttributeValue: can throw an if an attribute is not returned.
143 // For DumpRenderTree's purpose, we should ignore those exceptions
145 id valueObject = [accessibilityObject accessibilityAttributeValue:attribute];
146 NSString* value = descriptionOfValue(valueObject, accessibilityObject);
147 [attributesString appendFormat:@"%@: %@\n", attribute, value];
148 } @catch (NSException* e) { }
151 return attributesString;
154 static JSStringRef concatenateAttributeAndValue(NSString* attribute, NSString* value)
156 Vector<UniChar> buffer([attribute length]);
157 [attribute getCharacters:buffer.data()];
161 Vector<UniChar> valueBuffer([value length]);
162 [value getCharacters:valueBuffer.data()];
163 buffer.append(valueBuffer);
165 return JSStringCreateWithCharacters(buffer.data(), buffer.size());
168 static void convertNSArrayToVector(NSArray* array, Vector<AccessibilityUIElement>& elementVector)
170 NSUInteger count = [array count];
171 for (NSUInteger i = 0; i < count; ++i)
172 elementVector.append(AccessibilityUIElement([array objectAtIndex:i]));
175 static JSStringRef descriptionOfElements(Vector<AccessibilityUIElement>& elementVector)
177 NSMutableString* allElementString = [NSMutableString string];
178 size_t size = elementVector.size();
179 for (size_t i = 0; i < size; ++i) {
180 NSString* attributes = attributesOfElement(elementVector[i].platformUIElement());
181 [allElementString appendFormat:@"%@\n------------\n", attributes];
184 return [allElementString createJSStringRef];
187 void AccessibilityUIElement::getLinkedUIElements(Vector<AccessibilityUIElement>& elementVector)
189 NSArray* linkedElements = [m_element accessibilityAttributeValue:NSAccessibilityLinkedUIElementsAttribute];
190 convertNSArrayToVector(linkedElements, elementVector);
193 void AccessibilityUIElement::getDocumentLinks(Vector<AccessibilityUIElement>& elementVector)
195 NSArray* linkElements = [m_element accessibilityAttributeValue:@"AXLinkUIElements"];
196 convertNSArrayToVector(linkElements, elementVector);
199 void AccessibilityUIElement::getChildren(Vector<AccessibilityUIElement>& elementVector)
201 NSArray* children = [m_element accessibilityAttributeValue:NSAccessibilityChildrenAttribute];
202 convertNSArrayToVector(children, elementVector);
205 void AccessibilityUIElement::getChildrenWithRange(Vector<AccessibilityUIElement>& elementVector, unsigned location, unsigned length)
207 NSArray* children = [m_element accessibilityArrayAttributeValues:NSAccessibilityChildrenAttribute index:location maxCount:length];
208 convertNSArrayToVector(children, elementVector);
211 int AccessibilityUIElement::childrenCount()
213 Vector<AccessibilityUIElement> children;
214 getChildren(children);
216 return children.size();
219 AccessibilityUIElement AccessibilityUIElement::elementAtPoint(int x, int y)
221 id element = [m_element accessibilityHitTest:NSMakePoint(x, y)];
225 return AccessibilityUIElement(element);
228 AccessibilityUIElement AccessibilityUIElement::getChildAtIndex(unsigned index)
230 Vector<AccessibilityUIElement> children;
231 getChildrenWithRange(children, index, 1);
233 if (children.size() == 1)
238 AccessibilityUIElement AccessibilityUIElement::disclosedRowAtIndex(unsigned index)
240 NSArray* rows = [m_element accessibilityAttributeValue:NSAccessibilityDisclosedRowsAttribute];
241 if (index < [rows count])
242 return [rows objectAtIndex:index];
247 AccessibilityUIElement AccessibilityUIElement::selectedRowAtIndex(unsigned index)
249 NSArray* rows = [m_element accessibilityAttributeValue:NSAccessibilitySelectedRowsAttribute];
250 if (index < [rows count])
251 return [rows objectAtIndex:index];
256 AccessibilityUIElement AccessibilityUIElement::titleUIElement()
258 id accessibilityObject = [m_element accessibilityAttributeValue:NSAccessibilityTitleUIElementAttribute];
259 if (accessibilityObject)
260 return AccessibilityUIElement(accessibilityObject);
265 AccessibilityUIElement AccessibilityUIElement::parentElement()
267 id accessibilityObject = [m_element accessibilityAttributeValue:NSAccessibilityParentAttribute];
268 if (accessibilityObject)
269 return AccessibilityUIElement(accessibilityObject);
274 AccessibilityUIElement AccessibilityUIElement::disclosedByRow()
276 id accessibilityObject = [m_element accessibilityAttributeValue:NSAccessibilityDisclosedByRowAttribute];
277 if (accessibilityObject)
278 return AccessibilityUIElement(accessibilityObject);
283 JSStringRef AccessibilityUIElement::attributesOfLinkedUIElements()
285 Vector<AccessibilityUIElement> linkedElements;
286 getLinkedUIElements(linkedElements);
287 return descriptionOfElements(linkedElements);
290 JSStringRef AccessibilityUIElement::attributesOfDocumentLinks()
292 Vector<AccessibilityUIElement> linkElements;
293 getDocumentLinks(linkElements);
294 return descriptionOfElements(linkElements);
297 JSStringRef AccessibilityUIElement::attributesOfChildren()
299 Vector<AccessibilityUIElement> children;
300 getChildren(children);
301 return descriptionOfElements(children);
304 JSStringRef AccessibilityUIElement::allAttributes()
306 NSString* attributes = attributesOfElement(m_element);
307 return [attributes createJSStringRef];
310 JSStringRef AccessibilityUIElement::attributeValue(JSStringRef attribute)
312 id value = [m_element accessibilityAttributeValue:[NSString stringWithJSStringRef:attribute]];
313 if (![value isKindOfClass:[NSString class]])
315 return [value createJSStringRef];
318 bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute)
320 return [m_element accessibilityIsAttributeSettable:[NSString stringWithJSStringRef:attribute]];
323 JSStringRef AccessibilityUIElement::parameterizedAttributeNames()
325 NSArray* supportedParameterizedAttributes = [m_element accessibilityParameterizedAttributeNames];
327 NSMutableString* attributesString = [NSMutableString string];
328 for (NSUInteger i = 0; i < [supportedParameterizedAttributes count]; ++i) {
329 [attributesString appendFormat:@"%@\n", [supportedParameterizedAttributes objectAtIndex:i]];
332 return [attributesString createJSStringRef];
335 JSStringRef AccessibilityUIElement::role()
337 NSString* role = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityRoleAttribute], m_element);
338 return concatenateAttributeAndValue(@"AXRole", role);
341 JSStringRef AccessibilityUIElement::subrole()
343 NSString* role = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilitySubroleAttribute], m_element);
344 return concatenateAttributeAndValue(@"AXSubrole", role);
347 JSStringRef AccessibilityUIElement::title()
349 NSString* title = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityTitleAttribute], m_element);
350 return concatenateAttributeAndValue(@"AXTitle", title);
353 JSStringRef AccessibilityUIElement::description()
355 id description = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityDescriptionAttribute], m_element);
356 return concatenateAttributeAndValue(@"AXDescription", description);
359 JSStringRef AccessibilityUIElement::stringValue()
361 id description = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityValueAttribute], m_element);
362 return concatenateAttributeAndValue(@"AXValue", description);
365 JSStringRef AccessibilityUIElement::language()
367 id description = descriptionOfValue([m_element accessibilityAttributeValue:@"AXLanguage"], m_element);
368 return concatenateAttributeAndValue(@"AXLanguage", description);
371 double AccessibilityUIElement::x()
373 NSValue* positionValue = [m_element accessibilityAttributeValue:NSAccessibilityPositionAttribute];
374 return static_cast<double>([positionValue pointValue].x);
377 double AccessibilityUIElement::y()
379 NSValue* positionValue = [m_element accessibilityAttributeValue:NSAccessibilityPositionAttribute];
380 return static_cast<double>([positionValue pointValue].y);
383 double AccessibilityUIElement::width()
385 NSValue* sizeValue = [m_element accessibilityAttributeValue:NSAccessibilitySizeAttribute];
386 return static_cast<double>([sizeValue sizeValue].width);
389 double AccessibilityUIElement::height()
391 NSValue* sizeValue = [m_element accessibilityAttributeValue:NSAccessibilitySizeAttribute];
392 return static_cast<double>([sizeValue sizeValue].height);
395 double AccessibilityUIElement::clickPointX()
397 NSValue* positionValue = [m_element accessibilityAttributeValue:@"AXClickPoint"];
398 return static_cast<double>([positionValue pointValue].x);
401 double AccessibilityUIElement::clickPointY()
403 NSValue* positionValue = [m_element accessibilityAttributeValue:@"AXClickPoint"];
404 return static_cast<double>([positionValue pointValue].y);
407 double AccessibilityUIElement::intValue()
409 id value = [m_element accessibilityAttributeValue:NSAccessibilityValueAttribute];
410 if ([value isKindOfClass:[NSNumber class]])
411 return [(NSNumber*)value doubleValue];
415 double AccessibilityUIElement::minValue()
417 id value = [m_element accessibilityAttributeValue:NSAccessibilityMinValueAttribute];
418 if ([value isKindOfClass:[NSNumber class]])
419 return [(NSNumber*)value doubleValue];
423 double AccessibilityUIElement::maxValue()
425 id value = [m_element accessibilityAttributeValue:NSAccessibilityMaxValueAttribute];
426 if ([value isKindOfClass:[NSNumber class]])
427 return [(NSNumber*)value doubleValue];
431 JSStringRef AccessibilityUIElement::valueDescription()
433 NSString* valueDescription = [m_element accessibilityAttributeValue:NSAccessibilityValueDescriptionAttribute];
434 if ([valueDescription isKindOfClass:[NSString class]])
435 return [valueDescription createJSStringRef];
439 int AccessibilityUIElement::insertionPointLineNumber()
441 id value = [m_element accessibilityAttributeValue:NSAccessibilityInsertionPointLineNumberAttribute];
442 if ([value isKindOfClass:[NSNumber class]])
443 return [(NSNumber *)value intValue];
447 bool AccessibilityUIElement::isActionSupported(JSStringRef action)
449 NSArray* actions = [m_element accessibilityActionNames];
450 return [actions containsObject:[NSString stringWithJSStringRef:action]];
453 bool AccessibilityUIElement::isEnabled()
455 id value = [m_element accessibilityAttributeValue:NSAccessibilityEnabledAttribute];
456 if ([value isKindOfClass:[NSNumber class]])
457 return [value boolValue];
461 bool AccessibilityUIElement::isRequired() const
463 id value = [m_element accessibilityAttributeValue:@"AXRequired"];
464 if ([value isKindOfClass:[NSNumber class]])
465 return [value boolValue];
469 bool AccessibilityUIElement::isSelected() const
471 id value = [m_element accessibilityAttributeValue:NSAccessibilitySelectedAttribute];
472 if ([value isKindOfClass:[NSNumber class]])
473 return [value boolValue];
477 bool AccessibilityUIElement::isExpanded() const
479 id value = [m_element accessibilityAttributeValue:NSAccessibilityExpandedAttribute];
480 if ([value isKindOfClass:[NSNumber class]])
481 return [value boolValue];
485 int AccessibilityUIElement::hierarchicalLevel() const
487 id value = [m_element accessibilityAttributeValue:NSAccessibilityDisclosureLevelAttribute];
488 if ([value isKindOfClass:[NSNumber class]])
489 return [value intValue];
493 // parameterized attributes
494 int AccessibilityUIElement::lineForIndex(int index)
496 id value = [m_element accessibilityAttributeValue:NSAccessibilityLineForIndexParameterizedAttribute forParameter:[NSNumber numberWithInt:index]];
497 if ([value isKindOfClass:[NSNumber class]])
498 return [(NSNumber *)value intValue];
502 JSStringRef AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
504 NSRange range = NSMakeRange(location, length);
505 id value = [m_element accessibilityAttributeValue:NSAccessibilityBoundsForRangeParameterizedAttribute forParameter:[NSValue valueWithRange:range]];
506 NSRect rect = NSMakeRect(0,0,0,0);
507 if ([value isKindOfClass:[NSValue class]])
508 rect = [value rectValue];
510 // don't return position information because it is platform dependent
511 NSMutableString* boundsDescription = [NSMutableString stringWithFormat:@"{{%f, %f}, {%f, %f}}",-1.0f,-1.0f,rect.size.width,rect.size.height];
512 return [boundsDescription createJSStringRef];
515 JSStringRef AccessibilityUIElement::stringForRange(unsigned location, unsigned length)
517 NSRange range = NSMakeRange(location, length);
518 id string = [m_element accessibilityAttributeValue:NSAccessibilityStringForRangeParameterizedAttribute forParameter:[NSValue valueWithRange:range]];
519 if (![string isKindOfClass:[NSString class]])
522 return [string createJSStringRef];
525 JSStringRef AccessibilityUIElement::attributesOfColumnHeaders()
527 // not yet defined in AppKit... odd
528 NSArray* columnHeadersArray = [m_element accessibilityAttributeValue:@"AXColumnHeaderUIElements"];
529 Vector<AccessibilityUIElement> columnHeadersVector;
530 convertNSArrayToVector(columnHeadersArray, columnHeadersVector);
531 return descriptionOfElements(columnHeadersVector);
534 JSStringRef AccessibilityUIElement::attributesOfRowHeaders()
536 NSArray* rowHeadersArray = [m_element accessibilityAttributeValue:@"AXRowHeaderUIElements"];
537 Vector<AccessibilityUIElement> rowHeadersVector;
538 convertNSArrayToVector(rowHeadersArray, rowHeadersVector);
539 return descriptionOfElements(rowHeadersVector);
542 JSStringRef AccessibilityUIElement::attributesOfColumns()
544 NSArray* columnsArray = [m_element accessibilityAttributeValue:NSAccessibilityColumnsAttribute];
545 Vector<AccessibilityUIElement> columnsVector;
546 convertNSArrayToVector(columnsArray, columnsVector);
547 return descriptionOfElements(columnsVector);
550 JSStringRef AccessibilityUIElement::attributesOfRows()
552 NSArray* rowsArray = [m_element accessibilityAttributeValue:NSAccessibilityRowsAttribute];
553 Vector<AccessibilityUIElement> rowsVector;
554 convertNSArrayToVector(rowsArray, rowsVector);
555 return descriptionOfElements(rowsVector);
558 JSStringRef AccessibilityUIElement::attributesOfVisibleCells()
560 NSArray* cellsArray = [m_element accessibilityAttributeValue:@"AXVisibleCells"];
561 Vector<AccessibilityUIElement> cellsVector;
562 convertNSArrayToVector(cellsArray, cellsVector);
563 return descriptionOfElements(cellsVector);
566 JSStringRef AccessibilityUIElement::attributesOfHeader()
568 id headerObject = [m_element accessibilityAttributeValue:NSAccessibilityHeaderAttribute];
570 return [@"" createJSStringRef];
572 Vector<AccessibilityUIElement> headerVector;
573 headerVector.append(headerObject);
574 return descriptionOfElements(headerVector);
577 int AccessibilityUIElement::indexInTable()
579 NSNumber* indexNumber = [m_element accessibilityAttributeValue:NSAccessibilityIndexAttribute];
582 return [indexNumber intValue];
585 JSStringRef AccessibilityUIElement::rowIndexRange()
587 NSValue* indexRange = [m_element accessibilityAttributeValue:@"AXRowIndexRange"];
588 NSRange range = indexRange ? [indexRange rangeValue] : NSMakeRange(0,0);
589 NSMutableString* rangeDescription = [NSMutableString stringWithFormat:@"{%d, %d}",range.location, range.length];
590 return [rangeDescription createJSStringRef];
593 JSStringRef AccessibilityUIElement::columnIndexRange()
595 NSNumber* indexRange = [m_element accessibilityAttributeValue:@"AXColumnIndexRange"];
596 NSRange range = indexRange ? [indexRange rangeValue] : NSMakeRange(0,0);
597 NSMutableString* rangeDescription = [NSMutableString stringWithFormat:@"{%d, %d}",range.location, range.length];
598 return [rangeDescription createJSStringRef];
601 AccessibilityUIElement AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row)
603 NSArray *colRowArray = [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInt:col], [NSNumber numberWithUnsignedInt:row], nil];
604 return [m_element accessibilityAttributeValue:@"AXCellForColumnAndRow" forParameter:colRowArray];
607 JSStringRef AccessibilityUIElement::selectedTextRange()
609 NSNumber *indexRange = [m_element accessibilityAttributeValue:NSAccessibilitySelectedTextRangeAttribute];
610 NSRange range = indexRange ? [indexRange rangeValue] : NSMakeRange(0,0);
611 NSMutableString *rangeDescription = [NSMutableString stringWithFormat:@"{%d, %d}",range.location, range.length];
612 return [rangeDescription createJSStringRef];
615 void AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
617 NSRange textRange = NSMakeRange(location, length);
618 NSValue *textRangeValue = [NSValue valueWithRange:textRange];
619 [m_element accessibilitySetValue:textRangeValue forAttribute:NSAccessibilitySelectedTextRangeAttribute];
622 void AccessibilityUIElement::increment()
624 [m_element accessibilityPerformAction:NSAccessibilityIncrementAction];
627 void AccessibilityUIElement::decrement()
629 [m_element accessibilityPerformAction:NSAccessibilityDecrementAction];
632 void AccessibilityUIElement::showMenu()
634 [m_element accessibilityPerformAction:NSAccessibilityShowMenuAction];
637 JSStringRef AccessibilityUIElement::accessibilityValue() const
640 return JSStringCreateWithCharacters(0, 0);