diff options
author | dtseng@chromium.org <dtseng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-19 21:18:50 +0000 |
---|---|---|
committer | dtseng@chromium.org <dtseng@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-19 21:18:50 +0000 |
commit | 0d7dad6df2d9d6d9676ca7adb02e3c92beaa8a79 (patch) | |
tree | f646a38a4c8a43acdfea7addaf660bea0a02cbcc /chrome/browser/accessibility | |
parent | 027db59033125d49ab7cbf4a553e6be7c83b0960 (diff) | |
download | chromium_src-0d7dad6df2d9d6d9676ca7adb02e3c92beaa8a79.zip chromium_src-0d7dad6df2d9d6d9676ca7adb02e3c92beaa8a79.tar.gz chromium_src-0d7dad6df2d9d6d9676ca7adb02e3c92beaa8a79.tar.bz2 |
Make the BrowserAccessibilityManagerMac own the management of the cocoa ax tree.
BUG=55657
TEST=passing unit tests.
Review URL: http://codereview.chromium.org/3826002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@63110 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/accessibility')
8 files changed, 619 insertions, 60 deletions
diff --git a/chrome/browser/accessibility/browser_accessibility.h b/chrome/browser/accessibility/browser_accessibility.h index 62cffb5..6e6b8fb 100644 --- a/chrome/browser/accessibility/browser_accessibility.h +++ b/chrome/browser/accessibility/browser_accessibility.h @@ -15,8 +15,8 @@ #include "webkit/glue/webaccessibility.h" class BrowserAccessibilityManager; -#if defined(OS_MACOSX) -class BrowserAccessibilityMac; +#if defined(OS_MACOSX) && __OBJC__ +@class BrowserAccessibilityCocoa; #elif defined(OS_WIN) class BrowserAccessibilityWin; #endif @@ -91,16 +91,26 @@ class BrowserAccessibility { BrowserAccessibility* new_acc); // Accessors + const std::map<int32, string16>& attributes() const { return attributes_; } int32 child_id() const { return child_id_; } const std::vector<BrowserAccessibility*>& children() const { return children_; } - int32 renderer_id() const { return renderer_id_; } + const std::vector<std::pair<string16, string16> >& html_attributes() const { + return html_attributes_; + } int32 index_in_parent() const { return index_in_parent_; } WebKit::WebRect location() const { return location_; } + BrowserAccessibilityManager* manager() const { return manager_; } + const string16& name() const { return name_; } + int32 renderer_id() const { return renderer_id_; } + int32 role() const { return role_; } + const string16& role_name() const { return role_name_; } + int32 state() const { return state_; } + const string16& value() const { return value_; } -#if defined(OS_MACOSX) - BrowserAccessibilityMac* toBrowserAccessibilityMac(); +#if defined(OS_MACOSX) && __OBJC__ + BrowserAccessibilityCocoa* toBrowserAccessibilityCocoa(); #elif defined(OS_WIN) BrowserAccessibilityWin* toBrowserAccessibilityWin(); #endif diff --git a/chrome/browser/accessibility/browser_accessibility_cocoa.h b/chrome/browser/accessibility/browser_accessibility_cocoa.h new file mode 100644 index 0000000..170c713 --- /dev/null +++ b/chrome/browser/accessibility/browser_accessibility_cocoa.h @@ -0,0 +1,52 @@ +// 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_ACCESSIBILITY_BROWSER_ACCESSIBILITY_COCOA_H_ +#define CHROME_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_COCOA_H_ +#pragma once + +#import <Cocoa/Cocoa.h> + +#import "base/scoped_nsobject.h" +#import "chrome/browser/accessibility/browser_accessibility_delegate_mac.h" +#include "chrome/browser/accessibility/browser_accessibility.h" + +// BrowserAccessibilityCocoa is a cocoa wrapper around the BrowserAccessibility +// object. The renderer converts webkit's accessibility tree into a +// WebAccessibility tree and passes it to the browser process over IPC. +// This class converts it into a format Cocoa can query. +@interface BrowserAccessibilityCocoa : NSObject { + @private + BrowserAccessibility* browserAccessibility_; + id<BrowserAccessibilityDelegateCocoa> delegate_; +} + +// This creates a cocoa browser accessibility object around +// the cross platform BrowserAccessibility object. The delegate is +// used to communicate with the host renderer. None of these +// parameters can be null. +- (id)initWithObject:(BrowserAccessibility*)accessibility + delegate:(id<BrowserAccessibilityDelegateCocoa>)delegate; + +// Backing source of accessibility info. +@property(nonatomic, assign) BrowserAccessibility* browserAccessibility; +// Children is an array of BrowserAccessibility objects, representing +// the accessibility children of this object. +@property(nonatomic, readonly) NSArray* children; +// isIgnored returns whether or not the accessibility object +// should be ignored by the accessibility hierarchy. +@property(nonatomic, readonly, getter=isIgnored) BOOL ignored; +// The origin of this object in the page's document. +// This is relative to webkit's top-left origin, not Cocoa's +// bottom-left origin. +@property(nonatomic, readonly) NSPoint origin; +// A string indicating the role of this object as far as accessibility +// is concerned. +@property(nonatomic, readonly) NSString* role; +// The size of this object. +@property(nonatomic, readonly) NSSize size; + +@end + +#endif // CHROME_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_COCOA_H_ diff --git a/chrome/browser/accessibility/browser_accessibility_cocoa.mm b/chrome/browser/accessibility/browser_accessibility_cocoa.mm new file mode 100644 index 0000000..0a52a05 --- /dev/null +++ b/chrome/browser/accessibility/browser_accessibility_cocoa.mm @@ -0,0 +1,362 @@ +// 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> + +#import "chrome/browser/accessibility/browser_accessibility_cocoa.h" + +#include "app/l10n_util_mac.h" +#include "base/string16.h" +#include "base/sys_string_conversions.h" +#include "chrome/browser/renderer_host/render_widget_host_view_mac.h" +#include "grit/webkit_strings.h" +#include "third_party/WebKit/WebKit/chromium/public/WebRect.h" + +namespace { + +// Returns an autoreleased copy of the WebAccessibility's attribute. +NSString* NSStringForWebAccessibilityAttribute( + const std::map<int32, string16>& attributes, + WebAccessibility::Attribute attribute) { + std::map<int32, string16>::const_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}, +}; + +// GetState checks the bitmask used in webaccessibility.h to check +// if the given state was set on the accessibility object. +bool GetState(BrowserAccessibility* accessibility, int state) { + return ((accessibility->state() >> state) & 1); +} + +} // namespace + +@implementation BrowserAccessibilityCocoa + +@synthesize browserAccessibility = browserAccessibility_; + +- (id)initWithObject:(BrowserAccessibility*)accessibility + delegate:(id<BrowserAccessibilityDelegateCocoa>)delegate { + if ((self = [super init])) { + browserAccessibility_ = accessibility; + delegate_ = delegate; + } + return self; +} + +// Deletes our associated BrowserAccessibilityMac. +- (void)dealloc { + if (browserAccessibility_) { + delete browserAccessibility_; + browserAccessibility_ = NULL; + } + + [super dealloc]; +} + +// Returns an array of BrowserAccessibilityCocoa objects, representing the +// accessibility children of this object. +- (NSArray*)children { + NSMutableArray* ret = [[[NSMutableArray alloc] + initWithCapacity:browserAccessibility_->GetChildCount()] autorelease]; + for (uint32 index = 0; + index < browserAccessibility_->GetChildCount(); + ++index) { + [ret addObject: + browserAccessibility_->GetChild(index)->toBrowserAccessibilityCocoa()]; + } + return ret; +} + +// Returns whether or not this node should be ignored in the +// accessibility tree. +- (BOOL)isIgnored { + return browserAccessibility_->role() == WebAccessibility::ROLE_IGNORED; +} + +// The origin of this accessibility object in the page's document. +// This is relative to webkit's top-left origin, not Cocoa's +// bottom-left origin. +- (NSPoint)origin { + return NSMakePoint(browserAccessibility_->location().x, + browserAccessibility_->location().y); +} + +// Returns a string indicating the role of this object. +- (NSString*)role { + NSString* role = NSAccessibilityUnknownRole; + WebAccessibility::Role value = + static_cast<WebAccessibility::Role>( browserAccessibility_->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; +} + +// Returns a string indicating the role description of this object. +- (NSString*)roleDescription { + // The following descriptions are specific to webkit. + if ([[self role] isEqualToString:@"AXWebArea"]) + return l10n_util::GetNSString(IDS_AX_ROLE_WEB_AREA); + + if ([[self role] isEqualToString:@"NSAccessibilityLinkRole"]) + return l10n_util::GetNSString(IDS_AX_ROLE_LINK); + + if ([[self role] isEqualToString:@"AXHeading"]) + return l10n_util::GetNSString(IDS_AX_ROLE_HEADING); + + return NSAccessibilityRoleDescription([self role], nil); +} + +// Returns the size of this object. +- (NSSize)size { + return NSMakeSize(browserAccessibility_->location().width, + browserAccessibility_->location().height); +} + +// Returns the accessibility value for the given attribute. If the value isn't +// supported this will return nil. +- (id)accessibilityAttributeValue:(NSString*)attribute { + if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) { + return [self role]; + } + if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) { + return NSStringForWebAccessibilityAttribute( + browserAccessibility_->attributes(), + WebAccessibility::ATTR_DESCRIPTION); + } + if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) { + return [NSValue valueWithPoint:[delegate_ accessibilityPointInScreen:self]]; + } + if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) { + return [NSValue valueWithSize:[self size]]; + } + if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute] || + [attribute isEqualToString:NSAccessibilityWindowAttribute]) { + return [delegate_ window]; + } + if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { + return [self children]; + } + if ([attribute isEqualToString:NSAccessibilityParentAttribute]) { + // A nil parent means we're the root. + if (browserAccessibility_->GetParent()) { + return NSAccessibilityUnignoredAncestor( + browserAccessibility_->GetParent()->toBrowserAccessibilityCocoa()); + } else { + // Hook back up to RenderWidgetHostViewCocoa. + return browserAccessibility_->manager()->GetParentView(); + } + } + if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) { + return base::SysUTF16ToNSString(browserAccessibility_->name()); + } + if ([attribute isEqualToString:NSAccessibilityHelpAttribute]) { + return NSStringForWebAccessibilityAttribute( + browserAccessibility_->attributes(), + WebAccessibility::ATTR_HELP); + } + if ([attribute isEqualToString:NSAccessibilityValueAttribute]) { + return base::SysUTF16ToNSString(browserAccessibility_->value()); + } + if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) { + return [self roleDescription]; + } + if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { + NSNumber* ret = [NSNumber numberWithBool: + GetState(browserAccessibility_, WebAccessibility::STATE_FOCUSED)]; + return ret; + } + // TODO(dtseng): provide complete implementations for the following. + if ([attribute isEqualToString:NSAccessibilityEnabledAttribute] || + [attribute isEqualToString:@"AXVisited"] || + [attribute isEqualToString:@"AXLoaded"]) { + return [NSNumber numberWithBool:YES]; + } + return nil; +} + +// Returns an array of action names that this object will respond to. +- (NSArray*)accessibilityActionNames { + return [NSArray arrayWithObjects: + NSAccessibilityPressAction, NSAccessibilityShowMenuAction, nil]; +} + +// Returns a sub-array of values for the given attribute value, starting at +// index, with up to maxCount items. If the given index is out of bounds, +// or there are no values for the given attribute, it will return nil. +// This method is used for querying subsets of values, without having to +// return a large set of data, such as elements with a large number of +// children. +- (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]; +} + +// Returns the count of the specified accessibility array attribute. +- (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute { + NSArray* fullArray = [self accessibilityAttributeValue:attribute]; + return [fullArray count]; +} + +// Returns the list of accessibility attributes that this object supports. +- (NSArray*)accessibilityAttributeNames { + return [NSArray arrayWithObjects: + NSAccessibilityChildrenAttribute, + NSAccessibilityDescriptionAttribute, + NSAccessibilityEnabledAttribute, + NSAccessibilityFocusedAttribute, + NSAccessibilityHelpAttribute, + NSAccessibilityParentAttribute, + NSAccessibilityPositionAttribute, + NSAccessibilityRoleAttribute, + NSAccessibilityRoleDescriptionAttribute, + NSAccessibilitySizeAttribute, + NSAccessibilityTitleAttribute, + NSAccessibilityTopLevelUIElementAttribute, + NSAccessibilityValueAttribute, + NSAccessibilityWindowAttribute, + nil]; +} + +// Returns the index of the child in this objects array of children. +- (NSUInteger)accessibilityIndexOfChild:(id)child { + NSUInteger index = 0; + for (BrowserAccessibilityCocoa* childToCheck in [self children]) { + if ([child isEqual:childToCheck]) + return index; + if (![childToCheck isIgnored]) + ++index; + } + return NSNotFound; +} + +// Returns whether or not the specified attribute can be set by the +// accessibility API via |accessibilitySetValue:forAttribute:|. +- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute { + if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) + return GetState(browserAccessibility_, WebAccessibility::STATE_FOCUSABLE); + if ([attribute isEqualToString:NSAccessibilityValueAttribute]) + return !GetState(browserAccessibility_, WebAccessibility::STATE_READONLY); + return NO; +} + +// Returns whether or not this object should be ignored in the accessibilty +// tree. +- (BOOL)accessibilityIsIgnored { + return [self isIgnored]; +} + +// Performs the given accessibilty action on the webkit accessibility object +// that backs this object. +- (void)accessibilityPerformAction:(NSString*)action { + // TODO(feldstein): Support more actions. + [delegate_ doDefaultAction:browserAccessibility_->renderer_id()]; +} + +// Returns the description of the given action. +- (NSString*)accessibilityActionDescription:(NSString*)action { + return NSAccessibilityActionDescription(action); +} + +// Sets an override value for a specific accessibility attribute. +// This class does not support this. +- (BOOL)accessibilitySetOverrideValue:(id)value + forAttribute:(NSString*)attribute { + return NO; +} + +// Sets the value for an accessibility attribute via the accessibility API. +- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute { + if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) { + NSNumber* focusedNumber = value; + BOOL focused = [focusedNumber intValue]; + [delegate_ setAccessibilityFocus:focused + accessibilityId:browserAccessibility_->renderer_id()]; + } +} + +// Returns the deepest accessibility child that should not be ignored. +// It is assumed that the hit test has been narrowed down to this object +// or one of its children, so this will never return nil. +- (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:[BrowserAccessibilityCocoa class]]) + return NO; + return ([self hash] == [object hash]); +} + +- (NSUInteger)hash { + return browserAccessibility_->renderer_id(); +} + +@end + diff --git a/chrome/browser/accessibility/browser_accessibility_delegate_mac.h b/chrome/browser/accessibility/browser_accessibility_delegate_mac.h new file mode 100644 index 0000000..18eb49b --- /dev/null +++ b/chrome/browser/accessibility/browser_accessibility_delegate_mac.h @@ -0,0 +1,23 @@ +// 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_ACCESSIBILITY_BROWSER_ACCESSIBILITY_DELEGATE_MAC_H_ +#define CHROME_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_DELEGATE_MAC_H_ +#pragma once + +@class BrowserAccessibilityCocoa; +@class NSWindow; + +// This protocol is used by the BrowserAccessibility objects to pass messages +// to, or otherwise communicate with, their underlying WebAccessibility +// objects over the IPC boundary. +@protocol BrowserAccessibilityDelegateCocoa +- (NSPoint)accessibilityPointInScreen:(BrowserAccessibilityCocoa*)accessibility; +- (void)doDefaultAction:(int32)accessibilityObjectId; +- (void)setAccessibilityFocus:(BOOL)focus + accessibilityId:(int32)accessibilityObjectId; +- (NSWindow*)window; +@end + +#endif // CHROME_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_DELEGATE_MAC_H_ diff --git a/chrome/browser/accessibility/browser_accessibility_mac.h b/chrome/browser/accessibility/browser_accessibility_mac.h index 136be99..1e83fd8 100644 --- a/chrome/browser/accessibility/browser_accessibility_mac.h +++ b/chrome/browser/accessibility/browser_accessibility_mac.h @@ -10,6 +10,7 @@ #include <utility> #include <vector> +#include "base/scoped_nsobject.h" #include "chrome/browser/accessibility/browser_accessibility.h" @class BrowserAccessibilityCocoa; @@ -20,29 +21,11 @@ class BrowserAccessibilityMac : public BrowserAccessibility { virtual void Initialize(); virtual void ReleaseReference(); - // Accessers that allow the cocoa wrapper to read these values. - const string16& name() const { return name_; } - const string16& value() const { return value_; } - const std::map<int32, string16>& attributes() const { return attributes_; } - - const std::vector<std::pair<string16, string16> >& html_attributes() const { - return html_attributes_; - } - - int32 role() const { return role_; } - int32 state() const { return state_; } - const string16& role_name() const { return role_name_; } - - // Accesser and setter for - // the BrowserAccessibilityCocoa associated with us. + // The BrowserAccessibilityCocoa associated with us. BrowserAccessibilityCocoa* native_view() const { return browser_accessibility_cocoa_; } - void native_view(BrowserAccessibilityCocoa* v) { - browser_accessibility_cocoa_ = v; - } - private: // This gives BrowserAccessibility::Create access to the class constructor. friend class BrowserAccessibility; @@ -50,8 +33,8 @@ class BrowserAccessibilityMac : public BrowserAccessibility { BrowserAccessibilityMac(); // Allows access to the BrowserAccessibilityCocoa which wraps this. - // BrowserAccessibility. We only initialize this member if the accessibility - // API requests this object. + // BrowserAccessibility. + // We own this object. BrowserAccessibilityCocoa* browser_accessibility_cocoa_; DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityMac); }; diff --git a/chrome/browser/accessibility/browser_accessibility_mac.mm b/chrome/browser/accessibility/browser_accessibility_mac.mm index 1a6c1d4..4df23c6 100644 --- a/chrome/browser/accessibility/browser_accessibility_mac.mm +++ b/chrome/browser/accessibility/browser_accessibility_mac.mm @@ -2,26 +2,47 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import <Cocoa/Cocoa.h> + #import "chrome/browser/accessibility/browser_accessibility_mac.h" +#import "chrome/browser/accessibility/browser_accessibility_cocoa.h" +#import "chrome/browser/accessibility/browser_accessibility_delegate_mac.h" +#include "chrome/browser/accessibility/browser_accessibility_manager.h" +#import "chrome/browser/renderer_host/render_widget_host_view_mac.h" + + // Static. BrowserAccessibility* BrowserAccessibility::Create() { return new BrowserAccessibilityMac(); } -BrowserAccessibilityMac::BrowserAccessibilityMac() { +BrowserAccessibilityMac::BrowserAccessibilityMac() + : browser_accessibility_cocoa_(NULL) { } -// TODO(dtseng): ensure we create BrowserAccessibilityCocoa here -// (RenderWidgetHostViewCocoa to BrowserAccessibilityManagerMac refactoring). void BrowserAccessibilityMac::Initialize() { + if (browser_accessibility_cocoa_) + return; + + // We take ownership of the cocoa obj here. + browser_accessibility_cocoa_ = [[BrowserAccessibilityCocoa alloc] + initWithObject:this + delegate:(RenderWidgetHostViewCocoa*)manager_->GetParentView()]; } -// TODO(dtseng): ensure we cleanup BrowserAccessibilityCocoa and this class. -// (RenderWidgetHostViewCocoa to BrowserAccessibilityManagerMac refactoring). void BrowserAccessibilityMac::ReleaseReference() { + if (browser_accessibility_cocoa_) { + // Relinquish ownership of the cocoa obj. + [browser_accessibility_cocoa_ release]; + browser_accessibility_cocoa_ = nil; + // At this point, other processes may have a reference to + // browser_accessibility_cocoa_. When the retain count hits zero, it will + // destroy us in dealloc. + } } -BrowserAccessibilityMac* BrowserAccessibility::toBrowserAccessibilityMac() { - return static_cast<BrowserAccessibilityMac*>(this); +BrowserAccessibilityCocoa* BrowserAccessibility::toBrowserAccessibilityCocoa() { + return static_cast<BrowserAccessibilityMac*>(this)-> + native_view(); } diff --git a/chrome/browser/accessibility/browser_accessibility_mac_unittest.mm b/chrome/browser/accessibility/browser_accessibility_mac_unittest.mm new file mode 100644 index 0000000..266182f --- /dev/null +++ b/chrome/browser/accessibility/browser_accessibility_mac_unittest.mm @@ -0,0 +1,123 @@ +// 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/scoped_ptr.h"; +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/accessibility/browser_accessibility_cocoa.h" +#include "chrome/browser/accessibility/browser_accessibility_manager.h" +#include "chrome/browser/cocoa/cocoa_test_helper.h" +#include "testing/gtest/include/gtest/gtest.h" +#import "testing/gtest_mac.h" + +@interface MockAccessibilityDelegate : + NSView<BrowserAccessibilityDelegateCocoa> + +- (NSPoint)accessibilityPointInScreen:(BrowserAccessibilityCocoa*)accessibility; +- (void)doDefaultAction:(int32)accessibilityObjectId; +- (void)setAccessibilityFocus:(BOOL)focus + accessibilityId:(int32)accessibilityObjectId; +- (NSWindow*)window; + +@end + +@implementation MockAccessibilityDelegate + +- (NSPoint)accessibilityPointInScreen: + (BrowserAccessibilityCocoa*)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]); + manager_.reset( + BrowserAccessibilityManager::Create(delegate_, root, NULL)); + // The manager still owns this object. + accessibility_ = manager_->GetRoot()->toBrowserAccessibilityCocoa(); + } + + protected: + scoped_nsobject<MockAccessibilityDelegate> delegate_; + // We do not own this object. + BrowserAccessibilityCocoa* accessibility_; + scoped_ptr<BrowserAccessibilityManager> manager_; +}; + +// Standard hit test. +TEST_F(BrowserAccessibilityTest, HitTestTest) { + BrowserAccessibilityCocoa* firstChild = + [accessibility_ accessibilityHitTest:NSMakePoint(50, 50)]; + EXPECT_NSEQ(@"Child1", + [firstChild accessibilityAttributeValue:NSAccessibilityTitleAttribute]); +} + +// Test doing a hit test on the edge of a child. +TEST_F(BrowserAccessibilityTest, EdgeHitTest) { + BrowserAccessibilityCocoa* firstChild = + [accessibility_ accessibilityHitTest:NSMakePoint(0, 0)]; + EXPECT_NSEQ(@"Child1", + [firstChild accessibilityAttributeValue:NSAccessibilityTitleAttribute]); +} + +// This will test a hit test with invalid coordinates. It is assumed that +// the hit test has been narrowed down to this object or one of its children +// so it should return itself since it has no better hit result. +TEST_F(BrowserAccessibilityTest, InvalidHitTestCoordsTest) { + BrowserAccessibilityCocoa* hitTestResult = + [accessibility_ accessibilityHitTest:NSMakePoint(-50, 50)]; + EXPECT_NSEQ(accessibility_, hitTestResult); +} + +// Test to ensure querying standard attributes works. +TEST_F(BrowserAccessibilityTest, BasicAttributeTest) { + NSString* helpText = [accessibility_ + accessibilityAttributeValue:NSAccessibilityHelpAttribute]; + EXPECT_NSEQ(@"HelpText", helpText); +} + +// Test querying for an invalid attribute to ensure it doesn't crash. +TEST_F(BrowserAccessibilityTest, InvalidAttributeTest) { + NSString* shouldBeNil = [accessibility_ + accessibilityAttributeValue:@"NSAnInvalidAttribute"]; + EXPECT_TRUE(shouldBeNil == nil); +} diff --git a/chrome/browser/accessibility/browser_accessibility_manager_mac.mm b/chrome/browser/accessibility/browser_accessibility_manager_mac.mm index 8aadf8b..6e39cd1f 100644 --- a/chrome/browser/accessibility/browser_accessibility_manager_mac.mm +++ b/chrome/browser/accessibility/browser_accessibility_manager_mac.mm @@ -4,10 +4,7 @@ #include "chrome/browser/accessibility/browser_accessibility_manager_mac.h" -#include "chrome/browser/accessibility/browser_accessibility_mac.h" -#import "chrome/browser/cocoa/browser_accessibility.h" -// TODO(dtseng): move to delegate? -#import "chrome/browser/renderer_host/render_widget_host_view_mac.h" +#import "chrome/browser/accessibility/browser_accessibility_cocoa.h" // static BrowserAccessibilityManager* BrowserAccessibilityManager::Create( @@ -27,54 +24,42 @@ BrowserAccessibilityManagerMac::BrowserAccessibilityManagerMac( gfx::NativeView parent_window, const webkit_glue::WebAccessibility& src, BrowserAccessibilityDelegate* delegate, - BrowserAccessibilityFactory* factory) + BrowserAccessibilityFactory* factory) : BrowserAccessibilityManager(parent_window, src, delegate, factory) { } void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( ViewHostMsg_AccessibilityNotification_Params::NotificationType n, BrowserAccessibility* node) { - // TODO(dtseng): support all notifications. + // Refer to AXObjectCache.mm (webkit). NSString* event_id = @""; switch (n) { case ViewHostMsg_AccessibilityNotification_Params:: NOTIFICATION_TYPE_CHECK_STATE_CHANGED: + // Does not exist on Mac. return; case ViewHostMsg_AccessibilityNotification_Params:: NOTIFICATION_TYPE_CHILDREN_CHANGED: - event_id = NSAccessibilityValueChangedNotification; - if (GetRoot() == node) - [((RenderWidgetHostViewCocoa*)GetParentView()) - setAccessibilityTreeRoot:GetRoot()]; - else - [node->GetParent()->toBrowserAccessibilityMac()->native_view() - updateDescendants]; - break; + // TODO(dtseng): no clear equivalent on Mac. + return; case ViewHostMsg_AccessibilityNotification_Params:: NOTIFICATION_TYPE_FOCUS_CHANGED: event_id = NSAccessibilityFocusedUIElementChangedNotification; - if (GetRoot() == node) - [((RenderWidgetHostViewCocoa*)GetParentView()) - setAccessibilityTreeRoot:GetRoot()]; - else - [node->GetParent()->toBrowserAccessibilityMac()->native_view() - updateDescendants]; break; case ViewHostMsg_AccessibilityNotification_Params:: NOTIFICATION_TYPE_LOAD_COMPLETE: - [((RenderWidgetHostViewCocoa*)GetParentView()) - setAccessibilityTreeRoot:GetRoot()]; - return; + event_id = @"AXLoadComplete"; + break; case ViewHostMsg_AccessibilityNotification_Params:: NOTIFICATION_TYPE_VALUE_CHANGED: event_id = NSAccessibilityValueChangedNotification; break; case ViewHostMsg_AccessibilityNotification_Params:: NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED: - return; + event_id = NSAccessibilitySelectedTextChangedNotification; + break; } - BrowserAccessibilityCocoa* native_node = node->toBrowserAccessibilityMac()-> - native_view(); + BrowserAccessibilityCocoa* native_node = node->toBrowserAccessibilityCocoa(); DCHECK(native_node); - NSAccessibilityPostNotification(native_node, event_id); + NSAccessibilityPostNotification(native_node, event_id); } |