diff options
author | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-03 04:31:13 +0000 |
---|---|---|
committer | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-03 04:31:13 +0000 |
commit | c43b0dcbeb14720b12eb019db586b07c01f6ede4 (patch) | |
tree | 40d9314acfb361d9c75ec2d8eef9000ffc6a0562 /content | |
parent | c80b8ee2e4a157c905c4ac19b97ac78eb4253b6c (diff) | |
download | chromium_src-c43b0dcbeb14720b12eb019db586b07c01f6ede4.zip chromium_src-c43b0dcbeb14720b12eb019db586b07c01f6ede4.tar.gz chromium_src-c43b0dcbeb14720b12eb019db586b07c01f6ede4.tar.bz2 |
Improve support for multiselect list box accessibility on Windows.
Exposes several new states and attributes and fires new notifications
when the selection changes. Adds general-purpose implementations of
IUnknown and IEnumVARIANT to base/win/, which will be used in the future
as we remote ATL from accessibility code.
BUG=5027,98984
TEST=manual testing with JAWS and NVDA
Review URL: http://codereview.chromium.org/8588036
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@112870 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
5 files changed, 159 insertions, 31 deletions
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc index d2f63ba..9bed3ac 100644 --- a/content/browser/accessibility/browser_accessibility.cc +++ b/content/browser/accessibility/browser_accessibility.cc @@ -257,6 +257,10 @@ bool BrowserAccessibility::GetHtmlAttribute( return false; } +bool BrowserAccessibility::HasState(WebAccessibility::State state_enum) const { + return (state_ >> state_enum) & 1; +} + bool BrowserAccessibility::IsEditableText() const { return (role_ == WebAccessibility::ROLE_TEXT_FIELD || role_ == WebAccessibility::ROLE_TEXTAREA); diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h index e6f6811..5b86072 100644 --- a/content/browser/accessibility/browser_accessibility.h +++ b/content/browser/accessibility/browser_accessibility.h @@ -222,6 +222,9 @@ class CONTENT_EXPORT BrowserAccessibility { // returns true if found. bool GetHtmlAttribute(const char* attr, string16* value) const; + // Returns true if the bit corresponding to the given state enum is 1. + bool HasState(WebAccessibility::State state_enum) const; + // Returns true if this node is an editable text field of any kind. bool IsEditableText() const; diff --git a/content/browser/accessibility/browser_accessibility_win.cc b/content/browser/accessibility/browser_accessibility_win.cc index d2ec06d..5ffacd4 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/enum_variant.h" #include "base/win/scoped_comptr.h" #include "content/browser/accessibility/browser_accessibility_manager_win.h" #include "content/common/view_messages.h" @@ -168,7 +169,8 @@ BrowserAccessibilityWin::BrowserAccessibilityWin() ia_state_(0), ia2_role_(0), ia2_state_(0), - first_time_(true) { + first_time_(true), + old_ia_state_(0) { } BrowserAccessibilityWin::~BrowserAccessibilityWin() { @@ -529,7 +531,48 @@ STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) { if (!instance_active_) return E_FAIL; - return E_NOTIMPL; + if (role_ != WebAccessibility::ROLE_LISTBOX) + return E_NOTIMPL; + + unsigned long selected_count = 0; + for (size_t i = 0; i < children_.size(); ++i) { + if (children_[i]->HasState(WebAccessibility::STATE_SELECTED)) + ++selected_count; + } + + if (selected_count == 0) { + selected->vt = VT_EMPTY; + return S_OK; + } + + if (selected_count == 1) { + for (size_t i = 0; i < children_.size(); ++i) { + if (children_[i]->HasState(WebAccessibility::STATE_SELECTED)) { + selected->vt = VT_DISPATCH; + selected->pdispVal = + children_[i]->toBrowserAccessibilityWin()->NewReference(); + return S_OK; + } + } + } + + // Multiple items are selected. + base::win::EnumVariant* enum_variant = + new base::win::EnumVariant(selected_count); + enum_variant->AddRef(); + unsigned long index = 0; + for (size_t i = 0; i < children_.size(); ++i) { + if (children_[i]->HasState(WebAccessibility::STATE_SELECTED)) { + enum_variant->ItemAt(index)->vt = VT_DISPATCH; + enum_variant->ItemAt(index)->pdispVal = + children_[i]->toBrowserAccessibilityWin()->NewReference(); + ++index; + } + } + selected->vt = VT_UNKNOWN; + selected->punkVal = static_cast<IUnknown*>( + static_cast<base::win::IUnknownImpl*>(enum_variant)); + return S_OK; } STDMETHODIMP BrowserAccessibilityWin::accSelect( @@ -683,6 +726,28 @@ STDMETHODIMP BrowserAccessibilityWin::get_relations( return S_OK; } +STDMETHODIMP BrowserAccessibilityWin::get_groupPosition( + LONG* group_level, + LONG* similar_items_in_group, + LONG* position_in_group) { + if (!instance_active_) + return E_FAIL; + + if (!group_level || !similar_items_in_group || !position_in_group) + return E_INVALIDARG; + + if (role_ == WebAccessibility::ROLE_LISTBOX_OPTION && + parent_ && + parent_->role() == WebAccessibility::ROLE_LISTBOX) { + *group_level = 0; + *similar_items_in_group = parent_->child_count(); + *position_in_group = index_in_parent_ + 1; + return S_OK; + } + + return E_NOTIMPL; +} + // // IAccessibleImage methods. // @@ -2335,6 +2400,16 @@ void BrowserAccessibilityWin::Initialize() { // Expose "level" attribute for tree nodes. IntAttributeToIA2(WebAccessibility::ATTR_HIERARCHICAL_LEVEL, "level"); + // Expose the set size and position in set for listbox options. + if (role_ == WebAccessibility::ROLE_LISTBOX_OPTION && + parent_ && + parent_->role() == WebAccessibility::ROLE_LISTBOX) { + ia2_attributes_.push_back( + L"setsize:" + base::IntToString16(parent_->child_count())); + ia2_attributes_.push_back( + L"setsize:" + base::IntToString16(index_in_parent_ + 1)); + } + // Expose live region attributes. StringAttributeToIA2(WebAccessibility::ATTR_LIVE_STATUS, "live"); StringAttributeToIA2(WebAccessibility::ATTR_LIVE_RELEVANT, "relevant"); @@ -2387,9 +2462,11 @@ void BrowserAccessibilityWin::Initialize() { } } - // If this is static text, put the text in the name rather than the value. - if (role_ == WebAccessibility::ROLE_STATIC_TEXT && name_.empty()) + if (name_.empty() && + (role_ == WebAccessibility::ROLE_LISTBOX_OPTION || + role_ == WebAccessibility::ROLE_STATIC_TEXT)) { name_.swap(value_); + } // If this object doesn't have a name but it does have a description, // use the description as its name - because some screen readers only @@ -2443,6 +2520,40 @@ void BrowserAccessibilityWin::SendNodeUpdateEvents() { previous_text_ = text; } + // Fire events if the state has changed. + if (!first_time_ && ia_state_ != old_ia_state_) { + // Normally focus events are handled elsewhere, however + // focus for managed descendants is platform-specific. + // Fire a focus event if the focused descendant in a multi-select + // list box changes. + if (role_ == WebAccessibility::ROLE_LISTBOX_OPTION && + (ia_state_ & STATE_SYSTEM_FOCUSABLE) && + (ia_state_ & STATE_SYSTEM_SELECTABLE) && + (ia_state_ & STATE_SYSTEM_FOCUSED) && + !(old_ia_state_ & STATE_SYSTEM_FOCUSED)) { + ::NotifyWinEvent(EVENT_OBJECT_FOCUS, + manager_->GetParentView(), + OBJID_CLIENT, + child_id()); + } + + if ((ia_state_ & STATE_SYSTEM_SELECTED) && + !(old_ia_state_ & STATE_SYSTEM_SELECTED)) { + ::NotifyWinEvent(EVENT_OBJECT_SELECTIONADD, + manager_->GetParentView(), + OBJID_CLIENT, + child_id()); + } else if (!(ia_state_ & STATE_SYSTEM_SELECTED) && + (old_ia_state_ & STATE_SYSTEM_SELECTED)) { + ::NotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE, + manager_->GetParentView(), + OBJID_CLIENT, + child_id()); + } + + old_ia_state_ = ia_state_; + } + first_time_ = false; } @@ -2571,51 +2682,53 @@ void BrowserAccessibilityWin::InitRoleAndState() { ia2_state_ = IA2_STATE_OPAQUE; ia2_attributes_.clear(); - if ((state_ >> WebAccessibility::STATE_BUSY) & 1) + if (HasState(WebAccessibility::STATE_BUSY)) ia_state_|= STATE_SYSTEM_BUSY; - if ((state_ >> WebAccessibility::STATE_CHECKED) & 1) + if (HasState(WebAccessibility::STATE_CHECKED)) ia_state_ |= STATE_SYSTEM_CHECKED; - if ((state_ >> WebAccessibility::STATE_COLLAPSED) & 1) + if (HasState(WebAccessibility::STATE_COLLAPSED)) ia_state_|= STATE_SYSTEM_COLLAPSED; - if ((state_ >> WebAccessibility::STATE_EXPANDED) & 1) + if (HasState(WebAccessibility::STATE_EXPANDED)) ia_state_|= STATE_SYSTEM_EXPANDED; - if ((state_ >> WebAccessibility::STATE_FOCUSABLE) & 1) + if (HasState(WebAccessibility::STATE_FOCUSABLE)) ia_state_|= STATE_SYSTEM_FOCUSABLE; - if ((state_ >> WebAccessibility::STATE_HASPOPUP) & 1) + if (HasState(WebAccessibility::STATE_HASPOPUP)) ia_state_|= STATE_SYSTEM_HASPOPUP; - if ((state_ >> WebAccessibility::STATE_HOTTRACKED) & 1) + if (HasState(WebAccessibility::STATE_HOTTRACKED)) ia_state_|= STATE_SYSTEM_HOTTRACKED; - if ((state_ >> WebAccessibility::STATE_INDETERMINATE) & 1) + if (HasState(WebAccessibility::STATE_INDETERMINATE)) ia_state_|= STATE_SYSTEM_INDETERMINATE; - if ((state_ >> WebAccessibility::STATE_INVISIBLE) & 1) + if (HasState(WebAccessibility::STATE_INVISIBLE)) ia_state_|= STATE_SYSTEM_INVISIBLE; - if ((state_ >> WebAccessibility::STATE_LINKED) & 1) + if (HasState(WebAccessibility::STATE_LINKED)) ia_state_|= STATE_SYSTEM_LINKED; - if ((state_ >> WebAccessibility::STATE_MULTISELECTABLE) & 1) + if (HasState(WebAccessibility::STATE_MULTISELECTABLE)) { + ia_state_|= STATE_SYSTEM_EXTSELECTABLE; ia_state_|= STATE_SYSTEM_MULTISELECTABLE; + } // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect. - if ((state_ >> WebAccessibility::STATE_OFFSCREEN) & 1) + if (HasState(WebAccessibility::STATE_OFFSCREEN)) ia_state_|= STATE_SYSTEM_OFFSCREEN; - if ((state_ >> WebAccessibility::STATE_PRESSED) & 1) + if (HasState(WebAccessibility::STATE_PRESSED)) ia_state_|= STATE_SYSTEM_PRESSED; - if ((state_ >> WebAccessibility::STATE_PROTECTED) & 1) + if (HasState(WebAccessibility::STATE_PROTECTED)) ia_state_|= STATE_SYSTEM_PROTECTED; - if ((state_ >> WebAccessibility::STATE_REQUIRED) & 1) + if (HasState(WebAccessibility::STATE_REQUIRED)) ia2_state_|= IA2_STATE_REQUIRED; - if ((state_ >> WebAccessibility::STATE_SELECTABLE) & 1) + if (HasState(WebAccessibility::STATE_SELECTABLE)) ia_state_|= STATE_SYSTEM_SELECTABLE; - if ((state_ >> WebAccessibility::STATE_SELECTED) & 1) + if (HasState(WebAccessibility::STATE_SELECTED)) ia_state_|= STATE_SYSTEM_SELECTED; - if ((state_ >> WebAccessibility::STATE_TRAVERSED) & 1) + if (HasState(WebAccessibility::STATE_TRAVERSED)) ia_state_|= STATE_SYSTEM_TRAVERSED; - if ((state_ >> WebAccessibility::STATE_UNAVAILABLE) & 1) + if (HasState(WebAccessibility::STATE_UNAVAILABLE)) ia_state_|= STATE_SYSTEM_UNAVAILABLE; - if ((state_ >> WebAccessibility::STATE_VERTICAL) & 1) { + if (HasState(WebAccessibility::STATE_VERTICAL)) { ia2_state_|= IA2_STATE_VERTICAL; } else { ia2_state_|= IA2_STATE_HORIZONTAL; } - if ((state_ >> WebAccessibility::STATE_VISITED) & 1) + if (HasState(WebAccessibility::STATE_VISITED)) ia_state_|= STATE_SYSTEM_TRAVERSED; // The meaning of the readonly state on Windows is very different from @@ -2788,6 +2901,11 @@ void BrowserAccessibilityWin::InitRoleAndState() { break; case WebAccessibility::ROLE_LISTBOX_OPTION: ia_role_ = ROLE_SYSTEM_LISTITEM; + if (ia_state_ & STATE_SYSTEM_SELECTABLE) { + ia_state_ |= STATE_SYSTEM_FOCUSABLE; + if (HasState(WebAccessibility::STATE_FOCUSED)) + ia_state_|= STATE_SYSTEM_FOCUSED; + } break; case WebAccessibility::ROLE_LIST_ITEM: ia_role_ = ROLE_SYSTEM_LISTITEM; diff --git a/content/browser/accessibility/browser_accessibility_win.h b/content/browser/accessibility/browser_accessibility_win.h index 3a46698..addb39b 100644 --- a/content/browser/accessibility/browser_accessibility_win.h +++ b/content/browser/accessibility/browser_accessibility_win.h @@ -194,6 +194,10 @@ BrowserAccessibilityWin IAccessibleRelation** relations, LONG* n_relations); + CONTENT_EXPORT STDMETHODIMP get_groupPosition(LONG* group_level, + LONG* similar_items_in_group, + LONG* position_in_group); + // IAccessible2 methods not implemented. CONTENT_EXPORT STDMETHODIMP get_extendedRole(BSTR* extended_role) { return E_NOTIMPL; @@ -207,11 +211,6 @@ BrowserAccessibilityWin LONG y) { return E_NOTIMPL; } - CONTENT_EXPORT STDMETHODIMP get_groupPosition(LONG* group_level, - LONG* similar_items_in_group, - LONG* position_in_group) { - return E_NOTIMPL; - } CONTENT_EXPORT STDMETHODIMP get_localizedExtendedRole( BSTR* localized_extended_role) { return E_NOTIMPL; @@ -722,6 +721,9 @@ BrowserAccessibilityWin // is initialized again but the text doesn't change. string16 old_text_; + // The previous state, used to see if there was a state change. + int32 old_ia_state_; + // Relationships between this node and other nodes. std::vector<BrowserAccessibilityRelation*> relations_; diff --git a/content/renderer/renderer_accessibility.cc b/content/renderer/renderer_accessibility.cc index bb65fd4..7ea78ad 100644 --- a/content/renderer/renderer_accessibility.cc +++ b/content/renderer/renderer_accessibility.cc @@ -514,7 +514,8 @@ bool RendererAccessibility::ShouldIncludeChildren( WebKit::WebAccessibilityNotification type = notification.type; if (type == WebKit::WebAccessibilityNotificationChildrenChanged || type == WebKit::WebAccessibilityNotificationLoadComplete || - type == WebKit::WebAccessibilityNotificationLiveRegionChanged) { + type == WebKit::WebAccessibilityNotificationLiveRegionChanged || + type == WebKit::WebAccessibilityNotificationSelectedChildrenChanged) { return true; } return false; |