blob: e2f059716162a40cb701ba3679fc4493549c02be [file] [log] [blame]
/*
* Copyright (C) 2008 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "DumpRenderTree.h"
#import "AccessibilityUIElement.h"
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JSRetainPtr.h>
#import <JavaScriptCore/JSStringRef.h>
#import <JavaScriptCore/JSStringRefCF.h>
#import <WebKit/WebFrame.h>
#import <WebKit/WebHTMLView.h>
#import <WebKit/WebTypesInternal.h>
#import <wtf/RetainPtr.h>
#import <wtf/Vector.h>
#ifdef BUILDING_ON_TIGER
#define NSAccessibilityValueDescriptionAttribute @"AXValueDescription"
#endif
#ifndef NSAccessibilityOwnsAttribute
#define NSAccessibilityOwnsAttribute @"AXOwns"
#endif
#ifndef NSAccessibilityGrabbedAttribute
#define NSAccessibilityGrabbedAttribute @"AXGrabbed"
#endif
#ifndef NSAccessibilityDropEffectsAttribute
#define NSAccessibilityDropEffectsAttribute @"AXDropEffects"
#endif
// If an unsupported attribute is passed in, it will raise an accessibility exception. These are usually caught by the Accessibility Runtime to inform
// the AX client app of the error. However, DRT is the AX client app, so it must catch these exceptions.
#define BEGIN_AX_OBJC_EXCEPTIONS @try {
#define END_AX_OBJC_EXCEPTIONS } @catch(NSException *e) { if (![[e name] isEqualToString:NSAccessibilityException]) @throw; }
typedef void (*AXPostedNotificationCallback)(id element, NSString* notification, void* context);
@interface NSObject (WebKitAccessibilityAdditions)
- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount;
- (void)accessibilitySetShouldRepostNotifications:(BOOL)repost;
- (NSUInteger)accessibilityIndexOfChild:(id)child;
- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute;
@end
@interface NSString (JSStringRefAdditions)
+ (NSString *)stringWithJSStringRef:(JSStringRef)jsStringRef;
- (JSStringRef)createJSStringRef;
@end
@implementation NSString (JSStringRefAdditions)
+ (NSString *)stringWithJSStringRef:(JSStringRef)jsStringRef
{
if (!jsStringRef)
return NULL;
CFStringRef cfString = JSStringCopyCFString(kCFAllocatorDefault, jsStringRef);
return [(NSString *)cfString autorelease];
}
- (JSStringRef)createJSStringRef
{
return JSStringCreateWithCFString((CFStringRef)self);
}
@end
@interface AccessibilityNotificationHandler : NSObject
{
id m_platformElement;
JSObjectRef m_notificationFunctionCallback;
}
@end
@implementation AccessibilityNotificationHandler
- (id)initWithPlatformElement:(id)platformElement
{
self = [super init];
m_platformElement = platformElement;
// Once an object starts requesting notifications, it's on for the duration of the program.
// This is to avoid any race conditions between tests turning this flag on and off. Instead
// AccessibilityNotificationHandler can just listen when they want to.
[m_platformElement accessibilitySetShouldRepostNotifications:YES];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_notificationReceived:) name:@"AXDRTNotification" object:nil];
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
JSValueUnprotect([mainFrame globalContext], m_notificationFunctionCallback);
m_notificationFunctionCallback = 0;
[super dealloc];
}
- (void)_notificationReceived:(NSNotification *)notification
{
NSString *notificationName = [[notification userInfo] objectForKey:@"notificationName"];
if (!notificationName)
return;
JSRetainPtr<JSStringRef> jsNotification(Adopt, [notificationName createJSStringRef]);
JSValueRef argument = JSValueMakeString([mainFrame globalContext], jsNotification.get());
JSObjectCallAsFunction([mainFrame globalContext], m_notificationFunctionCallback, 0, 1, &argument, 0);
}
- (void)setCallback:(JSObjectRef)callback
{
if (!callback)
return;
// Release the old callback.
if (m_notificationFunctionCallback)
JSValueUnprotect([mainFrame globalContext], m_notificationFunctionCallback);
m_notificationFunctionCallback = callback;
JSValueProtect([mainFrame globalContext], m_notificationFunctionCallback);
}
@end
AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element)
: m_element(element)
, m_notificationHandler(0)
{
// FIXME: ap@webkit.org says ObjC objects need to be CFRetained/CFRelease to be GC-compliant on the mac.
[m_element retain];
}
AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other)
: m_element(other.m_element)
, m_notificationHandler(0)
{
[m_element retain];
}
AccessibilityUIElement::~AccessibilityUIElement()
{
// The notification handler should be nil because removeNotificationListener() should have been called in the test.
ASSERT(!m_notificationHandler);
[m_element release];
}
static NSString* descriptionOfValue(id valueObject, id focusedAccessibilityObject)
{
if (!valueObject)
return NULL;
if ([valueObject isKindOfClass:[NSArray class]])
return [NSString stringWithFormat:@"<array of size %d>", [(NSArray*)valueObject count]];
if ([valueObject isKindOfClass:[NSNumber class]])
return [(NSNumber*)valueObject stringValue];
if ([valueObject isKindOfClass:[NSValue class]]) {
NSString* type = [NSString stringWithCString:[valueObject objCType] encoding:NSASCIIStringEncoding];
NSValue* value = (NSValue*)valueObject;
if ([type rangeOfString:@"NSRect"].length > 0)
return [NSString stringWithFormat:@"NSRect: %@", NSStringFromRect([value rectValue])];
if ([type rangeOfString:@"NSPoint"].length > 0)
return [NSString stringWithFormat:@"NSPoint: %@", NSStringFromPoint([value pointValue])];
if ([type rangeOfString:@"NSSize"].length > 0)
return [NSString stringWithFormat:@"NSSize: %@", NSStringFromSize([value sizeValue])];
if ([type rangeOfString:@"NSRange"].length > 0)
return [NSString stringWithFormat:@"NSRange: %@", NSStringFromRange([value rangeValue])];
}
// Strip absolute URL paths
NSString* description = [valueObject description];
NSRange range = [description rangeOfString:@"LayoutTests"];
if (range.length)
return [description substringFromIndex:range.location];
// Strip pointer locations
if ([description rangeOfString:@"0x"].length) {
NSString* role = [focusedAccessibilityObject accessibilityAttributeValue:NSAccessibilityRoleAttribute];
NSString* title = [focusedAccessibilityObject accessibilityAttributeValue:NSAccessibilityTitleAttribute];
if ([title length])
return [NSString stringWithFormat:@"<%@: '%@'>", role, title];
return [NSString stringWithFormat:@"<%@>", role];
}
return [valueObject description];
}
static NSString* attributesOfElement(id accessibilityObject)
{
NSArray* supportedAttributes = [accessibilityObject accessibilityAttributeNames];
NSMutableString* attributesString = [NSMutableString string];
for (NSUInteger i = 0; i < [supportedAttributes count]; ++i) {
NSString* attribute = [supportedAttributes objectAtIndex:i];
// Right now, position provides useless and screen-specific information, so we do not
// want to include it for the sake of universally passing tests.
if ([attribute isEqualToString:@"AXPosition"])
continue;
// accessibilityAttributeValue: can throw an if an attribute is not returned.
// For DumpRenderTree's purpose, we should ignore those exceptions
BEGIN_AX_OBJC_EXCEPTIONS
id valueObject = [accessibilityObject accessibilityAttributeValue:attribute];
NSString* value = descriptionOfValue(valueObject, accessibilityObject);
[attributesString appendFormat:@"%@: %@\n", attribute, value];
END_AX_OBJC_EXCEPTIONS
}
return attributesString;
}
static JSStringRef concatenateAttributeAndValue(NSString* attribute, NSString* value)
{
Vector<UniChar> buffer([attribute length]);
[attribute getCharacters:buffer.data()];
buffer.append(':');
buffer.append(' ');
Vector<UniChar> valueBuffer([value length]);
[value getCharacters:valueBuffer.data()];
buffer.append(valueBuffer);
return JSStringCreateWithCharacters(buffer.data(), buffer.size());
}
static void convertNSArrayToVector(NSArray* array, Vector<AccessibilityUIElement>& elementVector)
{
NSUInteger count = [array count];
for (NSUInteger i = 0; i < count; ++i)
elementVector.append(AccessibilityUIElement([array objectAtIndex:i]));
}
static JSStringRef descriptionOfElements(Vector<AccessibilityUIElement>& elementVector)
{
NSMutableString* allElementString = [NSMutableString string];
size_t size = elementVector.size();
for (size_t i = 0; i < size; ++i) {
NSString* attributes = attributesOfElement(elementVector[i].platformUIElement());
[allElementString appendFormat:@"%@\n------------\n", attributes];
}
return [allElementString createJSStringRef];
}
void AccessibilityUIElement::getLinkedUIElements(Vector<AccessibilityUIElement>& elementVector)
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* linkedElements = [m_element accessibilityAttributeValue:NSAccessibilityLinkedUIElementsAttribute];
convertNSArrayToVector(linkedElements, elementVector);
END_AX_OBJC_EXCEPTIONS
}
void AccessibilityUIElement::getDocumentLinks(Vector<AccessibilityUIElement>& elementVector)
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* linkElements = [m_element accessibilityAttributeValue:@"AXLinkUIElements"];
convertNSArrayToVector(linkElements, elementVector);
END_AX_OBJC_EXCEPTIONS
}
void AccessibilityUIElement::getChildren(Vector<AccessibilityUIElement>& elementVector)
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* children = [m_element accessibilityAttributeValue:NSAccessibilityChildrenAttribute];
convertNSArrayToVector(children, elementVector);
END_AX_OBJC_EXCEPTIONS
}
void AccessibilityUIElement::getChildrenWithRange(Vector<AccessibilityUIElement>& elementVector, unsigned location, unsigned length)
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* children = [m_element accessibilityArrayAttributeValues:NSAccessibilityChildrenAttribute index:location maxCount:length];
convertNSArrayToVector(children, elementVector);
END_AX_OBJC_EXCEPTIONS
}
int AccessibilityUIElement::childrenCount()
{
Vector<AccessibilityUIElement> children;
getChildren(children);
return children.size();
}
AccessibilityUIElement AccessibilityUIElement::elementAtPoint(int x, int y)
{
id element = [m_element accessibilityHitTest:NSMakePoint(x, y)];
if (!element)
return nil;
return AccessibilityUIElement(element);
}
unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element)
{
return [m_element accessibilityIndexOfChild:element->platformUIElement()];
}
AccessibilityUIElement AccessibilityUIElement::getChildAtIndex(unsigned index)
{
Vector<AccessibilityUIElement> children;
getChildrenWithRange(children, index, 1);
if (children.size() == 1)
return children[0];
return 0;
}
AccessibilityUIElement AccessibilityUIElement::linkedUIElementAtIndex(unsigned index)
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* objects = [m_element accessibilityAttributeValue:NSAccessibilityLinkedUIElementsAttribute];
if (index < [objects count])
return [objects objectAtIndex:index];
END_AX_OBJC_EXCEPTIONS
return 0;
}
AccessibilityUIElement AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index)
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* objects = [m_element accessibilityAttributeValue:NSAccessibilityOwnsAttribute];
if (index < [objects count])
return [objects objectAtIndex:index];
END_AX_OBJC_EXCEPTIONS
return 0;
}
AccessibilityUIElement AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index)
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* objects = [m_element accessibilityAttributeValue:NSAccessibilityLinkedUIElementsAttribute];
if (index < [objects count])
return [objects objectAtIndex:index];
END_AX_OBJC_EXCEPTIONS
return 0;
}
AccessibilityUIElement AccessibilityUIElement::disclosedRowAtIndex(unsigned index)
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* rows = [m_element accessibilityAttributeValue:NSAccessibilityDisclosedRowsAttribute];
if (index < [rows count])
return [rows objectAtIndex:index];
END_AX_OBJC_EXCEPTIONS
return 0;
}
AccessibilityUIElement AccessibilityUIElement::selectedChildAtIndex(unsigned index) const
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* array = [m_element accessibilityAttributeValue:NSAccessibilitySelectedChildrenAttribute];
if (index < [array count])
return [array objectAtIndex:index];
END_AX_OBJC_EXCEPTIONS
return 0;
}
unsigned AccessibilityUIElement::selectedChildrenCount() const
{
BEGIN_AX_OBJC_EXCEPTIONS
return [m_element accessibilityArrayAttributeCount:NSAccessibilitySelectedChildrenAttribute];
END_AX_OBJC_EXCEPTIONS
return 0;
}
AccessibilityUIElement AccessibilityUIElement::selectedRowAtIndex(unsigned index)
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* rows = [m_element accessibilityAttributeValue:NSAccessibilitySelectedRowsAttribute];
if (index < [rows count])
return [rows objectAtIndex:index];
END_AX_OBJC_EXCEPTIONS
return 0;
}
AccessibilityUIElement AccessibilityUIElement::titleUIElement()
{
BEGIN_AX_OBJC_EXCEPTIONS
id accessibilityObject = [m_element accessibilityAttributeValue:NSAccessibilityTitleUIElementAttribute];
if (accessibilityObject)
return AccessibilityUIElement(accessibilityObject);
END_AX_OBJC_EXCEPTIONS
return 0;
}
AccessibilityUIElement AccessibilityUIElement::parentElement()
{
BEGIN_AX_OBJC_EXCEPTIONS
id accessibilityObject = [m_element accessibilityAttributeValue:NSAccessibilityParentAttribute];
if (accessibilityObject)
return AccessibilityUIElement(accessibilityObject);
END_AX_OBJC_EXCEPTIONS
return 0;
}
AccessibilityUIElement AccessibilityUIElement::disclosedByRow()
{
BEGIN_AX_OBJC_EXCEPTIONS
id accessibilityObject = [m_element accessibilityAttributeValue:NSAccessibilityDisclosedByRowAttribute];
if (accessibilityObject)
return AccessibilityUIElement(accessibilityObject);
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::attributesOfLinkedUIElements()
{
Vector<AccessibilityUIElement> linkedElements;
getLinkedUIElements(linkedElements);
return descriptionOfElements(linkedElements);
}
JSStringRef AccessibilityUIElement::attributesOfDocumentLinks()
{
Vector<AccessibilityUIElement> linkElements;
getDocumentLinks(linkElements);
return descriptionOfElements(linkElements);
}
JSStringRef AccessibilityUIElement::attributesOfChildren()
{
Vector<AccessibilityUIElement> children;
getChildren(children);
return descriptionOfElements(children);
}
JSStringRef AccessibilityUIElement::allAttributes()
{
NSString* attributes = attributesOfElement(m_element);
return [attributes createJSStringRef];
}
JSStringRef AccessibilityUIElement::stringAttributeValue(JSStringRef attribute)
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:[NSString stringWithJSStringRef:attribute]];
if ([value isKindOfClass:[NSString class]])
return [value createJSStringRef];
END_AX_OBJC_EXCEPTIONS
return 0;
}
bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute)
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:[NSString stringWithJSStringRef:attribute]];
if ([value isKindOfClass:[NSNumber class]])
return [value boolValue];
END_AX_OBJC_EXCEPTIONS
return false;
}
bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute)
{
BEGIN_AX_OBJC_EXCEPTIONS
return [m_element accessibilityIsAttributeSettable:[NSString stringWithJSStringRef:attribute]];
END_AX_OBJC_EXCEPTIONS
return false;
}
bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute)
{
BEGIN_AX_OBJC_EXCEPTIONS
return [[m_element accessibilityAttributeNames] containsObject:[NSString stringWithJSStringRef:attribute]];
END_AX_OBJC_EXCEPTIONS
return false;
}
JSStringRef AccessibilityUIElement::parameterizedAttributeNames()
{
NSArray* supportedParameterizedAttributes = [m_element accessibilityParameterizedAttributeNames];
NSMutableString* attributesString = [NSMutableString string];
for (NSUInteger i = 0; i < [supportedParameterizedAttributes count]; ++i) {
[attributesString appendFormat:@"%@\n", [supportedParameterizedAttributes objectAtIndex:i]];
}
return [attributesString createJSStringRef];
}
JSStringRef AccessibilityUIElement::role()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSString *role = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityRoleAttribute], m_element);
return concatenateAttributeAndValue(@"AXRole", role);
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::subrole()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSString* role = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilitySubroleAttribute], m_element);
return concatenateAttributeAndValue(@"AXSubrole", role);
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::roleDescription()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSString* role = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityRoleDescriptionAttribute], m_element);
return concatenateAttributeAndValue(@"AXRoleDescription", role);
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::title()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSString* title = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityTitleAttribute], m_element);
return concatenateAttributeAndValue(@"AXTitle", title);
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::description()
{
BEGIN_AX_OBJC_EXCEPTIONS
id description = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityDescriptionAttribute], m_element);
return concatenateAttributeAndValue(@"AXDescription", description);
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::orientation() const
{
BEGIN_AX_OBJC_EXCEPTIONS
id description = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityOrientationAttribute], m_element);
return concatenateAttributeAndValue(@"AXOrientation", description);
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::stringValue()
{
BEGIN_AX_OBJC_EXCEPTIONS
id description = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityValueAttribute], m_element);
return concatenateAttributeAndValue(@"AXValue", description);
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::language()
{
BEGIN_AX_OBJC_EXCEPTIONS
id description = descriptionOfValue([m_element accessibilityAttributeValue:@"AXLanguage"], m_element);
return concatenateAttributeAndValue(@"AXLanguage", description);
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::helpText() const
{
BEGIN_AX_OBJC_EXCEPTIONS
id description = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityHelpAttribute], m_element);
return concatenateAttributeAndValue(@"AXHelp", description);
END_AX_OBJC_EXCEPTIONS
return 0;
}
double AccessibilityUIElement::x()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSValue* positionValue = [m_element accessibilityAttributeValue:NSAccessibilityPositionAttribute];
return static_cast<double>([positionValue pointValue].x);
END_AX_OBJC_EXCEPTIONS
return 0.0f;
}
double AccessibilityUIElement::y()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSValue* positionValue = [m_element accessibilityAttributeValue:NSAccessibilityPositionAttribute];
return static_cast<double>([positionValue pointValue].y);
END_AX_OBJC_EXCEPTIONS
return 0.0f;
}
double AccessibilityUIElement::width()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSValue* sizeValue = [m_element accessibilityAttributeValue:NSAccessibilitySizeAttribute];
return static_cast<double>([sizeValue sizeValue].width);
END_AX_OBJC_EXCEPTIONS
return 0.0f;
}
double AccessibilityUIElement::height()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSValue* sizeValue = [m_element accessibilityAttributeValue:NSAccessibilitySizeAttribute];
return static_cast<double>([sizeValue sizeValue].height);
END_AX_OBJC_EXCEPTIONS
return 0.0f;
}
double AccessibilityUIElement::clickPointX()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSValue* positionValue = [m_element accessibilityAttributeValue:@"AXClickPoint"];
return static_cast<double>([positionValue pointValue].x);
END_AX_OBJC_EXCEPTIONS
return 0.0f;
}
double AccessibilityUIElement::clickPointY()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSValue* positionValue = [m_element accessibilityAttributeValue:@"AXClickPoint"];
return static_cast<double>([positionValue pointValue].y);
END_AX_OBJC_EXCEPTIONS
return 0.0f;
}
double AccessibilityUIElement::intValue() const
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:NSAccessibilityValueAttribute];
if ([value isKindOfClass:[NSNumber class]])
return [(NSNumber*)value doubleValue];
END_AX_OBJC_EXCEPTIONS
return 0.0f;
}
double AccessibilityUIElement::minValue()
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:NSAccessibilityMinValueAttribute];
if ([value isKindOfClass:[NSNumber class]])
return [(NSNumber*)value doubleValue];
END_AX_OBJC_EXCEPTIONS
return 0.0f;
}
double AccessibilityUIElement::maxValue()
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:NSAccessibilityMaxValueAttribute];
if ([value isKindOfClass:[NSNumber class]])
return [(NSNumber*)value doubleValue];
END_AX_OBJC_EXCEPTIONS
return 0.0;
}
JSStringRef AccessibilityUIElement::valueDescription()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSString* valueDescription = [m_element accessibilityAttributeValue:NSAccessibilityValueDescriptionAttribute];
if ([valueDescription isKindOfClass:[NSString class]])
return [valueDescription createJSStringRef];
END_AX_OBJC_EXCEPTIONS
return 0;
}
int AccessibilityUIElement::insertionPointLineNumber()
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:NSAccessibilityInsertionPointLineNumberAttribute];
if ([value isKindOfClass:[NSNumber class]])
return [(NSNumber *)value intValue];
END_AX_OBJC_EXCEPTIONS
return -1;
}
bool AccessibilityUIElement::isActionSupported(JSStringRef action)
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* actions = [m_element accessibilityActionNames];
return [actions containsObject:[NSString stringWithJSStringRef:action]];
END_AX_OBJC_EXCEPTIONS
return false;
}
bool AccessibilityUIElement::isEnabled()
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:NSAccessibilityEnabledAttribute];
if ([value isKindOfClass:[NSNumber class]])
return [value boolValue];
END_AX_OBJC_EXCEPTIONS
return false;
}
bool AccessibilityUIElement::isRequired() const
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:@"AXRequired"];
if ([value isKindOfClass:[NSNumber class]])
return [value boolValue];
END_AX_OBJC_EXCEPTIONS
return false;
}
bool AccessibilityUIElement::isFocused() const
{
// FIXME: implement
return false;
}
bool AccessibilityUIElement::isSelected() const
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:NSAccessibilitySelectedAttribute];
if ([value isKindOfClass:[NSNumber class]])
return [value boolValue];
END_AX_OBJC_EXCEPTIONS
return false;
}
bool AccessibilityUIElement::isExpanded() const
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:NSAccessibilityExpandedAttribute];
if ([value isKindOfClass:[NSNumber class]])
return [value boolValue];
END_AX_OBJC_EXCEPTIONS
return false;
}
bool AccessibilityUIElement::isChecked() const
{
// On the Mac, intValue()==1 if a a checkable control is checked.
return intValue() == 1;
}
int AccessibilityUIElement::hierarchicalLevel() const
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:NSAccessibilityDisclosureLevelAttribute];
if ([value isKindOfClass:[NSNumber class]])
return [value intValue];
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::speak()
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:@"AXDRTSpeechAttribute"];
if ([value isKindOfClass:[NSString class]])
return [value createJSStringRef];
END_AX_OBJC_EXCEPTIONS
return 0;
}
bool AccessibilityUIElement::ariaIsGrabbed() const
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:NSAccessibilityGrabbedAttribute];
if ([value isKindOfClass:[NSNumber class]])
return [value boolValue];
END_AX_OBJC_EXCEPTIONS
return false;
}
JSStringRef AccessibilityUIElement::ariaDropEffects() const
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:NSAccessibilityDropEffectsAttribute];
if (![value isKindOfClass:[NSArray class]])
return 0;
NSMutableString* dropEffects = [NSMutableString string];
NSInteger length = [value count];
for (NSInteger k = 0; k < length; ++k) {
[dropEffects appendString:[value objectAtIndex:k]];
if (k < length - 1)
[dropEffects appendString:@","];
}
return [dropEffects createJSStringRef];
END_AX_OBJC_EXCEPTIONS
return 0;
}
// parameterized attributes
int AccessibilityUIElement::lineForIndex(int index)
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:NSAccessibilityLineForIndexParameterizedAttribute forParameter:[NSNumber numberWithInt:index]];
if ([value isKindOfClass:[NSNumber class]])
return [(NSNumber *)value intValue];
END_AX_OBJC_EXCEPTIONS
return -1;
}
JSStringRef AccessibilityUIElement::rangeForLine(int line)
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:NSAccessibilityRangeForLineParameterizedAttribute forParameter:[NSNumber numberWithInt:line]];
if ([value isKindOfClass:[NSValue class]]) {
return [NSStringFromRange([value rangeValue]) createJSStringRef];
}
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
{
NSRange range = NSMakeRange(location, length);
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:NSAccessibilityBoundsForRangeParameterizedAttribute forParameter:[NSValue valueWithRange:range]];
NSRect rect = NSMakeRect(0,0,0,0);
if ([value isKindOfClass:[NSValue class]])
rect = [value rectValue];
// don't return position information because it is platform dependent
NSMutableString* boundsDescription = [NSMutableString stringWithFormat:@"{{%f, %f}, {%f, %f}}",-1.0f,-1.0f,rect.size.width,rect.size.height];
return [boundsDescription createJSStringRef];
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::stringForRange(unsigned location, unsigned length)
{
NSRange range = NSMakeRange(location, length);
BEGIN_AX_OBJC_EXCEPTIONS
id string = [m_element accessibilityAttributeValue:NSAccessibilityStringForRangeParameterizedAttribute forParameter:[NSValue valueWithRange:range]];
if (![string isKindOfClass:[NSString class]])
return 0;
return [string createJSStringRef];
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::attributedStringForRange(unsigned location, unsigned length)
{
NSRange range = NSMakeRange(location, length);
BEGIN_AX_OBJC_EXCEPTIONS
NSAttributedString* string = [m_element accessibilityAttributeValue:NSAccessibilityAttributedStringForRangeParameterizedAttribute forParameter:[NSValue valueWithRange:range]];
if (![string isKindOfClass:[NSAttributedString class]])
return 0;
NSString* stringWithAttrs = [string description];
return [stringWithAttrs createJSStringRef];
END_AX_OBJC_EXCEPTIONS
return 0;
}
bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned location, unsigned length)
{
NSRange range = NSMakeRange(location, length);
BEGIN_AX_OBJC_EXCEPTIONS
NSAttributedString* string = [m_element accessibilityAttributeValue:NSAccessibilityAttributedStringForRangeParameterizedAttribute forParameter:[NSValue valueWithRange:range]];
if (![string isKindOfClass:[NSAttributedString class]])
return false;
NSDictionary* attrs = [string attributesAtIndex:0 effectiveRange:nil];
if([[attrs objectForKey:NSAccessibilityMisspelledTextAttribute] boolValue])
return true;
END_AX_OBJC_EXCEPTIONS
return false;
}
JSStringRef AccessibilityUIElement::attributesOfColumnHeaders()
{
// not yet defined in AppKit... odd
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* columnHeadersArray = [m_element accessibilityAttributeValue:@"AXColumnHeaderUIElements"];
Vector<AccessibilityUIElement> columnHeadersVector;
convertNSArrayToVector(columnHeadersArray, columnHeadersVector);
return descriptionOfElements(columnHeadersVector);
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::attributesOfRowHeaders()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* rowHeadersArray = [m_element accessibilityAttributeValue:@"AXRowHeaderUIElements"];
Vector<AccessibilityUIElement> rowHeadersVector;
convertNSArrayToVector(rowHeadersArray, rowHeadersVector);
return descriptionOfElements(rowHeadersVector);
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::attributesOfColumns()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* columnsArray = [m_element accessibilityAttributeValue:NSAccessibilityColumnsAttribute];
Vector<AccessibilityUIElement> columnsVector;
convertNSArrayToVector(columnsArray, columnsVector);
return descriptionOfElements(columnsVector);
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::attributesOfRows()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* rowsArray = [m_element accessibilityAttributeValue:NSAccessibilityRowsAttribute];
Vector<AccessibilityUIElement> rowsVector;
convertNSArrayToVector(rowsArray, rowsVector);
return descriptionOfElements(rowsVector);
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::attributesOfVisibleCells()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* cellsArray = [m_element accessibilityAttributeValue:@"AXVisibleCells"];
Vector<AccessibilityUIElement> cellsVector;
convertNSArrayToVector(cellsArray, cellsVector);
return descriptionOfElements(cellsVector);
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::attributesOfHeader()
{
BEGIN_AX_OBJC_EXCEPTIONS
id headerObject = [m_element accessibilityAttributeValue:NSAccessibilityHeaderAttribute];
if (!headerObject)
return [@"" createJSStringRef];
Vector<AccessibilityUIElement> headerVector;
headerVector.append(headerObject);
return descriptionOfElements(headerVector);
END_AX_OBJC_EXCEPTIONS
return 0;
}
int AccessibilityUIElement::rowCount()
{
BEGIN_AX_OBJC_EXCEPTIONS
return [m_element accessibilityArrayAttributeCount:NSAccessibilityRowsAttribute];
END_AX_OBJC_EXCEPTIONS
return 0;
}
int AccessibilityUIElement::columnCount()
{
BEGIN_AX_OBJC_EXCEPTIONS
return [m_element accessibilityArrayAttributeCount:NSAccessibilityColumnsAttribute];
END_AX_OBJC_EXCEPTIONS
return 0;
}
int AccessibilityUIElement::indexInTable()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSNumber* indexNumber = [m_element accessibilityAttributeValue:NSAccessibilityIndexAttribute];
if (indexNumber)
return [indexNumber intValue];
END_AX_OBJC_EXCEPTIONS
return -1;
}
JSStringRef AccessibilityUIElement::rowIndexRange()
{
NSRange range = NSMakeRange(0,0);
BEGIN_AX_OBJC_EXCEPTIONS
NSValue* indexRange = [m_element accessibilityAttributeValue:@"AXRowIndexRange"];
if (indexRange)
range = [indexRange rangeValue];
NSMutableString* rangeDescription = [NSMutableString stringWithFormat:@"{%d, %d}",range.location, range.length];
return [rangeDescription createJSStringRef];
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::columnIndexRange()
{
NSRange range = NSMakeRange(0,0);
BEGIN_AX_OBJC_EXCEPTIONS
NSNumber* indexRange = [m_element accessibilityAttributeValue:@"AXColumnIndexRange"];
if (indexRange)
range = [indexRange rangeValue];
NSMutableString* rangeDescription = [NSMutableString stringWithFormat:@"{%d, %d}",range.location, range.length];
return [rangeDescription createJSStringRef];
END_AX_OBJC_EXCEPTIONS
return 0;
}
AccessibilityUIElement AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row)
{
NSArray *colRowArray = [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInt:col], [NSNumber numberWithUnsignedInt:row], nil];
BEGIN_AX_OBJC_EXCEPTIONS
return [m_element accessibilityAttributeValue:@"AXCellForColumnAndRow" forParameter:colRowArray];
END_AX_OBJC_EXCEPTIONS
return 0;
}
JSStringRef AccessibilityUIElement::selectedTextRange()
{
NSRange range = NSMakeRange(NSNotFound, 0);
BEGIN_AX_OBJC_EXCEPTIONS
NSValue *indexRange = [m_element accessibilityAttributeValue:NSAccessibilitySelectedTextRangeAttribute];
if (indexRange)
range = [indexRange rangeValue];
NSMutableString *rangeDescription = [NSMutableString stringWithFormat:@"{%d, %d}",range.location, range.length];
return [rangeDescription createJSStringRef];
END_AX_OBJC_EXCEPTIONS
return 0;
}
void AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
{
NSRange textRange = NSMakeRange(location, length);
NSValue *textRangeValue = [NSValue valueWithRange:textRange];
BEGIN_AX_OBJC_EXCEPTIONS
[m_element accessibilitySetValue:textRangeValue forAttribute:NSAccessibilitySelectedTextRangeAttribute];
END_AX_OBJC_EXCEPTIONS
}
void AccessibilityUIElement::increment()
{
BEGIN_AX_OBJC_EXCEPTIONS
[m_element accessibilityPerformAction:NSAccessibilityIncrementAction];
END_AX_OBJC_EXCEPTIONS
}
void AccessibilityUIElement::decrement()
{
BEGIN_AX_OBJC_EXCEPTIONS
[m_element accessibilityPerformAction:NSAccessibilityDecrementAction];
END_AX_OBJC_EXCEPTIONS
}
void AccessibilityUIElement::showMenu()
{
BEGIN_AX_OBJC_EXCEPTIONS
[m_element accessibilityPerformAction:NSAccessibilityShowMenuAction];
END_AX_OBJC_EXCEPTIONS
}
void AccessibilityUIElement::press()
{
BEGIN_AX_OBJC_EXCEPTIONS
[m_element accessibilityPerformAction:NSAccessibilityPressAction];
END_AX_OBJC_EXCEPTIONS
}
void AccessibilityUIElement::setSelectedChild(AccessibilityUIElement* element) const
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* array = [NSArray arrayWithObject:element->platformUIElement()];
[m_element accessibilitySetValue:array forAttribute:NSAccessibilitySelectedChildrenAttribute];
END_AX_OBJC_EXCEPTIONS
}
JSStringRef AccessibilityUIElement::accessibilityValue() const
{
// FIXME: implement
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::documentEncoding()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::documentURI()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::url()
{
BEGIN_AX_OBJC_EXCEPTIONS
NSURL *url = [m_element accessibilityAttributeValue:NSAccessibilityURLAttribute];
return [[url absoluteString] createJSStringRef];
END_AX_OBJC_EXCEPTIONS
return nil;
}
bool AccessibilityUIElement::addNotificationListener(JSObjectRef functionCallback)
{
if (!functionCallback)
return false;
// Mac programmers should not be adding more than one notification listener per element.
// Other platforms may be different.
if (m_notificationHandler)
return false;
m_notificationHandler = [[AccessibilityNotificationHandler alloc] initWithPlatformElement:platformUIElement()];
[m_notificationHandler setCallback:functionCallback];
return true;
}
void AccessibilityUIElement::removeNotificationListener()
{
// Mac programmers should not be trying to remove a listener that's already removed.
ASSERT(m_notificationHandler);
[m_notificationHandler release];
m_notificationHandler = nil;
}
bool AccessibilityUIElement::isFocusable() const
{
// FIXME: implement
return false;
}
bool AccessibilityUIElement::isSelectable() const
{
// FIXME: implement
return false;
}
bool AccessibilityUIElement::isMultiSelectable() const
{
// FIXME: implement
return false;
}
bool AccessibilityUIElement::isVisible() const
{
// FIXME: implement
return false;
}
bool AccessibilityUIElement::isOffScreen() const
{
// FIXME: implement
return false;
}
bool AccessibilityUIElement::isCollapsed() const
{
// FIXME: implement
return false;
}
bool AccessibilityUIElement::isIgnored() const
{
BOOL result = NO;
BEGIN_AX_OBJC_EXCEPTIONS
result = [m_element accessibilityIsIgnored];
END_AX_OBJC_EXCEPTIONS
return result;
}
bool AccessibilityUIElement::hasPopup() const
{
BEGIN_AX_OBJC_EXCEPTIONS
id value = [m_element accessibilityAttributeValue:@"AXHasPopup"];
if ([value isKindOfClass:[NSNumber class]])
return [value boolValue];
END_AX_OBJC_EXCEPTIONS
return false;
}
void AccessibilityUIElement::takeFocus()
{
// FIXME: implement
}
void AccessibilityUIElement::takeSelection()
{
// FIXME: implement
}
void AccessibilityUIElement::addSelection()
{
// FIXME: implement
}
void AccessibilityUIElement::removeSelection()
{
// FIXME: implement
}
#if SUPPORTS_AX_TEXTMARKERS
// Text markers
AccessibilityTextMarkerRange AccessibilityUIElement::textMarkerRangeForElement(AccessibilityUIElement* element)
{
BEGIN_AX_OBJC_EXCEPTIONS
id textMarkerRange = [m_element accessibilityAttributeValue:@"AXTextMarkerRangeForUIElement" forParameter:element->platformUIElement()];
return AccessibilityTextMarkerRange(textMarkerRange);
END_AX_OBJC_EXCEPTIONS
return 0;
}
int AccessibilityUIElement::textMarkerRangeLength(AccessibilityTextMarkerRange* range)
{
BEGIN_AX_OBJC_EXCEPTIONS
NSNumber* lengthValue = [m_element accessibilityAttributeValue:@"AXLengthForTextMarkerRange" forParameter:(id)range->platformTextMarkerRange()];
return [lengthValue intValue];
END_AX_OBJC_EXCEPTIONS
return 0;
}
AccessibilityTextMarkerRange AccessibilityUIElement::textMarkerRangeForMarkers(AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker)
{
BEGIN_AX_OBJC_EXCEPTIONS
NSArray* textMarkers = [NSArray arrayWithObjects:(id)startMarker->platformTextMarker(), (id)endMarker->platformTextMarker(), nil];
id textMarkerRange = [m_element accessibilityAttributeValue:@"AXTextMarkerRangeForUnorderedTextMarkers" forParameter:textMarkers];
return AccessibilityTextMarkerRange(textMarkerRange);
END_AX_OBJC_EXCEPTIONS
return 0;
}
AccessibilityTextMarker AccessibilityUIElement::startTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
{
BEGIN_AX_OBJC_EXCEPTIONS
id textMarker = [m_element accessibilityAttributeValue:@"AXStartTextMarkerForTextMarkerRange" forParameter:(id)range->platformTextMarkerRange()];
return AccessibilityTextMarker(textMarker);
END_AX_OBJC_EXCEPTIONS
return 0;
}
AccessibilityTextMarker AccessibilityUIElement::endTextMarkerForTextMarkerRange(AccessibilityTextMarkerRange* range)
{
BEGIN_AX_OBJC_EXCEPTIONS
id textMarker = [m_element accessibilityAttributeValue:@"AXEndTextMarkerForTextMarkerRange" forParameter:(id)range->platformTextMarkerRange()];
return AccessibilityTextMarker(textMarker);
END_AX_OBJC_EXCEPTIONS
return 0;
}
AccessibilityTextMarker AccessibilityUIElement::textMarkerForPoint(int x, int y)
{
BEGIN_AX_OBJC_EXCEPTIONS
id textMarker = [m_element accessibilityAttributeValue:@"AXTextMarkerForPosition" forParameter:[NSValue valueWithPoint:NSMakePoint(x, y)]];
return AccessibilityTextMarker(textMarker);
END_AX_OBJC_EXCEPTIONS
return 0;
}
AccessibilityUIElement AccessibilityUIElement::accessibilityElementForTextMarker(AccessibilityTextMarker* marker)
{
BEGIN_AX_OBJC_EXCEPTIONS
id uiElement = [m_element accessibilityAttributeValue:@"AXUIElementForTextMarker" forParameter:(id)marker->platformTextMarker()];
return AccessibilityUIElement(uiElement);
END_AX_OBJC_EXCEPTIONS
return 0;
}
#endif // SUPPORTS_AX_TEXTMARKERS