/* * Copyright (C) 2009 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 "AccessibilityCommonMac.h" #import #import #import #import #import #import #import #import #import #import #if PLATFORM(IOS) #import typedef void (*AXPostedNotificationCallback)(id element, NSString* notification, void* context); AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element) : m_element(element) { [m_element retain]; } AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other) : m_element(other.m_element) { [m_element retain]; } AccessibilityUIElement::~AccessibilityUIElement() { [m_element release]; } @interface NSObject (UIAccessibilityHidden) - (id)accessibilityHitTest:(CGPoint)point; - (id)accessibilityLinkedElement; - (NSRange)accessibilityColumnRange; - (NSRange)accessibilityRowRange; - (id)accessibilityElementForRow:(NSInteger)row andColumn:(NSInteger)column; - (NSURL *)accessibilityURL; - (NSArray *)accessibilityHeaderElements; - (NSString *)accessibilityPlaceholderValue; - (NSString *)stringForRange:(NSRange)range; - (NSArray *)elementsForRange:(NSRange)range; - (NSString *)selectionRangeString; - (void)accessibilityModifySelection:(WebCore::TextGranularity)granularity increase:(BOOL)increase; - (void)accessibilitySetPostedNotificationCallback:(AXPostedNotificationCallback)function withContext:(void*)context; @end static JSStringRef concatenateAttributeAndValue(NSString* attribute, NSString* value) { Vector buffer([attribute length]); [attribute getCharacters:buffer.data()]; buffer.append(':'); buffer.append(' '); Vector valueBuffer([value length]); [value getCharacters:valueBuffer.data()]; buffer.append(valueBuffer); return JSStringCreateWithCharacters(buffer.data(), buffer.size()); } #pragma mark iPhone Attributes JSStringRef AccessibilityUIElement::iphoneLabel() { return concatenateAttributeAndValue(@"AXLabel", [m_element accessibilityLabel]); } JSStringRef AccessibilityUIElement::iphoneHint() { return concatenateAttributeAndValue(@"AXHint", [m_element accessibilityHint]); } JSStringRef AccessibilityUIElement::iphoneValue() { return concatenateAttributeAndValue(@"AXValue", [m_element accessibilityValue]); } JSStringRef AccessibilityUIElement::iphoneIdentifier() { return concatenateAttributeAndValue(@"AXIdentifier", [m_element accessibilityIdentifier]); } JSStringRef AccessibilityUIElement::iphoneTraits() { return concatenateAttributeAndValue(@"AXTraits", [NSString stringWithFormat:@"%qu", [m_element accessibilityTraits]]); } bool AccessibilityUIElement::iphoneIsElement() { return [m_element isAccessibilityElement]; } int AccessibilityUIElement::iphoneElementTextPosition() { NSRange range = [[m_element valueForKey:@"elementTextRange"] rangeValue]; return range.location; } int AccessibilityUIElement::iphoneElementTextLength() { NSRange range = [[m_element valueForKey:@"elementTextRange"] rangeValue]; return range.length; } JSStringRef AccessibilityUIElement::url() { NSURL *url = [m_element accessibilityURL]; return [[url absoluteString] createJSStringRef]; } double AccessibilityUIElement::x() { CGRect frame = [m_element accessibilityFrame]; return frame.origin.x; } double AccessibilityUIElement::y() { CGRect frame = [m_element accessibilityFrame]; return frame.origin.y; } double AccessibilityUIElement::width() { CGRect frame = [m_element accessibilityFrame]; return frame.size.width; } double AccessibilityUIElement::height() { CGRect frame = [m_element accessibilityFrame]; return frame.size.height; } double AccessibilityUIElement::clickPointX() { CGPoint centerPoint = [m_element accessibilityActivationPoint]; return centerPoint.x; } double AccessibilityUIElement::clickPointY() { CGPoint centerPoint = [m_element accessibilityActivationPoint]; return centerPoint.y; } void AccessibilityUIElement::getChildren(Vector& elementVector) { NSInteger childCount = [m_element accessibilityElementCount]; for (NSInteger k = 0; k < childCount; ++k) elementVector.append(AccessibilityUIElement([m_element accessibilityElementAtIndex:k])); } void AccessibilityUIElement::getChildrenWithRange(Vector& elementVector, unsigned location, unsigned length) { NSUInteger childCount = [m_element accessibilityElementCount]; for (NSUInteger k = location; k < childCount && k < (location+length); ++k) elementVector.append(AccessibilityUIElement([m_element accessibilityElementAtIndex:k])); } int AccessibilityUIElement::childrenCount() { Vector 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 0; } AccessibilityUIElement AccessibilityUIElement::getChildAtIndex(unsigned index) { Vector children; getChildrenWithRange(children, index, 1); if (children.size() == 1) return children[0]; return nil; } AccessibilityUIElement AccessibilityUIElement::headerElementAtIndex(unsigned index) { NSArray *headers = [m_element accessibilityHeaderElements]; if (index < [headers count]) return [headers objectAtIndex:index]; return 0; } AccessibilityUIElement AccessibilityUIElement::linkedElement() { id linkedElement = [m_element accessibilityLinkedElement]; if (linkedElement) return AccessibilityUIElement(linkedElement); return 0; } AccessibilityUIElement AccessibilityUIElement::linkedUIElementAtIndex(unsigned index) { // FIXME: implement return 0; } AccessibilityUIElement AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index) { return 0; } AccessibilityUIElement AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index) { return 0; } AccessibilityUIElement AccessibilityUIElement::disclosedRowAtIndex(unsigned index) { return 0; } AccessibilityUIElement AccessibilityUIElement::selectedRowAtIndex(unsigned index) { return 0; } AccessibilityUIElement AccessibilityUIElement::rowAtIndex(unsigned index) { return 0; } AccessibilityUIElement AccessibilityUIElement::titleUIElement() { return 0; } AccessibilityUIElement AccessibilityUIElement::parentElement() { id accessibilityObject = [m_element accessibilityContainer]; if (accessibilityObject) return AccessibilityUIElement(accessibilityObject); return nil; } AccessibilityUIElement AccessibilityUIElement::disclosedByRow() { return 0; } void AccessibilityUIElement::increaseTextSelection() { [m_element accessibilityModifySelection:WebCore::CharacterGranularity increase:YES]; } void AccessibilityUIElement::decreaseTextSelection() { [m_element accessibilityModifySelection:WebCore::CharacterGranularity increase:NO]; } JSStringRef AccessibilityUIElement::stringForSelection() { NSString *stringForRange = [m_element selectionRangeString]; if (!stringForRange) return 0; return [stringForRange createJSStringRef]; } JSStringRef AccessibilityUIElement::stringForRange(unsigned location, unsigned length) { NSString *stringForRange = [m_element stringForRange:NSMakeRange(location, length)]; if (!stringForRange) return 0; return [stringForRange createJSStringRef]; } JSStringRef AccessibilityUIElement::attributedStringForRange(unsigned, unsigned) { return JSStringCreateWithCharacters(0, 0); } bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned, unsigned) { return false; } void AccessibilityUIElement::elementsForRange(unsigned location, unsigned length, Vector& elements) { NSArray *elementsForRange = [m_element elementsForRange:NSMakeRange(location, length)]; for (id object in elementsForRange) { AccessibilityUIElement element = AccessibilityUIElement(object); elements.append(element); } } #pragma mark Unused void AccessibilityUIElement::getLinkedUIElements(Vector& elementVector) { } void AccessibilityUIElement::getDocumentLinks(Vector& elementVector) { } JSStringRef AccessibilityUIElement::attributesOfLinkedUIElements() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::attributesOfDocumentLinks() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::attributesOfChildren() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::allAttributes() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::stringAttributeValue(JSStringRef attribute) { if (JSStringIsEqualToUTF8CString(attribute, "AXPlaceholderValue")) return [[m_element accessibilityPlaceholderValue] createJSStringRef]; return JSStringCreateWithCharacters(0, 0); } bool AccessibilityUIElement::isPressActionSupported() { return false; } bool AccessibilityUIElement::isIncrementActionSupported() { return false; } bool AccessibilityUIElement::isDecrementActionSupported() { return false; } bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute) { return false; } bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute) { return false; } bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute) { return false; } JSStringRef AccessibilityUIElement::parameterizedAttributeNames() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::role() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::subrole() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::roleDescription() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::title() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::description() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::orientation() const { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::stringValue() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::language() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::helpText() const { return JSStringCreateWithCharacters(0, 0); } double AccessibilityUIElement::intValue() const { return 0.0f; } double AccessibilityUIElement::minValue() { return 0.0f; } double AccessibilityUIElement::maxValue() { return 0.0; } JSStringRef AccessibilityUIElement::valueDescription() { return JSStringCreateWithCharacters(0, 0); } int AccessibilityUIElement::insertionPointLineNumber() { return -1; } bool AccessibilityUIElement::isEnabled() { return false; } bool AccessibilityUIElement::isRequired() const { return false; } bool AccessibilityUIElement::isFocused() const { // FIXME: implement return false; } bool AccessibilityUIElement::isSelected() const { UIAccessibilityTraits traits = [m_element accessibilityTraits]; return (traits & UIAccessibilityTraitSelected); } bool AccessibilityUIElement::isExpanded() const { return false; } bool AccessibilityUIElement::isChecked() const { return false; } int AccessibilityUIElement::hierarchicalLevel() const { return 0; } bool AccessibilityUIElement::ariaIsGrabbed() const { return false; } JSStringRef AccessibilityUIElement::ariaDropEffects() const { return JSStringCreateWithCharacters(0, 0); } int AccessibilityUIElement::lineForIndex(int index) { return -1; } JSStringRef AccessibilityUIElement::boundsForRange(unsigned location, unsigned length) { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::attributesOfColumnHeaders() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::attributesOfRowHeaders() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::attributesOfColumns() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::attributesOfRows() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::attributesOfVisibleCells() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::attributesOfHeader() { return JSStringCreateWithCharacters(0, 0); } int AccessibilityUIElement::rowCount() { return -1; } int AccessibilityUIElement::columnCount() { return -1; } int AccessibilityUIElement::indexInTable() { return -1; } JSStringRef AccessibilityUIElement::rowIndexRange() { NSRange range = [m_element accessibilityRowRange]; NSMutableString* rangeDescription = [NSMutableString stringWithFormat:@"{%lu, %lu}", (unsigned long)range.location, (unsigned long)range.length]; return [rangeDescription createJSStringRef]; } JSStringRef AccessibilityUIElement::columnIndexRange() { NSRange range = [m_element accessibilityColumnRange]; NSMutableString* rangeDescription = [NSMutableString stringWithFormat:@"{%lu, %lu}", (unsigned long)range.location, (unsigned long)range.length]; return [rangeDescription createJSStringRef]; } AccessibilityUIElement AccessibilityUIElement::cellForColumnAndRow(unsigned col, unsigned row) { return AccessibilityUIElement([m_element accessibilityElementForRow:row andColumn:col]); } JSStringRef AccessibilityUIElement::selectedTextRange() { return JSStringCreateWithCharacters(0, 0); } void AccessibilityUIElement::assistiveTechnologySimulatedFocus() { [m_element accessibilityElementDidBecomeFocused]; } void AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length) { } void AccessibilityUIElement::increment() { [m_element accessibilityIncrement]; } void AccessibilityUIElement::decrement() { [m_element accessibilityDecrement]; } void AccessibilityUIElement::showMenu() { } void AccessibilityUIElement::press() { } JSStringRef AccessibilityUIElement::accessibilityValue() const { // FIXME: implement return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::documentEncoding() { return JSStringCreateWithCharacters(0, 0); } JSStringRef AccessibilityUIElement::documentURI() { return JSStringCreateWithCharacters(0, 0); } static void _accessibilityNotificationCallback(id element, NSString* notification, void* context) { if (!context) return; JSObjectRef functionCallback = static_cast(context); JSRetainPtr jsNotification(Adopt, [notification createJSStringRef]); JSValueRef argument = JSValueMakeString([mainFrame globalContext], jsNotification.get()); JSObjectCallAsFunction([mainFrame globalContext], functionCallback, NULL, 1, &argument, NULL); } bool AccessibilityUIElement::addNotificationListener(JSObjectRef functionCallback) { if (!functionCallback) return false; m_notificationFunctionCallback = functionCallback; [platformUIElement() accessibilitySetPostedNotificationCallback:_accessibilityNotificationCallback withContext:reinterpret_cast(m_notificationFunctionCallback)]; return true; } void AccessibilityUIElement::removeNotificationListener() { m_notificationFunctionCallback = 0; [platformUIElement() accessibilitySetPostedNotificationCallback:nil withContext: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::isSelectedOptionActive() 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 { // FIXME: implement return false; } bool AccessibilityUIElement::hasPopup() const { // FIXME: implement return false; } void AccessibilityUIElement::takeFocus() { // FIXME: implement } void AccessibilityUIElement::takeSelection() { // FIXME: implement } void AccessibilityUIElement::addSelection() { // FIXME: implement } void AccessibilityUIElement::removeSelection() { // FIXME: implement } AccessibilityUIElement AccessibilityUIElement::uiElementForSearchPredicate(JSContextRef context, AccessibilityUIElement* startElement, bool isDirectionNext, JSValueRef searchKey, JSStringRef searchText) { // FIXME: implement return 0; } double AccessibilityUIElement::numberAttributeValue(JSStringRef attribute) { // FIXME: implement return 0; } #endif // PLATFORM(IOS)