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',