1/* 2 * Copyright (C) 2011 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 * 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. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#import "config.h" 27#import "WKAccessibilityWebPageObject.h" 28 29#import "WebFrame.h" 30#import "WebPage.h" 31#import "WKArray.h" 32#import "WKNumber.h" 33#import "WKRetainPtr.h" 34#import "WKSharedAPICast.h" 35#import "WKString.h" 36#import "WKStringCF.h" 37#import <WebCore/AXObjectCache.h> 38#import <WebCore/Frame.h> 39#import <WebCore/FrameView.h> 40#import <WebCore/Page.h> 41#import <WebCore/ScrollView.h> 42#import <WebCore/Scrollbar.h> 43#import <WebKitSystemInterface.h> 44#import <wtf/ObjcRuntimeExtras.h> 45 46using namespace WebCore; 47using namespace WebKit; 48 49@implementation WKAccessibilityWebPageObject 50 51- (id)accessibilityRootObjectWrapper 52{ 53 if (!WebCore::AXObjectCache::accessibilityEnabled()) 54 WebCore::AXObjectCache::enableAccessibility(); 55 56 NSObject* mainFramePluginAccessibilityObjectWrapper = m_page->accessibilityObjectForMainFramePlugin(); 57 if (mainFramePluginAccessibilityObjectWrapper) 58 return mainFramePluginAccessibilityObjectWrapper; 59 60 WebCore::Page* page = m_page->corePage(); 61 if (!page) 62 return nil; 63 64 WebCore::Frame* core = page->mainFrame(); 65 if (!core || !core->document()) 66 return nil; 67 68 AccessibilityObject* root = core->document()->axObjectCache()->rootObject(); 69 if (!root) 70 return nil; 71 72 return root->wrapper(); 73} 74 75- (void)setWebPage:(WebPage*)page 76{ 77 m_page = page; 78} 79 80- (void)setRemoteParent:(id)parent 81{ 82 if (parent != m_parent) { 83 [m_parent release]; 84 m_parent = [parent retain]; 85 } 86} 87 88- (void)dealloc 89{ 90 WKUnregisterUniqueIdForElement(self); 91 [m_accessibilityChildren release]; 92 [m_attributeNames release]; 93 [m_parent release]; 94 [super dealloc]; 95} 96 97- (BOOL)accessibilityIsIgnored 98{ 99 return NO; 100} 101 102- (NSArray *)accessibilityAttributeNames 103{ 104 if (!m_attributeNames) 105 m_attributeNames = [[NSArray alloc] initWithObjects: 106 NSAccessibilityRoleAttribute, NSAccessibilityRoleDescriptionAttribute, NSAccessibilityFocusedAttribute, 107 NSAccessibilityParentAttribute, NSAccessibilityWindowAttribute, NSAccessibilityTopLevelUIElementAttribute, 108 NSAccessibilityPositionAttribute, NSAccessibilitySizeAttribute, NSAccessibilityChildrenAttribute, nil]; 109 110 return m_attributeNames; 111} 112 113- (NSArray *)accessibilityParameterizedAttributeNames 114{ 115 WKRetainPtr<WKArrayRef> result = adoptWK(m_page->pageOverlayCopyAccessibilityAttributesNames(true)); 116 if (!result) 117 return nil; 118 119 NSMutableArray *names = [NSMutableArray array]; 120 size_t count = WKArrayGetSize(result.get()); 121 for (size_t k = 0; k < count; k++) { 122 WKTypeRef item = WKArrayGetItemAtIndex(result.get(), k); 123 if (toImpl(item)->type() == WKStringGetTypeID()) { 124 RetainPtr<CFStringRef> name = adoptCF(WKStringCopyCFString(kCFAllocatorDefault, (WKStringRef)item)); 125 [names addObject:(NSString *)name.get()]; 126 } 127 } 128 129 return names; 130} 131 132- (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute 133{ 134 return NO; 135} 136 137- (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute 138{ 139 return; 140} 141 142- (NSArray *)accessibilityActionNames 143{ 144 return [NSArray array]; 145} 146 147- (NSArray *)accessibilityChildren 148{ 149 id wrapper = [self accessibilityRootObjectWrapper]; 150 if (!wrapper) 151 return [NSArray array]; 152 153 return [NSArray arrayWithObject:wrapper]; 154} 155 156- (id)accessibilityAttributeValue:(NSString *)attribute 157{ 158 if (!WebCore::AXObjectCache::accessibilityEnabled()) 159 WebCore::AXObjectCache::enableAccessibility(); 160 161 if ([attribute isEqualToString:NSAccessibilityParentAttribute]) 162 return m_parent; 163 if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) 164 return [m_parent accessibilityAttributeValue:NSAccessibilityWindowAttribute]; 165 if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) 166 return [m_parent accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute]; 167 if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) 168 return NSAccessibilityGroupRole; 169 if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) 170 return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil); 171 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) 172 return [NSNumber numberWithBool:NO]; 173 174 if (!m_page) 175 return nil; 176 177 if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) { 178 const WebCore::FloatPoint& point = m_page->accessibilityPosition(); 179 return [NSValue valueWithPoint:NSMakePoint(point.x(), point.y())]; 180 } 181 if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) { 182 const IntSize& s = m_page->size(); 183 return [NSValue valueWithSize:NSMakeSize(s.width(), s.height())]; 184 } 185 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) 186 return [self accessibilityChildren]; 187 188 return nil; 189} 190 191- (NSPoint)_convertScreenPointToWindow:(NSPoint)point 192{ 193 return m_page->screenToWindow(IntPoint(point.x, point.y)); 194} 195 196- (id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter 197{ 198 WKRetainPtr<WKTypeRef> pageOverlayParameter = 0; 199 200 if ([parameter isKindOfClass:[NSValue class]] && strcmp([(NSValue*)parameter objCType], @encode(NSPoint)) == 0) { 201 NSPoint point = [self _convertScreenPointToWindow:[(NSValue *)parameter pointValue]]; 202 pageOverlayParameter = WKPointCreate(WKPointMake(point.x, point.y)); 203 } 204 205 WKRetainPtr<WKStringRef> attributeRef = adoptWK(WKStringCreateWithCFString((CFStringRef)attribute)); 206 WKRetainPtr<WKTypeRef> result = adoptWK(m_page->pageOverlayCopyAccessibilityAttributeValue(attributeRef.get(), pageOverlayParameter.get())); 207 if (!result) 208 return nil; 209 210 if (toImpl(result.get())->type() == WKStringGetTypeID()) 211 return HardAutorelease(WKStringCopyCFString(kCFAllocatorDefault, (WKStringRef)result.get())); 212 else if (toImpl(result.get())->type() == WKBooleanGetTypeID()) 213 return [NSNumber numberWithBool:WKBooleanGetValue(static_cast<WKBooleanRef>(result.get()))]; 214 215 return nil; 216} 217 218- (BOOL)accessibilityShouldUseUniqueId 219{ 220 return YES; 221} 222 223- (id)accessibilityHitTest:(NSPoint)point 224{ 225 // Hit-test point comes in as bottom-screen coordinates. Needs to be normalized to the frame of the web page. 226 NSPoint remotePosition = [[self accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue]; 227 NSSize remoteSize = [[self accessibilityAttributeValue:NSAccessibilitySizeAttribute] sizeValue]; 228 229 // Get the y position of the WKView (we have to screen-flip and go from bottom left to top left). 230 CGFloat screenHeight = [(NSScreen *)[[NSScreen screens] objectAtIndex:0] frame].size.height; 231 remotePosition.y = (screenHeight - remotePosition.y) - remoteSize.height; 232 233 point.y = screenHeight - point.y; 234 235 // Re-center point into the web page's frame. 236 point.y -= remotePosition.y; 237 point.x -= remotePosition.x; 238 239 WebCore::FrameView* frameView = m_page ? m_page->mainFrameView() : 0; 240 if (frameView) { 241 point.y += frameView->scrollPosition().y(); 242 point.x += frameView->scrollPosition().x(); 243 } 244 245 return [[self accessibilityRootObjectWrapper] accessibilityHitTest:point]; 246} 247 248- (id)accessibilityFocusedUIElement 249{ 250 return [[self accessibilityRootObjectWrapper] accessibilityFocusedUIElement]; 251} 252 253 254@end 255