diff options
author | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-21 21:59:51 +0000 |
---|---|---|
committer | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-21 21:59:51 +0000 |
commit | d1029c57273282ad53b0291e43d63c40ee5ec841 (patch) | |
tree | a6b2aa433d8c3e8f7faebafde3d85e7ddc1576ee | |
parent | aae9eeb10159ca2e345408f74d8425a41bb81ab4 (diff) | |
download | chromium_src-d1029c57273282ad53b0291e43d63c40ee5ec841.zip chromium_src-d1029c57273282ad53b0291e43d63c40ee5ec841.tar.gz chromium_src-d1029c57273282ad53b0291e43d63c40ee5ec841.tar.bz2 |
Add support for accessible labels for controls.
This implements TitleUIElement on Mac, and IAccessibleRelation
on Windows.
BUG=89197
TEST=Manual testing with JAWS, NVDA, and VoiceOver.
Review URL: http://codereview.chromium.org/8359012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@106787 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/browser/accessibility/browser_accessibility.cc | 12 | ||||
-rw-r--r-- | content/browser/accessibility/browser_accessibility.h | 3 | ||||
-rw-r--r-- | content/browser/accessibility/browser_accessibility_cocoa.mm | 22 | ||||
-rw-r--r-- | content/browser/accessibility/browser_accessibility_win.cc | 221 | ||||
-rw-r--r-- | content/browser/accessibility/browser_accessibility_win.h | 27 | ||||
-rw-r--r-- | webkit/glue/webaccessibility.cc | 5 | ||||
-rw-r--r-- | webkit/glue/webaccessibility.h | 3 |
7 files changed, 279 insertions, 14 deletions
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc index f9ed6a2..6a6b5f7 100644 --- a/content/browser/accessibility/browser_accessibility.cc +++ b/content/browser/accessibility/browser_accessibility.cc @@ -255,3 +255,15 @@ bool BrowserAccessibility::IsEditableText() const { return (role_ == WebAccessibility::ROLE_TEXT_FIELD || role_ == WebAccessibility::ROLE_TEXTAREA); } + +string16 BrowserAccessibility::GetTextRecursive() const { + if (!name_.empty()) { + return name_; + } + + string16 result; + for (size_t i = 0; i < children_.size(); ++i) + result += children_[i]->GetTextRecursive(); + return result; +} + diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h index 80a0250..9ae0dc9 100644 --- a/content/browser/accessibility/browser_accessibility.h +++ b/content/browser/accessibility/browser_accessibility.h @@ -220,6 +220,9 @@ class CONTENT_EXPORT BrowserAccessibility { // Returns true if this node is an editable text field of any kind. bool IsEditableText() const; + // Append the text from this node and its children. + string16 GetTextRecursive() const; + protected: BrowserAccessibility(); diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm index 162d0f8..cf3ecc3 100644 --- a/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/content/browser/accessibility/browser_accessibility_cocoa.mm @@ -194,6 +194,7 @@ static const AttributeToMethodNameEntry attributeToMethodNameContainer[] = { { NSAccessibilitySubroleAttribute, @"subrole" }, { NSAccessibilityTabsAttribute, @"tabs" }, { NSAccessibilityTitleAttribute, @"title" }, + { NSAccessibilityTitleUIElementAttribute, @"titleUIElement" }, { NSAccessibilityTopLevelUIElementAttribute, @"window" }, { NSAccessibilityURLAttribute, @"url" }, { NSAccessibilityValueAttribute, @"value" }, @@ -570,6 +571,18 @@ NSDictionary* attributeToMethodNameMap = nil; return base::SysUTF16ToNSString(browserAccessibility_->name()); } +- (id)titleUIElement { + int titleElementId; + if (browserAccessibility_->GetIntAttribute( + WebAccessibility::ATTR_TITLE_UI_ELEMENT, &titleElementId)) { + BrowserAccessibility* titleElement = + browserAccessibility_->manager()->GetFromRendererID(titleElementId); + if (titleElement) + return titleElement->toBrowserAccessibilityCocoa(); + } + return nil; +} + - (NSString*)url { StringAttribute urlAttribute = [[self role] isEqualToString:@"AXWebArea"] ? @@ -862,6 +875,15 @@ NSDictionary* attributeToMethodNameMap = nil; nil]]; } + // Title UI Element. + int i; + if (browserAccessibility_->GetIntAttribute( + WebAccessibility::ATTR_TITLE_UI_ELEMENT, &i)) { + [ret addObjectsFromArray:[NSArray arrayWithObjects: + NSAccessibilityTitleUIElementAttribute, + nil]]; + } + return ret; } diff --git a/content/browser/accessibility/browser_accessibility_win.cc b/content/browser/accessibility/browser_accessibility_win.cc index 12960ea..7be2664 100644 --- a/content/browser/accessibility/browser_accessibility_win.cc +++ b/content/browser/accessibility/browser_accessibility_win.cc @@ -7,6 +7,7 @@ #include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" +#include "base/win/scoped_comptr.h" #include "content/browser/accessibility/browser_accessibility_manager_win.h" #include "content/common/view_messages.h" #include "net/base/escape.h" @@ -22,6 +23,133 @@ const GUID GUID_ISimpleDOM = { 0x0c539790, 0x12e4, 0x11cf, 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8}; +// +// BrowserAccessibilityRelation +// +// A simple implementation of IAccessibleRelation, used to represent +// a relationship between two accessible nodes in the tree. +// + +class BrowserAccessibilityRelation + : public CComObjectRootEx<CComMultiThreadModel>, + public IAccessibleRelation { + BEGIN_COM_MAP(BrowserAccessibilityRelation) + COM_INTERFACE_ENTRY(IAccessibleRelation) + END_COM_MAP() + + CONTENT_EXPORT BrowserAccessibilityRelation() {} + CONTENT_EXPORT virtual ~BrowserAccessibilityRelation() {} + + CONTENT_EXPORT void Initialize(BrowserAccessibilityWin* owner, + const string16& type); + CONTENT_EXPORT void AddTarget(int target_id); + + // IAccessibleRelation methods. + CONTENT_EXPORT STDMETHODIMP get_relationType(BSTR* relation_type); + CONTENT_EXPORT STDMETHODIMP get_nTargets(long* n_targets); + CONTENT_EXPORT STDMETHODIMP get_target(long target_index, IUnknown** target); + CONTENT_EXPORT STDMETHODIMP get_targets(long max_targets, + IUnknown** targets, + long* n_targets); + + // IAccessibleRelation methods not implemented. + CONTENT_EXPORT STDMETHODIMP get_localizedRelationType(BSTR* relation_type) { + return E_NOTIMPL; + } + + private: + string16 type_; + base::win::ScopedComPtr<BrowserAccessibilityWin> owner_; + std::vector<int> target_ids_; +}; + +void BrowserAccessibilityRelation::Initialize(BrowserAccessibilityWin* owner, + const string16& type) { + owner_ = owner; + type_ = type; +} + +void BrowserAccessibilityRelation::AddTarget(int target_id) { + target_ids_.push_back(target_id); +} + +STDMETHODIMP BrowserAccessibilityRelation::get_relationType( + BSTR* relation_type) { + if (!relation_type) + return E_INVALIDARG; + + if (!owner_->instance_active()) + return E_FAIL; + + *relation_type = SysAllocString(type_.c_str()); + DCHECK(*relation_type); + return S_OK; +} + +STDMETHODIMP BrowserAccessibilityRelation::get_nTargets(long* n_targets) { + if (!n_targets) + return E_INVALIDARG; + + if (!owner_->instance_active()) + return E_FAIL; + + *n_targets = static_cast<long>(target_ids_.size()); + return S_OK; +} + +STDMETHODIMP BrowserAccessibilityRelation::get_target( + long target_index, IUnknown** target) { + if (!target) + return E_INVALIDARG; + + if (!owner_->instance_active()) + return E_FAIL; + + if (target_index < 0 || + target_index >= static_cast<long>(target_ids_.size())) { + return E_INVALIDARG; + } + + BrowserAccessibilityManager* manager = owner_->manager(); + BrowserAccessibility* result = + manager->GetFromRendererID(target_ids_[target_index]); + if (!result->instance_active()) + return E_FAIL; + + *target = static_cast<IAccessible*>( + result->toBrowserAccessibilityWin()->NewReference()); + return S_OK; +} + +STDMETHODIMP BrowserAccessibilityRelation::get_targets( + long max_targets, IUnknown** targets, long* n_targets) { + if (!targets || !n_targets) + return E_INVALIDARG; + + if (!owner_->instance_active()) + return E_FAIL; + + long count = static_cast<long>(target_ids_.size()); + if (count > max_targets) + count = max_targets; + + *n_targets = count; + if (count == 0) + return S_FALSE; + + for (long i = 0; i < count; ++i) { + HRESULT result = get_target(i, &targets[i]); + if (result != S_OK) + return result; + } + + return S_OK; +} + +// +// BrowserAccessibilityWin +// + // static BrowserAccessibility* BrowserAccessibility::Create() { CComObject<BrowserAccessibilityWin>* instance; @@ -43,6 +171,8 @@ BrowserAccessibilityWin::BrowserAccessibilityWin() } BrowserAccessibilityWin::~BrowserAccessibilityWin() { + for (size_t i = 0; i < relations_.size(); ++i) + relations_[i]->Release(); } // @@ -286,10 +416,24 @@ STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) { if (!target) return E_INVALIDARG; - if (target->name_.empty()) + string16 name_str = target->name_; + + // If the name is empty, see if it's labeled by another element. + if (name_str.empty()) { + int title_elem_id; + if (target->GetIntAttribute(WebAccessibility::ATTR_TITLE_UI_ELEMENT, + &title_elem_id)) { + BrowserAccessibility* title_elem = + manager_->GetFromRendererID(title_elem_id); + if (title_elem) + name_str = title_elem->GetTextRecursive(); + } + } + + if (name_str.empty()) return S_FALSE; - *name = SysAllocString(target->name_.c_str()); + *name = SysAllocString(name_str.c_str()); DCHECK(*name); return S_OK; @@ -485,6 +629,59 @@ STDMETHODIMP BrowserAccessibilityWin::get_indexInParent(LONG* index_in_parent) { return S_OK; } +STDMETHODIMP BrowserAccessibilityWin::get_nRelations(LONG* n_relations) { + if (!instance_active_) + return E_FAIL; + + if (!n_relations) + return E_INVALIDARG; + + *n_relations = relations_.size(); + return S_OK; +} + +STDMETHODIMP BrowserAccessibilityWin::get_relation( + LONG relation_index, + IAccessibleRelation** relation) { + if (!instance_active_) + return E_FAIL; + + if (relation_index < 0 || + relation_index >= static_cast<long>(relations_.size())) { + return E_INVALIDARG; + } + + if (!relation) + return E_INVALIDARG; + + relations_[relation_index]->AddRef(); + *relation = relations_[relation_index]; + return S_OK; +} + +STDMETHODIMP BrowserAccessibilityWin::get_relations( + LONG max_relations, + IAccessibleRelation** relations, + LONG* n_relations) { + if (!instance_active_) + return E_FAIL; + + if (!relations || !n_relations) + return E_INVALIDARG; + + long count = static_cast<long>(relations_.size()); + *n_relations = count; + if (count == 0) + return S_FALSE; + + for (long i = 0; i < count; ++i) { + relations_[i]->AddRef(); + relations[i] = relations_[i]; + } + + return S_OK; +} + // // IAccessibleImage methods. // @@ -2200,6 +2397,26 @@ void BrowserAccessibilityWin::Initialize() { string16 url; if (value_.empty() && (ia_state_ & STATE_SYSTEM_LINKED)) GetStringAttribute(WebAccessibility::ATTR_URL, &value_); + + // Clear any old relationships between this node and other nodes. + for (size_t i = 0; i < relations_.size(); ++i) + relations_[i]->Release(); + relations_.clear(); + + // Handle title UI element. + int title_elem_id; + if (GetIntAttribute(WebAccessibility::ATTR_TITLE_UI_ELEMENT, + &title_elem_id)) { + // Add a labelled by relationship. + CComObject<BrowserAccessibilityRelation>* relation; + HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance( + &relation); + DCHECK(SUCCEEDED(hr)); + relation->AddRef(); + relation->Initialize(this, IA2_RELATION_LABELLED_BY); + relation->AddTarget(title_elem_id); + relations_.push_back(relation); + } } void BrowserAccessibilityWin::SendNodeUpdateEvents() { diff --git a/content/browser/accessibility/browser_accessibility_win.h b/content/browser/accessibility/browser_accessibility_win.h index dcd53b1..d20b650 100644 --- a/content/browser/accessibility/browser_accessibility_win.h +++ b/content/browser/accessibility/browser_accessibility_win.h @@ -21,6 +21,7 @@ #include "webkit/glue/webaccessibility.h" class BrowserAccessibilityManagerWin; +class BrowserAccessibilityRelation; using webkit_glue::WebAccessibility; @@ -33,7 +34,8 @@ using webkit_glue::WebAccessibility; // to be used by screen readers and other assistive technology (AT). // //////////////////////////////////////////////////////////////////////////////// -class BrowserAccessibilityWin +class __declspec(uuid("562072fe-3390-43b1-9e2c-dd4118f5ac79")) +BrowserAccessibilityWin : public BrowserAccessibility, public CComObjectRootEx<CComMultiThreadModel>, public IDispatchImpl<IAccessible2, &IID_IAccessible2, @@ -179,20 +181,17 @@ class BrowserAccessibilityWin // Get this object's index in its parent object. CONTENT_EXPORT STDMETHODIMP get_indexInParent(LONG* index_in_parent); - // IAccessible2 methods not implemented. - CONTENT_EXPORT STDMETHODIMP get_extendedRole(BSTR* extended_role) { - return E_NOTIMPL; - } - CONTENT_EXPORT STDMETHODIMP get_nRelations(LONG* n_relations) { - return E_NOTIMPL; - } + CONTENT_EXPORT STDMETHODIMP get_nRelations(LONG* n_relations); + CONTENT_EXPORT STDMETHODIMP get_relation(LONG relation_index, - IAccessibleRelation** relation) { - return E_NOTIMPL; - } + IAccessibleRelation** relation); + CONTENT_EXPORT STDMETHODIMP get_relations(LONG max_relations, IAccessibleRelation** relations, - LONG* n_relations) { + LONG* n_relations); + + // IAccessible2 methods not implemented. + CONTENT_EXPORT STDMETHODIMP get_extendedRole(BSTR* extended_role) { return E_NOTIMPL; } CONTENT_EXPORT STDMETHODIMP scrollTo(enum IA2ScrollType scroll_type) { @@ -713,8 +712,12 @@ class BrowserAccessibilityWin // is initialized again but the text doesn't change. string16 old_text_; + // Relationships between this node and other nodes. + std::vector<BrowserAccessibilityRelation*> relations_; + // Give BrowserAccessibility::Create access to our constructor. friend class BrowserAccessibility; + friend class BrowserAccessibilityRelation; DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityWin); }; diff --git a/webkit/glue/webaccessibility.cc b/webkit/glue/webaccessibility.cc index 8edac6f..92de74f 100644 --- a/webkit/glue/webaccessibility.cc +++ b/webkit/glue/webaccessibility.cc @@ -615,6 +615,9 @@ std::string WebAccessibility::DebugString(bool recursive, case ATTR_TABLE_CELL_ROW_SPAN: result += " rowspan=" + value; break; + case ATTR_TITLE_UI_ELEMENT: + result += " title_elem=" + value; + break; } } @@ -787,6 +790,8 @@ void WebAccessibility::Init(const WebKit::WebAccessibilityObject& src, string_attributes[ATTR_HELP] = src.helpText(); if (src.keyboardShortcut().length()) string_attributes[ATTR_SHORTCUT] = src.keyboardShortcut(); + if (src.titleUIElement().isValid()) + int_attributes[ATTR_TITLE_UI_ELEMENT] = src.titleUIElement().axID(); if (!src.url().isEmpty()) string_attributes[ATTR_URL] = src.url().spec().utf16(); diff --git a/webkit/glue/webaccessibility.h b/webkit/glue/webaccessibility.h index 9bddd5e..4167dce 100644 --- a/webkit/glue/webaccessibility.h +++ b/webkit/glue/webaccessibility.h @@ -209,6 +209,9 @@ struct WebAccessibility { // Tree control attributes. ATTR_HIERARCHICAL_LEVEL, + + // Relationships between this element and other elements. + ATTR_TITLE_UI_ELEMENT, }; enum FloatAttribute { |