summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authordmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-03 04:31:13 +0000
committerdmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-03 04:31:13 +0000
commitc43b0dcbeb14720b12eb019db586b07c01f6ede4 (patch)
tree40d9314acfb361d9c75ec2d8eef9000ffc6a0562 /content
parentc80b8ee2e4a157c905c4ac19b97ac78eb4253b6c (diff)
downloadchromium_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')
-rw-r--r--content/browser/accessibility/browser_accessibility.cc4
-rw-r--r--content/browser/accessibility/browser_accessibility.h3
-rw-r--r--content/browser/accessibility/browser_accessibility_win.cc168
-rw-r--r--content/browser/accessibility/browser_accessibility_win.h12
-rw-r--r--content/renderer/renderer_accessibility.cc3
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;