Commit c43b0dcb authored by dmazzoni@chromium.org's avatar dmazzoni@chromium.org

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
parent c80b8ee2
...@@ -236,10 +236,12 @@ ...@@ -236,10 +236,12 @@
'values_unittest.cc', 'values_unittest.cc',
'version_unittest.cc', 'version_unittest.cc',
'vlog_unittest.cc', 'vlog_unittest.cc',
'win/enum_variant_unittest.cc',
'win/event_trace_consumer_unittest.cc', 'win/event_trace_consumer_unittest.cc',
'win/event_trace_controller_unittest.cc', 'win/event_trace_controller_unittest.cc',
'win/event_trace_provider_unittest.cc', 'win/event_trace_provider_unittest.cc',
'win/i18n_unittest.cc', 'win/i18n_unittest.cc',
'win/iunknown_impl_unittest.cc',
'win/object_watcher_unittest.cc', 'win/object_watcher_unittest.cc',
'win/pe_image_unittest.cc', 'win/pe_image_unittest.cc',
'win/registry_unittest.cc', 'win/registry_unittest.cc',
......
...@@ -367,6 +367,8 @@ ...@@ -367,6 +367,8 @@
'nix/xdg_util.cc', 'nix/xdg_util.cc',
'nix/xdg_util.h', 'nix/xdg_util.h',
'wayland/wayland_event.h', 'wayland/wayland_event.h',
'win/enum_variant.h',
'win/enum_variant.cc',
'win/event_trace_consumer.h', 'win/event_trace_consumer.h',
'win/event_trace_controller.cc', 'win/event_trace_controller.cc',
'win/event_trace_controller.h', 'win/event_trace_controller.h',
...@@ -376,6 +378,8 @@ ...@@ -376,6 +378,8 @@
'win/i18n.h', 'win/i18n.h',
'win/iat_patch_function.cc', 'win/iat_patch_function.cc',
'win/iat_patch_function.h', 'win/iat_patch_function.h',
'win/iunknown_impl.h',
'win/iunknown_impl.cc',
'win/object_watcher.cc', 'win/object_watcher.cc',
'win/object_watcher.h', 'win/object_watcher.h',
'win/registry.cc', 'win/registry.cc',
......
// Copyright (c) 2011 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 "base/win/enum_variant.h"
#include <algorithm>
#include "base/logging.h"
namespace base {
namespace win {
EnumVariant::EnumVariant(unsigned long count)
: items_(new VARIANT[count]),
count_(count),
current_index_(0) {
}
EnumVariant::~EnumVariant() {
}
VARIANT* EnumVariant::ItemAt(unsigned long index) {
DCHECK(index < count_);
return &items_[index];
}
ULONG STDMETHODCALLTYPE EnumVariant::AddRef() {
return IUnknownImpl::AddRef();
}
ULONG STDMETHODCALLTYPE EnumVariant::Release() {
return IUnknownImpl::Release();
}
STDMETHODIMP EnumVariant::QueryInterface(REFIID riid, void** ppv) {
if (riid == IID_IEnumVARIANT) {
*ppv = static_cast<IEnumVARIANT*>(this);
AddRef();
return S_OK;
}
return IUnknownImpl::QueryInterface(riid, ppv);
}
STDMETHODIMP EnumVariant::Next(ULONG requested_count,
VARIANT* out_elements,
ULONG* out_elements_received) {
unsigned long count = std::min(requested_count, count_ - current_index_);
for (unsigned long i = 0; i < count; ++i)
out_elements[i] = items_[current_index_ + i];
current_index_ += count;
*out_elements_received = count;
return (count == requested_count ? S_OK : S_FALSE);
}
STDMETHODIMP EnumVariant::Skip(ULONG skip_count) {
unsigned long count = skip_count;
if (current_index_ + count > count_)
count = count_ - current_index_;
current_index_ += count;
return (count == skip_count ? S_OK : S_FALSE);
}
STDMETHODIMP EnumVariant::Reset() {
current_index_ = 0;
return S_OK;
}
STDMETHODIMP EnumVariant::Clone(IEnumVARIANT** out_cloned_object) {
EnumVariant* other = new EnumVariant(count_);
if (count_ > 0)
memcpy(other->ItemAt(0), &items_[0], count_ * sizeof(VARIANT));
other->Skip(current_index_);
other->AddRef();
*out_cloned_object = other;
return S_OK;
}
} // namespace win
} // namespace base
// Copyright (c) 2011 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 BASE_WIN_ENUM_VARIANT_H_
#define BASE_WIN_ENUM_VARIANT_H_
#pragma once
#include <unknwn.h>
#include "base/memory/scoped_ptr.h"
#include "base/win/iunknown_impl.h"
namespace base {
namespace win {
// A simple implementation of IEnumVARIANT.
class BASE_EXPORT EnumVariant
: public IEnumVARIANT,
public IUnknownImpl {
public:
// The constructor allocates an array of size |count|. Then use
// ItemAt to set the value of each item in the array to initialize it.
explicit EnumVariant(unsigned long count);
// Returns a mutable pointer to the item at position |index|.
VARIANT* ItemAt(unsigned long index);
// IUnknown.
ULONG STDMETHODCALLTYPE AddRef() OVERRIDE;
ULONG STDMETHODCALLTYPE Release() OVERRIDE;
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) OVERRIDE;
// IEnumVARIANT.
STDMETHODIMP Next(ULONG requested_count,
VARIANT* out_elements,
ULONG* out_elements_received);
STDMETHODIMP Skip(ULONG skip_count);
STDMETHODIMP Reset();
STDMETHODIMP Clone(IEnumVARIANT** out_cloned_object);
private:
~EnumVariant();
scoped_array<VARIANT> items_;
unsigned long count_;
unsigned long current_index_;
};
} // namespace win
} // namespace base
#endif // BASE_WIN_ENUM_VARIANT_H_
// Copyright (c) 2011 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 "base/win/enum_variant.h"
#include "base/win/scoped_com_initializer.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace win {
TEST(EnumVariantTest, EmptyEnumVariant) {
ScopedCOMInitializer com_initializer;
EnumVariant* ev = new EnumVariant(0);
ev->AddRef();
IUnknown* iunknown;
EXPECT_TRUE(SUCCEEDED(
ev->QueryInterface(IID_IUnknown, reinterpret_cast<void**>(&iunknown))));
iunknown->Release();
IEnumVARIANT* ienumvariant;
EXPECT_TRUE(SUCCEEDED(
ev->QueryInterface(IID_IEnumVARIANT,
reinterpret_cast<void**>(&ienumvariant))));
EXPECT_EQ(ev, ienumvariant);
ienumvariant->Release();
VARIANT out_element;
ULONG out_received = 0;
EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
EXPECT_EQ(0, out_received);
EXPECT_EQ(S_FALSE, ev->Skip(1));
EXPECT_EQ(S_OK, ev->Reset());
IEnumVARIANT* ev2 = NULL;
EXPECT_EQ(S_OK, ev->Clone(&ev2));
EXPECT_NE(static_cast<IEnumVARIANT*>(NULL), ev2);
EXPECT_NE(ev, ev2);
EXPECT_EQ(S_FALSE, ev2->Skip(1));
EXPECT_EQ(S_OK, ev2->Reset());
ULONG ev2_finalrefcount = ev2->Release();
EXPECT_EQ(0, ev2_finalrefcount);
ULONG ev_finalrefcount = ev->Release();
EXPECT_EQ(0, ev_finalrefcount);
}
TEST(EnumVariantTest, SimpleEnumVariant) {
ScopedCOMInitializer com_initializer;
EnumVariant* ev = new EnumVariant(3);
ev->AddRef();
ev->ItemAt(0)->vt = VT_I4;
ev->ItemAt(0)->lVal = 10;
ev->ItemAt(1)->vt = VT_I4;
ev->ItemAt(1)->lVal = 20;
ev->ItemAt(2)->vt = VT_I4;
ev->ItemAt(2)->lVal = 30;
// Get elements one at a time.
VARIANT out_element;
ULONG out_received = 0;
EXPECT_EQ(S_OK, ev->Next(1, &out_element, &out_received));
EXPECT_EQ(1, out_received);
EXPECT_EQ(VT_I4, out_element.vt);
EXPECT_EQ(10, out_element.lVal);
EXPECT_EQ(S_OK, ev->Skip(1));
EXPECT_EQ(S_OK, ev->Next(1, &out_element, &out_received));
EXPECT_EQ(1, out_received);
EXPECT_EQ(VT_I4, out_element.vt);
EXPECT_EQ(30, out_element.lVal);
EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
// Reset and get all elements at once.
VARIANT out_elements[3];
EXPECT_EQ(S_OK, ev->Reset());
EXPECT_EQ(S_OK, ev->Next(3, out_elements, &out_received));
EXPECT_EQ(3, out_received);
EXPECT_EQ(VT_I4, out_elements[0].vt);
EXPECT_EQ(10, out_elements[0].lVal);
EXPECT_EQ(VT_I4, out_elements[1].vt);
EXPECT_EQ(20, out_elements[1].lVal);
EXPECT_EQ(VT_I4, out_elements[2].vt);
EXPECT_EQ(30, out_elements[2].lVal);
EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
// Clone it.
IEnumVARIANT* ev2 = NULL;
EXPECT_EQ(S_OK, ev->Clone(&ev2));
EXPECT_TRUE(ev2 != NULL);
EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
EXPECT_EQ(S_OK, ev2->Reset());
EXPECT_EQ(S_OK, ev2->Next(3, out_elements, &out_received));
EXPECT_EQ(3, out_received);
EXPECT_EQ(VT_I4, out_elements[0].vt);
EXPECT_EQ(10, out_elements[0].lVal);
EXPECT_EQ(VT_I4, out_elements[1].vt);
EXPECT_EQ(20, out_elements[1].lVal);
EXPECT_EQ(VT_I4, out_elements[2].vt);
EXPECT_EQ(30, out_elements[2].lVal);
EXPECT_EQ(S_FALSE, ev2->Next(1, &out_element, &out_received));
ULONG ev2_finalrefcount = ev2->Release();
EXPECT_EQ(0, ev2_finalrefcount);
ULONG ev_finalrefcount = ev->Release();
EXPECT_EQ(0, ev_finalrefcount);
}
} // namespace win
} // namespace base
// Copyright (c) 2011 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 "base/win/iunknown_impl.h"
namespace base {
namespace win {
IUnknownImpl::IUnknownImpl()
: ref_count_(0) {
}
IUnknownImpl::~IUnknownImpl() {
}
ULONG STDMETHODCALLTYPE IUnknownImpl::AddRef() {
base::AtomicRefCountInc(&ref_count_);
return 1;
}
ULONG STDMETHODCALLTYPE IUnknownImpl::Release() {
if (!base::AtomicRefCountDec(&ref_count_)) {
delete this;
return 0;
}
return 1;
}
STDMETHODIMP IUnknownImpl::QueryInterface(REFIID riid, void** ppv) {
if (riid == IID_IUnknown) {
*ppv = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
} // namespace win
} // namespace base
// Copyright (c) 2011 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 BASE_WIN_IUNKNOWN_IMPL_H_
#define BASE_WIN_IUNKNOWN_IMPL_H_
#pragma once
#include <unknwn.h>
#include "base/atomic_ref_count.h"
#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
namespace base {
namespace win {
// IUnknown implementation for other classes to derive from.
class BASE_EXPORT IUnknownImpl : public IUnknown {
public:
IUnknownImpl();
virtual ULONG STDMETHODCALLTYPE AddRef() OVERRIDE;
virtual ULONG STDMETHODCALLTYPE Release() OVERRIDE;
// Subclasses should extend this to return any interfaces they provide.
virtual STDMETHODIMP QueryInterface(REFIID riid, void** ppv) OVERRIDE;
protected:
virtual ~IUnknownImpl();
private:
AtomicRefCount ref_count_;
};
} // namespace win
} // namespace base
#endif // BASE_WIN_IUNKNOWN_IMPL_H_
// Copyright (c) 2011 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 "base/win/iunknown_impl.h"
#include "base/win/scoped_com_initializer.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace win {
class TestIUnknownImplSubclass : public IUnknownImpl {
public:
TestIUnknownImplSubclass() {
++instance_count;
}
virtual ~TestIUnknownImplSubclass() {
--instance_count;
}
static int instance_count;
};
// static
int TestIUnknownImplSubclass::instance_count = 0;
TEST(IUnknownImplTest, IUnknownImpl) {
ScopedCOMInitializer com_initializer;
EXPECT_EQ(0, TestIUnknownImplSubclass::instance_count);
IUnknown* u = new TestIUnknownImplSubclass();
EXPECT_EQ(1, TestIUnknownImplSubclass::instance_count);
EXPECT_EQ(1, u->AddRef());
EXPECT_EQ(1, u->AddRef());
IUnknown* other = NULL;
EXPECT_EQ(E_NOINTERFACE, u->QueryInterface(
IID_IDispatch, reinterpret_cast<void**>(&other)));
EXPECT_EQ(S_OK, u->QueryInterface(
IID_IUnknown, reinterpret_cast<void**>(&other)));
other->Release();
EXPECT_EQ(1, u->Release());
EXPECT_EQ(0, u->Release());
EXPECT_EQ(0, TestIUnknownImplSubclass::instance_count);
}
} // namespace win
} // namespace base
...@@ -257,6 +257,10 @@ bool BrowserAccessibility::GetHtmlAttribute( ...@@ -257,6 +257,10 @@ bool BrowserAccessibility::GetHtmlAttribute(
return false; return false;
} }
bool BrowserAccessibility::HasState(WebAccessibility::State state_enum) const {
return (state_ >> state_enum) & 1;
}
bool BrowserAccessibility::IsEditableText() const { bool BrowserAccessibility::IsEditableText() const {
return (role_ == WebAccessibility::ROLE_TEXT_FIELD || return (role_ == WebAccessibility::ROLE_TEXT_FIELD ||
role_ == WebAccessibility::ROLE_TEXTAREA); role_ == WebAccessibility::ROLE_TEXTAREA);
......
...@@ -222,6 +222,9 @@ class CONTENT_EXPORT BrowserAccessibility { ...@@ -222,6 +222,9 @@ class CONTENT_EXPORT BrowserAccessibility {
// returns true if found. // returns true if found.
bool GetHtmlAttribute(const char* attr, string16* value) const; 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. // Returns true if this node is an editable text field of any kind.
bool IsEditableText() const; bool IsEditableText() const;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/string_number_conversions.h" #include "base/string_number_conversions.h"
#include "base/string_util.h" #include "base/string_util.h"
#include "base/utf_string_conversions.h" #include "base/utf_string_conversions.h"
#include "base/win/enum_variant.h"
#include "base/win/scoped_comptr.h" #include "base/win/scoped_comptr.h"
#include "content/browser/accessibility/browser_accessibility_manager_win.h" #include "content/browser/accessibility/browser_accessibility_manager_win.h"
#include "content/common/view_messages.h" #include "content/common/view_messages.h"
...@@ -168,7 +169,8 @@ BrowserAccessibilityWin::BrowserAccessibilityWin() ...@@ -168,7 +169,8 @@ BrowserAccessibilityWin::BrowserAccessibilityWin()
ia_state_(0), ia_state_(0),
ia2_role_(0), ia2_role_(0),
ia2_state_(0), ia2_state_(0),
first_time_(true) { first_time_(true),
old_ia_state_(0) {
} }
BrowserAccessibilityWin::~BrowserAccessibilityWin() { BrowserAccessibilityWin::~BrowserAccessibilityWin() {
...@@ -529,7 +531,48 @@ STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) { ...@@ -529,7 +531,48 @@ STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) {
if (!instance_active_) if (!instance_active_)
return E_FAIL; 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( STDMETHODIMP BrowserAccessibilityWin::accSelect(
...@@ -683,6 +726,28 @@ STDMETHODIMP BrowserAccessibilityWin::get_relations( ...@@ -683,6 +726,28 @@ STDMETHODIMP BrowserAccessibilityWin::get_relations(
return S_OK; 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. // IAccessibleImage methods.
// //
...@@ -2335,6 +2400,16 @@ void BrowserAccessibilityWin::Initialize() { ...@@ -2335,6 +2400,16 @@ void BrowserAccessibilityWin::Initialize() {
// Expose "level" attribute for tree nodes. // Expose "level" attribute for tree nodes.
IntAttributeToIA2(WebAccessibility::ATTR_HIERARCHICAL_LEVEL, "level"); 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. // Expose live region attributes.
StringAttributeToIA2(WebAccessibility::ATTR_LIVE_STATUS, "live"); StringAttributeToIA2(WebAccessibility::ATTR_LIVE_STATUS, "live");
StringAttributeToIA2(WebAccessibility::ATTR_LIVE_RELEVANT, "relevant"); StringAttributeToIA2(WebAccessibility::ATTR_LIVE_RELEVANT, "relevant");
...@@ -2387,9 +2462,11 @@ void BrowserAccessibilityWin::Initialize() { ...@@ -2387,9 +2462,11 @@ void BrowserAccessibilityWin::Initialize() {
} }
} }
// If this is static text, put the text in the name rather than the value. if (name_.empty() &&
if (role_ == WebAccessibility::ROLE_STATIC_TEXT && name_.empty()) (role_ == WebAccessibility::ROLE_LISTBOX_OPTION ||
role_ == WebAccessibility::ROLE_STATIC_TEXT)) {
name_.swap(value_); name_.swap(value_);
}
// If this object doesn't have a name but it does have a description, // 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 // use the description as its name - because some screen readers only
...@@ -2443,6 +2520,40 @@ void BrowserAccessibilityWin::SendNodeUpdateEvents() { ...@@ -2443,6 +2520,40 @@ void BrowserAccessibilityWin::SendNodeUpdateEvents() {
previous_text_ = text; 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; first_time_ = false;
} }
...@@ -2571,51 +2682,53 @@ void BrowserAccessibilityWin::InitRoleAndState() { ...@@ -2571,51 +2682,53 @@ void BrowserAccessibilityWin::InitRoleAndState() {
ia2_state_ = IA2_STATE_OPAQUE; ia2_state_ = IA2_STATE_OPAQUE;
ia2_attributes_.clear(); ia2_attributes_.clear();
if ((state_ >> WebAccessibility::STATE_BUSY) & 1) if (HasState(WebAccessibility::STATE_BUSY))
ia_state_|= STATE_SYSTEM_BUSY; ia_state_|= STATE_SYSTEM_BUSY;
if ((state_ >> WebAccessibility::STATE_CHECKED) & 1) if (HasState(WebAccessibility::STATE_CHECKED))
ia_state_ |= STATE_SYSTEM_CHECKED; ia_state_ |= STATE_SYSTEM_CHECKED;
if ((state_ >> WebAccessibility::STATE_COLLAPSED) & 1) if (HasState(WebAccessibility::STATE_COLLAPSED))
ia_state_|= STATE_SYSTEM_COLLAPSED; ia_state_|= STATE_SYSTEM_COLLAPSED;
if ((state_ >> WebAccessibility::STATE_EXPANDED) & 1) if (HasState(WebAccessibility::STATE_EXPANDED))
ia_state_|= STATE_SYSTEM_EXPANDED; ia_state_|= STATE_SYSTEM_EXPANDED;
if ((state_ >> WebAccessibility::STATE_FOCUSABLE) & 1) if (HasState(WebAccessibility::STATE_FOCUSABLE))
ia_state_|= STATE_SYSTEM_FOCUSABLE; ia_state_|= STATE_SYSTEM_FOCUSABLE;
if ((state_ >> WebAccessibility::STATE_HASPOPUP) & 1) if (HasState(WebAccessibility::STATE_HASPOPUP))
ia_state_|= STATE_SYSTEM_HASPOPUP; ia_state_|= STATE_SYSTEM_HASPOPUP;
if ((state_ >> WebAccessibility::STATE_HOTTRACKED) & 1) if (HasState(WebAccessibility::STATE_HOTTRACKED))
ia_state_|= STATE_SYSTEM_HOTTRACKED; ia_state_|= STATE_SYSTEM_HOTTRACKED;
if ((state_ >> WebAccessibility::STATE_INDETERMINATE) & 1) if (HasState(WebAccessibility::STATE_INDETERMINATE))
ia_state_|= STATE_SYSTEM_INDETERMINATE; ia_state_|= STATE_SYSTEM_INDETERMINATE;
if ((state_ >> WebAccessibility::STATE_INVISIBLE) & 1) if (HasState(WebAccessibility::STATE_INVISIBLE))
ia_state_|= STATE_SYSTEM_INVISIBLE; ia_state_|= STATE_SYSTEM_INVISIBLE;
if ((state_ >> WebAccessibility::STATE_LINKED) & 1) if (HasState(WebAccessibility::STATE_LINKED))
ia_state_|= STATE_SYSTEM_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; ia_state_|= STATE_SYSTEM_MULTISELECTABLE;
}
// TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect. // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
if ((state_ >> WebAccessibility::STATE_OFFSCREEN) & 1) if (HasState(WebAccessibility::STATE_OFFSCREEN))
ia_state_|= STATE_SYSTEM_OFFSCREEN; ia_state_|= STATE_SYSTEM_OFFSCREEN;
if ((state_ >> WebAccessibility::STATE_PRESSED) & 1) if (HasState(WebAccessibility::STATE_PRESSED))
ia_state_|= STATE_SYSTEM_PRESSED; ia_state_|= STATE_SYSTEM_PRESSED;
if ((state_ >> WebAccessibility::STATE_PROTECTED) & 1) if (HasState(WebAccessibility::STATE_PROTECTED))
ia_state_|= STATE_SYSTEM_PROTECTED; ia_state_|= STATE_SYSTEM_PROTECTED;
if ((state_ >> WebAccessibility::STATE_REQUIRED) & 1) if (HasState(WebAccessibility::STATE_REQUIRED))
ia2_state_|= IA2_STATE_REQUIRED; ia2_state_|= IA2_STATE_REQUIRED;
if ((state_ >> WebAccessibility::STATE_SELECTABLE) & 1) if (HasState(WebAccessibility::STATE_SELECTABLE))
ia_state_|= STATE_SYSTEM_SELECTABLE; ia_state_|= STATE_SYSTEM_SELECTABLE;
if ((state_ >> WebAccessibility::STATE_SELECTED) & 1) if (HasState(WebAccessibility::STATE_SELECTED))
ia_state_|= STATE_SYSTEM_SELECTED; ia_state_|= STATE_SYSTEM_SELECTED;
if ((state_ >> WebAccessibility::STATE_TRAVERSED) & 1) if (HasState(WebAccessibility::STATE_TRAVERSED))
ia_state_|= STATE_SYSTEM_TRAVERSED; ia_state_|= STATE_SYSTEM_TRAVERSED;
if ((state_ >> WebAccessibility::STATE_UNAVAILABLE) & 1) if (HasState(WebAccessibility::STATE_UNAVAILABLE))
ia_state_|= STATE_SYSTEM_UNAVAILABLE; ia_state_|= STATE_SYSTEM_UNAVAILABLE;
if ((state_ >> WebAccessibility::STATE_VERTICAL) & 1) { if (HasState(WebAccessibility::STATE_VERTICAL)) {
ia2_state_|= IA2_STATE_VERTICAL; ia2_state_|= IA2_STATE_VERTICAL;
} else { } else {
ia2_state_|= IA2_STATE_HORIZONTAL; ia2_state_|= IA2_STATE_HORIZONTAL;
} }
if ((state_ >> WebAccessibility::STATE_VISITED) & 1) if (HasState(WebAccessibility::STATE_VISITED))
ia_state_|= STATE_SYSTEM_TRAVERSED; ia_state_|= STATE_SYSTEM_TRAVERSED;
// The meaning of the readonly state on Windows is very different from // The meaning of the readonly state on Windows is very different from
...@@ -2788,6 +2901,11 @@ void BrowserAccessibilityWin::InitRoleAndState() { ...@@ -2788,6 +2901,11 @@ void BrowserAccessibilityWin::InitRoleAndState() {
break; break;
case WebAccessibility::ROLE_LISTBOX_OPTION: case WebAccessibility::ROLE_LISTBOX_OPTION:
ia_role_ = ROLE_SYSTEM_LISTITEM; 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; break;
case WebAccessibility::ROLE_LIST_ITEM: case WebAccessibility::ROLE_LIST_ITEM:
ia_role_ = ROLE_SYSTEM_LISTITEM; ia_role_ = ROLE_SYSTEM_LISTITEM;
......
...@@ -194,6 +194,10 @@ BrowserAccessibilityWin ...@@ -194,6 +194,10 @@ BrowserAccessibilityWin
IAccessibleRelation** relations, IAccessibleRelation** relations,
LONG* n_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. // IAccessible2 methods not implemented.
CONTENT_EXPORT STDMETHODIMP get_extendedRole(BSTR* extended_role) { CONTENT_EXPORT STDMETHODIMP get_extendedRole(BSTR* extended_role) {
return E_NOTIMPL; return E_NOTIMPL;
...@@ -207,11 +211,6 @@ BrowserAccessibilityWin ...@@ -207,11 +211,6 @@ BrowserAccessibilityWin
LONG y) { LONG y) {
return E_NOTIMPL; 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( CONTENT_EXPORT STDMETHODIMP get_localizedExtendedRole(
BSTR* localized_extended_role) { BSTR* localized_extended_role) {
return E_NOTIMPL; return E_NOTIMPL;
...@@ -722,6 +721,9 @@ BrowserAccessibilityWin ...@@ -722,6 +721,9 @@ BrowserAccessibilityWin
// is initialized again but the text doesn't change. // is initialized again but the text doesn't change.
string16 old_text_; 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. // Relationships between this node and other nodes.
std::vector<BrowserAccessibilityRelation*> relations_; std::vector<BrowserAccessibilityRelation*> relations_;
......
...@@ -514,7 +514,8 @@ bool RendererAccessibility::ShouldIncludeChildren( ...@@ -514,7 +514,8 @@ bool RendererAccessibility::ShouldIncludeChildren(
WebKit::WebAccessibilityNotification type = notification.type; WebKit::WebAccessibilityNotification type = notification.type;
if (type == WebKit::WebAccessibilityNotificationChildrenChanged || if (type == WebKit::WebAccessibilityNotificationChildrenChanged ||
type == WebKit::WebAccessibilityNotificationLoadComplete || type == WebKit::WebAccessibilityNotificationLoadComplete ||
type == WebKit::WebAccessibilityNotificationLiveRegionChanged) { type == WebKit::WebAccessibilityNotificationLiveRegionChanged ||
type == WebKit::WebAccessibilityNotificationSelectedChildrenChanged) {
return true; return true;
} }
return false; return false;
......
...@@ -813,6 +813,10 @@ void WebAccessibility::Init(const WebKit::WebAccessibilityObject& src, ...@@ -813,6 +813,10 @@ void WebAccessibility::Init(const WebKit::WebAccessibilityObject& src,
if (role == ROLE_SLIDER) if (role == ROLE_SLIDER)
include_children = false; include_children = false;
// Treat the active list box item as focused.
if (role == ROLE_LISTBOX_OPTION && src.isSelectedOptionActive())
state |= (1 << WebAccessibility::STATE_FOCUSED);
WebKit::WebNode node = src.node(); WebKit::WebNode node = src.node();
bool is_iframe = false; bool is_iframe = false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment