First semi-successful attempt at mac accessibility.
Right now the accessibility tree is shipped over at page load, so this doesn't account for any changes to the html, or scrolling/resizing.
BUG=27112
TEST=Use accessibility inspector to inspect various elements of the webpage.
Review URL: http://codereview.chromium.org/2642001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51782 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/cocoa/browser_accessibility.h b/chrome/browser/cocoa/browser_accessibility.h
new file mode 100644
index 0000000..f891327
--- /dev/null
+++ b/chrome/browser/cocoa/browser_accessibility.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_COCOA_BROWSER_ACCESSIBILITY_H
+#define CHROME_BROWSER_COCOA_BROWSER_ACCESSIBILITY_H
+
+#import <Cocoa/Cocoa.h>
+
+#import "base/scoped_nsobject.h"
+#include "chrome/browser/cocoa/browser_accessibility_delegate.h"
+#include "webkit/glue/webaccessibility.h"
+
+using webkit_glue::WebAccessibility;
+
+// BrowserAccessibility is a cocoa wrapper around the WebAccessibility
+// object. The renderer converts webkit's accessibility tree into a
+// WebAccessibility tree and passes it to us over IPC. This class
+// converts it into a format Cocoa can query.
+@interface BrowserAccessibility : NSObject {
+ @private
+ WebAccessibility webAccessibility_;
+ id<BrowserAccessibilityDelegate> delegate_;
+ scoped_nsobject<NSMutableArray> children_;
+ // The parent of the accessibility object. This can be another
+ // BrowserAccessibility or a RenderWidgetHostViewCocoa.
+ id parent_;
+}
+
+- (id)initWithObject:(const WebAccessibility)accessibility
+ delegate:(id<BrowserAccessibilityDelegate>)delegate
+ parent:(id)parent;
+
+@property(nonatomic, readonly) NSArray* children;
+@property(nonatomic, readonly, getter=isIgnored) BOOL ignored;
+@property(nonatomic, readonly) NSPoint origin;
+@property(nonatomic, readonly) NSString* role;
+@property(nonatomic, readonly) NSSize size;
+
+@end
+
+#endif // CHROME_BROWSER_COCOA_BROWSER_ACCESSIBILITY_H
diff --git a/chrome/browser/cocoa/browser_accessibility.mm b/chrome/browser/cocoa/browser_accessibility.mm
new file mode 100644
index 0000000..6dbf3ce
--- /dev/null
+++ b/chrome/browser/cocoa/browser_accessibility.mm
@@ -0,0 +1,297 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <execinfo.h>
+
+#include "base/string16.h"
+#include "base/sys_string_conversions.h"
+#include "chrome/browser/cocoa/browser_accessibility.h"
+#include "chrome/browser/renderer_host/render_widget_host_view_mac.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRect.h"
+
+using webkit_glue::WebAccessibility;
+
+namespace {
+
+// Returns an autoreleased copy of the WebAccessibility's attribute.
+NSString* NSStringForWebAccessibilityAttribute(
+ std::map<int32, string16>& attributes,
+ WebAccessibility::Attribute attribute) {
+ std::map<int32, string16>::iterator iter =
+ attributes.find(attribute);
+ NSString* returnValue = @"";
+ if (iter != attributes.end()) {
+ returnValue = base::SysUTF16ToNSString(iter->second);
+ }
+ return returnValue;
+}
+
+struct RoleEntry {
+ WebAccessibility::Role value;
+ NSString* string;
+};
+
+static const RoleEntry roles[] = {
+ { WebAccessibility::ROLE_NONE, NSAccessibilityUnknownRole },
+ { WebAccessibility::ROLE_BUTTON, NSAccessibilityButtonRole },
+ { WebAccessibility::ROLE_RADIO_BUTTON, NSAccessibilityRadioButtonRole },
+ { WebAccessibility::ROLE_CHECKBOX, NSAccessibilityCheckBoxRole },
+ { WebAccessibility::ROLE_STATIC_TEXT, NSAccessibilityStaticTextRole},
+ { WebAccessibility::ROLE_IMAGE, NSAccessibilityImageRole},
+ { WebAccessibility::ROLE_TEXT_FIELD, NSAccessibilityTextFieldRole},
+ { WebAccessibility::ROLE_TEXTAREA, NSAccessibilityTextAreaRole},
+ { WebAccessibility::ROLE_LINK, NSAccessibilityLinkRole},
+ { WebAccessibility::ROLE_SCROLLAREA, NSAccessibilityScrollAreaRole},
+ { WebAccessibility::ROLE_SCROLLBAR, NSAccessibilityScrollBarRole},
+ { WebAccessibility::ROLE_RADIO_GROUP, NSAccessibilityRadioGroupRole},
+ { WebAccessibility::ROLE_TABLE, NSAccessibilityTableRole},
+ { WebAccessibility::ROLE_TAB_GROUP, NSAccessibilityTabGroupRole},
+ { WebAccessibility::ROLE_IGNORED, NSAccessibilityUnknownRole},
+ { WebAccessibility::ROLE_WEB_AREA, @"AXWebArea"},
+ { WebAccessibility::ROLE_GROUP, NSAccessibilityGroupRole},
+ { WebAccessibility::ROLE_GRID, NSAccessibilityGridRole},
+ { WebAccessibility::ROLE_WEBCORE_LINK, NSAccessibilityLinkRole},
+};
+
+bool GetState(WebAccessibility accessibility, int state) {
+ return ((accessibility.state >> state) & 1);
+}
+
+} // anonymous namespace
+
+@implementation BrowserAccessibility
+
+- (id)initWithObject:(const WebAccessibility)accessibility
+ delegate:(id<BrowserAccessibilityDelegate>)delegate
+ parent:(id)parent {
+ if ((self = [super init])) {
+ webAccessibility_ = accessibility;
+ parent_ = parent;
+ delegate_ = delegate;
+ }
+ return self;
+}
+
+- (NSArray*)children {
+ if (!children_.get()) {
+ const std::vector<WebAccessibility>& accessibilityChildren =
+ webAccessibility_.children;
+ children_.reset(
+ [[NSMutableArray alloc]
+ initWithCapacity:accessibilityChildren.size()]);
+ std::vector<WebAccessibility>::const_iterator iterator;
+ for (iterator = accessibilityChildren.begin();
+ iterator != accessibilityChildren.end();
+ iterator++) {
+ BrowserAccessibility* child =
+ [[BrowserAccessibility alloc]
+ initWithObject:*iterator
+ delegate:delegate_
+ parent:self];
+ [child autorelease];
+ [children_ addObject:child];
+ }
+ }
+ return children_;
+}
+
+- (BOOL)isIgnored {
+ return webAccessibility_.role == WebAccessibility::ROLE_IGNORED;
+}
+
+- (NSPoint)origin {
+ return NSMakePoint(webAccessibility_.location.x,
+ webAccessibility_.location.y);
+}
+
+- (NSString*)role {
+ NSString* role = NSAccessibilityUnknownRole;
+ WebAccessibility::Role value = webAccessibility_.role;
+ const size_t numRoles = sizeof(roles) / sizeof(roles[0]);
+ for (size_t i = 0; i < numRoles; ++i) {
+ if (roles[i].value == value) {
+ role = roles[i].string;
+ break;
+ }
+ }
+ return role;
+}
+
+- (NSSize)size {
+ return NSMakeSize(webAccessibility_.location.width,
+ webAccessibility_.location.height);
+}
+
+- (id)accessibilityAttributeValue:(NSString *)attribute {
+ if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
+ return [self role];
+ } else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) {
+ return NSStringForWebAccessibilityAttribute(webAccessibility_.attributes,
+ WebAccessibility::ATTR_DESCRIPTION);
+ } else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
+ return [NSValue valueWithPoint:[delegate_ accessibilityPointInScreen:self]];
+ } else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
+ return [NSValue valueWithSize:[self size]];
+ } else if (
+ [attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute] ||
+ [attribute isEqualToString:NSAccessibilityWindowAttribute]) {
+ return [delegate_ window];
+ } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
+ return [self children];
+ } else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
+ if (parent_) {
+ return NSAccessibilityUnignoredAncestor(parent_);
+ }
+ } else if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) {
+ return base::SysUTF16ToNSString(webAccessibility_.name);
+ } else if ([attribute isEqualToString:NSAccessibilityHelpAttribute]) {
+ return NSStringForWebAccessibilityAttribute(webAccessibility_.attributes,
+ WebAccessibility::ATTR_HELP);
+ } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
+ return base::SysUTF16ToNSString(webAccessibility_.value);
+ } else if (
+ [attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
+ return NSAccessibilityRoleDescription([self role], nil);
+ } else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
+ NSNumber* ret = [NSNumber numberWithBool:
+ GetState(webAccessibility_, WebAccessibility::STATE_FOCUSED)];
+ return ret;
+ } else if ([attribute isEqualToString:NSAccessibilityEnabledAttribute] ||
+ [attribute isEqualToString:@"AXVisited"] ||
+ [attribute isEqualToString:@"AXLoaded"]) {
+ return [NSNumber numberWithBool:YES];
+ }
+ return nil;
+}
+
+- (NSArray *)accessibilityActionNames {
+ return [NSArray arrayWithObjects:
+ NSAccessibilityPressAction, NSAccessibilityShowMenuAction, nil];
+}
+
+- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute
+ index:(NSUInteger)index
+ maxCount:(NSUInteger)maxCount {
+ NSArray* fullArray = [self accessibilityAttributeValue:attribute];
+ if (!fullArray)
+ return nil;
+ NSUInteger arrayCount = [fullArray count];
+ if (index >= arrayCount)
+ return nil;
+ NSRange subRange;
+ if ((index + maxCount) > arrayCount) {
+ subRange = NSMakeRange(index, arrayCount - index);
+ } else {
+ subRange = NSMakeRange(index, maxCount);
+ }
+ return [fullArray subarrayWithRange:subRange];
+}
+
+- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute {
+ NSArray* fullArray = [self accessibilityAttributeValue:attribute];
+ return [fullArray count];
+}
+
+- (NSArray *)accessibilityAttributeNames {
+ return [NSArray arrayWithObjects:
+ NSAccessibilityRoleAttribute,
+ NSAccessibilityRoleDescriptionAttribute,
+ NSAccessibilityChildrenAttribute,
+ NSAccessibilityHelpAttribute,
+ NSAccessibilityParentAttribute,
+ NSAccessibilityPositionAttribute,
+ NSAccessibilitySizeAttribute,
+ NSAccessibilityTitleAttribute,
+ NSAccessibilityDescriptionAttribute,
+ NSAccessibilityValueAttribute,
+ NSAccessibilityFocusedAttribute,
+ NSAccessibilityEnabledAttribute,
+ NSAccessibilityWindowAttribute,
+ NSAccessibilityTopLevelUIElementAttribute,
+ nil];
+}
+
+- (id)accessibilityFocusedUIElement {
+ return self;
+}
+
+- (NSUInteger)accessibilityIndexOfChild:(id)child {
+ NSUInteger index = 0;
+ for (BrowserAccessibility* childToCheck in [self children]) {
+ if ([child isEqual:childToCheck])
+ return index;
+ if (![childToCheck isIgnored])
+ ++index;
+ }
+ return NSNotFound;
+}
+
+- (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
+ if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
+ return GetState(webAccessibility_, WebAccessibility::STATE_FOCUSABLE);
+ } else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
+ return !GetState(webAccessibility_, WebAccessibility::STATE_READONLY);
+ }
+ return NO;
+}
+
+- (BOOL)accessibilityIsIgnored {
+ return [self isIgnored];
+}
+
+- (void)accessibilityPerformAction:(NSString *)action {
+ // TODO(feldstein): Support more actions.
+ [delegate_ doDefaultAction:webAccessibility_.id];
+}
+
+- (NSString*)accessibilityActionDescription:(NSString*)action {
+ return NSAccessibilityActionDescription(action);
+}
+
+- (BOOL)accessibilitySetOverrideValue:(id)value
+ forAttribute:(NSString *)attribute {
+ return NO;
+}
+
+- (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
+ if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
+ NSNumber* focusedNumber = value;
+ BOOL focused = [focusedNumber intValue];
+ [delegate_ setAccessibilityFocus:focused
+ accessibilityId:webAccessibility_.id];
+ }
+}
+
+- (id)accessibilityHitTest:(NSPoint)point {
+ id hit = self;
+ for (id child in [self children]) {
+ NSPoint origin = [child origin];
+ NSSize size = [child size];
+ NSRect rect;
+ rect.origin = origin;
+ rect.size = size;
+ if (NSPointInRect(point, rect)) {
+ hit = child;
+ id childResult = [child accessibilityHitTest:point];
+ if (![childResult accessibilityIsIgnored]) {
+ hit = childResult;
+ break;
+ }
+ }
+ }
+ return NSAccessibilityUnignoredAncestor(hit);
+}
+
+- (BOOL)isEqual:(id)object {
+ if (![object isKindOfClass:[BrowserAccessibility class]])
+ return NO;
+ return ([self hash] == [object hash]);
+}
+
+- (NSUInteger)hash {
+ return webAccessibility_.id;
+}
+
+@end
+
diff --git a/chrome/browser/cocoa/browser_accessibility_delegate.h b/chrome/browser/cocoa/browser_accessibility_delegate.h
new file mode 100644
index 0000000..988df266
--- /dev/null
+++ b/chrome/browser/cocoa/browser_accessibility_delegate.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_COCOA_BROWSER_ACCESSIBILITY_DELEGATE_H
+#define CHROME_BROWSER_COCOA_BROWSER_ACCESSIBILITY_DELEGATE_H
+
+@class BrowserAccessibility;
+@class NSWindow;
+
+@protocol BrowserAccessibilityDelegate
+- (NSPoint)accessibilityPointInScreen:(BrowserAccessibility*)accessibility;
+- (void)doDefaultAction:(int32)accessibilityObjectId;
+- (void)setAccessibilityFocus:(BOOL)focus
+ accessibilityId:(int32)accessibilityObjectId;
+- (NSWindow*)window;
+@end
+
+#endif // CHROME_BROWSER_COCOA_BROWSER_ACCESSIBILITY_DELEGATE_H
diff --git a/chrome/browser/cocoa/browser_accessibility_unittest.mm b/chrome/browser/cocoa/browser_accessibility_unittest.mm
new file mode 100644
index 0000000..2fa93e0e
--- /dev/null
+++ b/chrome/browser/cocoa/browser_accessibility_unittest.mm
@@ -0,0 +1,90 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/string_util.h"
+#include "chrome/browser/cocoa/browser_accessibility.h"
+#include "chrome/browser/cocoa/cocoa_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+@interface MockAccessibilityDelegate : NSObject<BrowserAccessibilityDelegate>
+
+- (NSPoint)accessibilityPointInScreen:(BrowserAccessibility*)accessibility;
+- (void)doDefaultAction:(int32)accessibilityObjectId;
+- (void)setAccessibilityFocus:(BOOL)focus
+ accessibilityId:(int32)accessibilityObjectId;
+- (NSWindow*)window;
+
+@end
+
+@implementation MockAccessibilityDelegate
+
+- (NSPoint)accessibilityPointInScreen:(BrowserAccessibility*)accessibility {
+ return NSZeroPoint;
+}
+- (void)doDefaultAction:(int32)accessibilityObjectId {
+}
+- (void)setAccessibilityFocus:(BOOL)focus
+ accessibilityId:(int32)accessibilityObjectId {
+}
+- (NSWindow*)window {
+ return nil;
+}
+
+@end
+
+
+class BrowserAccessibilityTest : public CocoaTest {
+ public:
+ virtual void SetUp() {
+ CocoaTest::SetUp();
+ WebAccessibility root;
+ root.location.x = 0;
+ root.location.y = 0;
+ root.location.width = 500;
+ root.location.height = 100;
+ root.attributes[WebAccessibility::ATTR_HELP] = ASCIIToUTF16("HelpText");
+
+ WebAccessibility child1;
+ child1.name = ASCIIToUTF16("Child1");
+ child1.location.x = 0;
+ child1.location.y = 0;
+ child1.location.width = 250;
+ child1.location.height = 100;
+
+ WebAccessibility child2;
+ child2.location.x = 250;
+ child2.location.y = 0;
+ child2.location.width = 250;
+ child2.location.height = 100;
+
+ root.children.push_back(child1);
+ root.children.push_back(child2);
+
+ delegate_.reset([[MockAccessibilityDelegate alloc] init]);
+ accessibility_.reset(
+ [[BrowserAccessibility alloc] initWithObject:root
+ delegate:delegate_
+ parent:delegate_]);
+ }
+
+ protected:
+ scoped_nsobject<MockAccessibilityDelegate> delegate_;
+ scoped_nsobject<BrowserAccessibility> accessibility_;
+};
+
+TEST_F(BrowserAccessibilityTest, HitTestTest) {
+ BrowserAccessibility* firstChild =
+ [accessibility_ accessibilityHitTest:NSMakePoint(50, 50)];
+ EXPECT_TRUE(
+ [[firstChild accessibilityAttributeValue:NSAccessibilityTitleAttribute]
+ isEqualToString:@"Child1"]);
+}
+
+TEST_F(BrowserAccessibilityTest, BasicAttributeTest) {
+ NSString* helpText = [accessibility_
+ accessibilityAttributeValue:NSAccessibilityHelpAttribute];
+ EXPECT_TRUE([helpText isEqualToString: @"HelpText"]);
+}
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.h b/chrome/browser/renderer_host/render_widget_host_view_mac.h
index de80e22..44d6951 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_mac.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_mac.h
@@ -13,6 +13,8 @@
#include "base/task.h"
#include "base/time.h"
#include "chrome/browser/cocoa/base_view.h"
+#include "chrome/browser/cocoa/browser_accessibility.h"
+#include "chrome/browser/cocoa/browser_accessibility_delegate.h"
#include "chrome/browser/renderer_host/accelerated_surface_container_manager_mac.h"
#include "chrome/browser/renderer_host/render_widget_host_view.h"
#include "third_party/WebKit/WebKit/chromium/public/WebCompositionUnderline.h"
@@ -33,12 +35,19 @@
// when it's removed from the view system.
// See http://crbug.com/47890 for why we don't use NSTextInputClient yet.
@interface RenderWidgetHostViewCocoa
- : BaseView <RenderWidgetHostViewMacOwner, NSTextInput, NSChangeSpelling> {
+ : BaseView <RenderWidgetHostViewMacOwner,
+ NSTextInput,
+ NSChangeSpelling,
+ BrowserAccessibilityDelegate> {
@private
scoped_ptr<RenderWidgetHostViewMac> renderWidgetHostView_;
BOOL canBeKeyView_;
BOOL closeOnDeactivate_;
+ BOOL rendererAccessible_;
+ BOOL accessibilityRequested_;
+ BOOL accessibilityReceived_;
scoped_ptr<RWHVMEditCommandHelper> editCommand_helper_;
+ scoped_nsobject<NSArray> accessibilityChildren_;
// These are part of the magic tooltip code from WebKit's WebHTMLView:
id trackingRectOwner_; // (not retained)
@@ -127,6 +136,8 @@
- (void)renderWidgetHostWasResized;
// Cancel ongoing composition (abandon the marked text).
- (void)cancelComposition;
+// Set the new accessibility tree.
+- (void)setAccessibilityTree:(const webkit_glue::WebAccessibility&) tree;
// Confirm ongoing composition.
- (void)confirmComposition;
@@ -201,7 +212,10 @@
virtual void WindowFrameChanged();
virtual void SetBackground(const SkBitmap& background);
virtual bool ContainsNativeView(gfx::NativeView native_view) const;
-
+ virtual void UpdateAccessibilityTree(
+ const webkit_glue::WebAccessibility& tree);
+ virtual void OnAccessibilityFocusChange(int acc_obj_id);
+ virtual void OnAccessibilityObjectStateChange(int acc_obj_id);
// Methods associated with GPU-accelerated plug-in instances.
virtual gfx::PluginWindowHandle AllocateFakePluginWindowHandle(bool opaque);
virtual void DestroyFakePluginWindowHandle(gfx::PluginWindowHandle window);
@@ -302,6 +316,9 @@
// Helper class for managing instances of accelerated plug-ins.
AcceleratedSurfaceContainerManagerMac plugin_container_manager_;
+ // Whether or not web accessibility is enabled.
+ bool renderer_accessible_;
+
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewMac);
};
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.mm b/chrome/browser/renderer_host/render_widget_host_view_mac.mm
index 1801f38..7dd47d4 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm
@@ -21,6 +21,7 @@
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/renderer_host/render_widget_host.h"
#include "chrome/browser/spellchecker_platform_engine.h"
+#include "chrome/common/chrome_switches.h"
#include "chrome/common/native_web_keyboard_event.h"
#include "chrome/common/edit_command.h"
#include "chrome/common/plugin_messages.h"
@@ -209,6 +210,9 @@
cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc]
initWithRenderWidgetHostViewMac:this] autorelease];
render_widget_host_->set_view(this);
+
+ renderer_accessible_ = !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableRendererAccessibility);
}
RenderWidgetHostViewMac::~RenderWidgetHostViewMac() {
@@ -702,8 +706,8 @@
// See if this is a tab drag window. The width check is to distinguish that
// case from extension popup windows.
NSWindow* ancestor_window = [enclosing_window parentWindow];
- if (ancestor_window && ([enclosing_window frame].size.width ==
- [ancestor_window frame].size.width)) {
+ if (ancestor_window && (NSWidth([enclosing_window frame]) ==
+ NSWidth([ancestor_window frame]))) {
enclosing_window = ancestor_window;
}
@@ -775,6 +779,21 @@
return false;
}
+void RenderWidgetHostViewMac::UpdateAccessibilityTree(
+ const webkit_glue::WebAccessibility& tree) {
+ if (renderer_accessible_) {
+ [cocoa_view_ setAccessibilityTree:tree];
+ }
+}
+
+void RenderWidgetHostViewMac::OnAccessibilityFocusChange(int acc_obj_id) {
+ NOTIMPLEMENTED();
+}
+
+void RenderWidgetHostViewMac::OnAccessibilityObjectStateChange(int acc_obj_id) {
+ NOTIMPLEMENTED();
+}
+
void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
if (active) {
if (text_input_type_ == WebKit::WebTextInputTypePassword)
@@ -851,13 +870,19 @@
- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
self = [super initWithFrame:NSZeroRect];
- if (self != nil) {
+ if (self) {
editCommand_helper_.reset(new RWHVMEditCommandHelper);
editCommand_helper_->AddEditingSelectorsToClass([self class]);
renderWidgetHostView_.reset(r);
canBeKeyView_ = YES;
closeOnDeactivate_ = NO;
+
+ rendererAccessible_ =
+ !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableRendererAccessibility);
+ accessibilityRequested_ = NO;
+ accessibilityReceived_ = NO;
}
return self;
}
@@ -1345,6 +1370,102 @@
return ([event type] == NSKeyDown) ? YES : NO;
}
+// Create the BrowserAccessibility tree from the WebAccessibility tree passed
+// from the renderer.
+- (void)setAccessibilityTree:(const webkit_glue::WebAccessibility&) tree {
+ BrowserAccessibility* root =
+ [[BrowserAccessibility alloc] initWithObject:tree
+ delegate:self
+ parent:self];
+ [root autorelease];
+ accessibilityChildren_.reset([[NSArray alloc] initWithObjects:root, nil]);
+ accessibilityReceived_ = YES;
+}
+
+- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute
+ index:(NSUInteger)index
+ maxCount:(NSUInteger)maxCount {
+ NSArray* fullArray = [self accessibilityAttributeValue:attribute];
+ NSUInteger totalLength = [fullArray count];
+ if (index >= totalLength)
+ return nil;
+ NSUInteger length = MIN(totalLength - index, maxCount);
+ return [fullArray subarrayWithRange:NSMakeRange(index, length)];
+}
+
+- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute {
+ NSArray* fullArray = [self accessibilityAttributeValue:attribute];
+ return [fullArray count];
+}
+
+- (id)accessibilityAttributeValue:(NSString *)attribute {
+ if (!accessibilityRequested_) {
+ renderWidgetHostView_->render_widget_host_->EnableRendererAccessibility();
+ accessibilityRequested_ = YES;
+ }
+ if (accessibilityReceived_) {
+ if (rendererAccessible_ &&
+ [attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
+ return accessibilityChildren_.get();
+ } else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
+ return NSAccessibilityScrollAreaRole;
+ }
+ }
+ id ret = [super accessibilityAttributeValue:attribute];
+ return ret;
+}
+
+- (id)accessibilityHitTest:(NSPoint)point {
+ if (!accessibilityRequested_) {
+ renderWidgetHostView_->render_widget_host_->EnableRendererAccessibility();
+ accessibilityRequested_ = YES;
+ }
+ if (!accessibilityReceived_) {
+ return self;
+ }
+ NSPoint pointInWindow = [[self window] convertScreenToBase:point];
+ NSPoint localPoint = [self convertPoint:pointInWindow fromView:nil];
+ localPoint.y = NSHeight([self bounds]) - localPoint.y;
+ if ([accessibilityChildren_ count] == 0)
+ return self;
+ BrowserAccessibility* root = [accessibilityChildren_ objectAtIndex:0];
+ id obj = [root accessibilityHitTest:localPoint];
+ return obj;
+}
+
+- (BOOL)accessibilityIsIgnored {
+ return NO;
+}
+
+- (NSUInteger)accessibilityIndexOfChild:(id)child {
+ return [accessibilityChildren_ indexOfObject:child];
+}
+
+- (void)doDefaultAction:(int32)accessibilityObjectId {
+ renderWidgetHostView_->render_widget_host_->
+ AccessibilityDoDefaultAction(accessibilityObjectId);
+}
+
+// Convert a web accessibility's location in web coordinates into a cocoa
+// screen coordinate.
+- (NSPoint)accessibilityPointInScreen:(BrowserAccessibility*)accessibility {
+ NSPoint origin = [accessibility origin];
+ NSSize size = [accessibility size];
+ origin.y = NSHeight([self bounds]) - origin.y;
+ NSPoint originInWindow = [self convertPoint:origin toView:nil];
+ NSPoint originInScreen = [[self window] convertBaseToScreen:originInWindow];
+ originInScreen.y = originInScreen.y - size.height;
+ return originInScreen;
+}
+
+- (void)setAccessibilityFocus:(BOOL)focus
+ accessibilityId:(int32)accessibilityObjectId {
+ if (focus) {
+ renderWidgetHostView_->render_widget_host_->
+ SetAccessibilityFocus(accessibilityObjectId);
+ }
+}
+
// Spellchecking methods
// The next three methods are implemented here since this class is the first
// responder for anything in the browser.
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 7886e49c..bdbad710 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -600,6 +600,9 @@
'browser/cocoa/base_bubble_controller.mm',
'browser/cocoa/base_view.h',
'browser/cocoa/base_view.mm',
+ 'browser/cocoa/browser_accessibility.h',
+ 'browser/cocoa/browser_accessibility.mm',
+ 'browser/cocoa/browser_accessibility_delegate.h',
'browser/cocoa/browser_window_factory.mm',
'browser/cocoa/bookmark_all_tabs_controller.h',
'browser/cocoa/bookmark_all_tabs_controller.mm',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index a6824d0..e5d9f8e 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -675,6 +675,7 @@
'browser/cocoa/bookmark_model_observer_for_cocoa_unittest.mm',
'browser/cocoa/bookmark_name_folder_controller_unittest.mm',
'browser/cocoa/bookmark_tree_browser_cell_unittest.mm',
+ 'browser/cocoa/browser_accessibility_unittest.mm',
'browser/cocoa/browser_frame_view_unittest.mm',
'browser/cocoa/browser_window_cocoa_unittest.mm',
'browser/cocoa/browser_window_controller_unittest.mm',