summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorctguil@chromium.org <ctguil@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-31 11:54:31 +0000
committerctguil@chromium.org <ctguil@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-31 11:54:31 +0000
commit520cb7d74997ad71aa26cbb842dc7eec75be4f49 (patch)
tree01761cfd3fb461bd60d612138e77c8a1c4dfdb2b
parent4a188ca7f4aeb5b84187029bc2b61dcbee1797a8 (diff)
downloadchromium_src-520cb7d74997ad71aa26cbb842dc7eec75be4f49.zip
chromium_src-520cb7d74997ad71aa26cbb842dc7eec75be4f49.tar.gz
chromium_src-520cb7d74997ad71aa26cbb842dc7eec75be4f49.tar.bz2
Update browser cache of accessibility tree on renderer sub-tree changes.
WebKit sister patch: http://trac.webkit.org/changeset/66305 BUG=13291 TEST=interactive_ui_tests --gtest_filter=AccessibilityWinBrowserTest.TestDynamicAccessibilityTree TEST=unit_tests --gtest_filter=BrowserAccessibilityTest.TestChildrenChange TEST=unit_tests --gtest_filter=BrowserAccessibilityTest.TestChildrenChangeNoLeaks Review URL: http://codereview.chromium.org/3117036 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57983 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/accessibility_win_browsertest.cc115
-rw-r--r--chrome/browser/browser_accessibility_manager_win.cc77
-rw-r--r--chrome/browser/browser_accessibility_manager_win.h17
-rw-r--r--chrome/browser/browser_accessibility_win.cc16
-rw-r--r--chrome/browser/browser_accessibility_win.h10
-rw-r--r--chrome/browser/browser_accessibility_win_unittest.cc157
-rw-r--r--chrome/browser/renderer_host/render_view_host.cc14
-rw-r--r--chrome/browser/renderer_host/render_view_host.h2
-rw-r--r--chrome/browser/renderer_host/render_widget_host.cc4
-rw-r--r--chrome/browser/renderer_host/render_widget_host.h3
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view.h5
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_win.cc12
-rw-r--r--chrome/browser/renderer_host/render_widget_host_view_win.h3
-rw-r--r--chrome/common/notification_type.h6
-rw-r--r--chrome/common/render_messages_internal.h28
-rw-r--r--chrome/renderer/render_view.cc32
-rw-r--r--chrome/renderer/render_view.h5
17 files changed, 455 insertions, 51 deletions
diff --git a/chrome/browser/accessibility_win_browsertest.cc b/chrome/browser/accessibility_win_browsertest.cc
index 4ee4236..7b2d377 100644
--- a/chrome/browser/accessibility_win_browsertest.cc
+++ b/chrome/browser/accessibility_win_browsertest.cc
@@ -15,6 +15,10 @@
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
+using std::auto_ptr;
+using std::vector;
+using std::wstring;
+
namespace {
class AccessibilityWinBrowserTest : public InProcessBrowserTest {
@@ -57,8 +61,14 @@ void AccessibilityWinBrowserTest::TearDownInProcessBrowserTestFixture() {
class AccessibleChecker {
public:
- AccessibleChecker(std::wstring expected_name, int32 expected_role);
- AccessibleChecker(std::wstring expected_name, std::wstring expected_role);
+ AccessibleChecker(
+ wstring expected_name,
+ int32 expected_role,
+ wstring expected_value);
+ AccessibleChecker(
+ wstring expected_name,
+ wstring expected_role,
+ wstring expected_value);
// Append an AccessibleChecker that verifies accessibility information for
// a child IAccessible. Order is important.
@@ -69,20 +79,27 @@ class AccessibleChecker {
// initialized with.
void CheckAccessible(IAccessible* accessible);
- typedef std::vector<AccessibleChecker*> AccessibleCheckerVector;
+ // Set the expected value for this AccessibleChecker.
+ void SetExpectedValue(wstring expected_value);
private:
void CheckAccessibleName(IAccessible* accessible);
void CheckAccessibleRole(IAccessible* accessible);
+ void CheckAccessibleValue(IAccessible* accessible);
void CheckAccessibleChildren(IAccessible* accessible);
private:
+ typedef vector<AccessibleChecker*> AccessibleCheckerVector;
+
// Expected accessible name. Checked against IAccessible::get_accName.
- std::wstring name_;
+ wstring name_;
// Expected accessible role. Checked against IAccessible::get_accRole.
CComVariant role_;
+ // Expected accessible value. Checked against IAccessible::get_accValue.
+ wstring value_;
+
// Expected accessible children. Checked using IAccessible::get_accChildCount
// and ::AccessibleChildren.
AccessibleCheckerVector children_;
@@ -134,15 +151,17 @@ AccessibilityWinBrowserTest::GetRenderWidgetHostViewClientAccessible() {
}
AccessibleChecker::AccessibleChecker(
- std::wstring expected_name, int32 expected_role) :
+ wstring expected_name, int32 expected_role, wstring expected_value) :
name_(expected_name),
- role_(expected_role) {
+ role_(expected_role),
+ value_(expected_value) {
}
AccessibleChecker::AccessibleChecker(
- std::wstring expected_name, std::wstring expected_role) :
+ wstring expected_name, wstring expected_role, wstring expected_value) :
name_(expected_name),
- role_(expected_role.c_str()) {
+ role_(expected_role.c_str()),
+ value_(expected_value) {
}
void AccessibleChecker::AppendExpectedChild(
@@ -153,9 +172,14 @@ void AccessibleChecker::AppendExpectedChild(
void AccessibleChecker::CheckAccessible(IAccessible* accessible) {
CheckAccessibleName(accessible);
CheckAccessibleRole(accessible);
+ CheckAccessibleValue(accessible);
CheckAccessibleChildren(accessible);
}
+void AccessibleChecker::SetExpectedValue(wstring expected_value) {
+ value_ = expected_value;
+}
+
void AccessibleChecker::CheckAccessibleName(IAccessible* accessible) {
CComBSTR name;
HRESULT hr =
@@ -167,9 +191,8 @@ void AccessibleChecker::CheckAccessibleName(IAccessible* accessible) {
} else {
// Test that the correct string was returned.
EXPECT_EQ(hr, S_OK);
- EXPECT_EQ(CompareString(LOCALE_NEUTRAL, 0, name, SysStringLen(name),
- name_.c_str(), name_.length()),
- CSTR_EQUAL);
+ EXPECT_STREQ(name_.c_str(),
+ wstring(name.m_str, SysStringLen(name)).c_str());
}
}
@@ -181,13 +204,24 @@ void AccessibleChecker::CheckAccessibleRole(IAccessible* accessible) {
ASSERT_TRUE(role_ == var_role);
}
+void AccessibleChecker::CheckAccessibleValue(IAccessible* accessible) {
+ CComBSTR value;
+ HRESULT hr =
+ accessible->get_accValue(CreateI4Variant(CHILDID_SELF), &value);
+ EXPECT_EQ(hr, S_OK);
+
+ // Test that the correct string was returned.
+ EXPECT_STREQ(value_.c_str(),
+ wstring(value.m_str, SysStringLen(value)).c_str());
+}
+
void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) {
LONG child_count = 0;
HRESULT hr = parent->get_accChildCount(&child_count);
EXPECT_EQ(hr, S_OK);
ASSERT_EQ(child_count, children_.size());
- std::auto_ptr<VARIANT> child_array(new VARIANT[child_count]);
+ auto_ptr<VARIANT> child_array(new VARIANT[child_count]);
LONG obtained_count = 0;
hr = AccessibleChildren(parent, 0, child_count,
child_array.get(), &obtained_count);
@@ -235,14 +269,14 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
document_accessible = GetRenderWidgetHostViewClientAccessible();
ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
- AccessibleChecker button_checker(L"push", ROLE_SYSTEM_PUSHBUTTON);
- AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON);
+ AccessibleChecker button_checker(L"push", ROLE_SYSTEM_PUSHBUTTON, L"push");
+ AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON, L"");
- AccessibleChecker grouping_checker(L"", L"div");
+ AccessibleChecker grouping_checker(L"", L"div", L"");
grouping_checker.AppendExpectedChild(&button_checker);
grouping_checker.AppendExpectedChild(&checkbox_checker);
- AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT);
+ AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
document_checker.AppendExpectedChild(&grouping_checker);
// Check the accessible tree of the renderer.
@@ -261,13 +295,52 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
// Verify that the IAccessible reference still points to a valid object and
// that calls to its methods fail since the tree is no longer valid after
// the page navagation.
- // Todo(ctguil): Currently this is giving a false positive because E_FAIL is
- // returned when BrowserAccessibilityManager::RequestAccessibilityInfo fails
- // since the previous render view host connection is lost. Verify that
- // instances are actually marked as invalid once the browse side cache is
- // checked in.
CComBSTR name;
hr = document_accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name);
ASSERT_EQ(E_FAIL, hr);
}
+
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
+ TestDynamicAccessibilityTree) {
+ // By requesting an accessible chrome will believe a screen reader has been
+ // detected. Request and wait for the accessibility tree to be updated.
+ GURL tree_url(
+ "data:text/html,<html><body><div onclick=\"this.innerHTML='<b>new text"
+ "</b>';\"><b>old text</b></div></body></html>");
+ browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
+ ScopedComPtr<IAccessible> document_accessible(
+ GetRenderWidgetHostViewClientAccessible());
+ ui_test_utils::WaitForNotification(
+ NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
+
+ AccessibleChecker text_checker(L"", ROLE_SYSTEM_TEXT, L"old text");
+ AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON, L"");
+
+ AccessibleChecker div_checker(L"", L"div", L"");
+ div_checker.AppendExpectedChild(&text_checker);
+
+ AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
+ document_checker.AppendExpectedChild(&div_checker);
+
+ // Check the accessible tree of the browser.
+ document_accessible = GetRenderWidgetHostViewClientAccessible();
+ ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
+ document_checker.CheckAccessible(document_accessible);
+
+ // Perform the default action on the div which executes the script that
+ // updates text node within the div.
+ CComPtr<IDispatch> div_dispatch;
+ HRESULT hr = document_accessible->get_accChild(CreateI4Variant(1),
+ &div_dispatch);
+ EXPECT_EQ(hr, S_OK);
+ CComQIPtr<IAccessible> div_accessible(div_dispatch);
+ hr = div_accessible->accDoDefaultAction(CreateI4Variant(CHILDID_SELF));
+ EXPECT_EQ(hr, S_OK);
+ ui_test_utils::WaitForNotification(
+ NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
+
+ // Check that the accessibility tree of the browser has been updated.
+ text_checker.SetExpectedValue(L"new text");
+ document_checker.CheckAccessible(document_accessible);
+}
} // namespace.
diff --git a/chrome/browser/browser_accessibility_manager_win.cc b/chrome/browser/browser_accessibility_manager_win.cc
index c1ce8a7..d17a0bd 100644
--- a/chrome/browser/browser_accessibility_manager_win.cc
+++ b/chrome/browser/browser_accessibility_manager_win.cc
@@ -39,7 +39,7 @@ BrowserAccessibilityManager::BrowserAccessibilityManager(
parent_hwnd_, OBJID_WINDOW, IID_IAccessible,
reinterpret_cast<void **>(&window_iaccessible_));
DCHECK(SUCCEEDED(hr));
- root_ = CreateAccessibilityTree(NULL, src, 0);
+ root_ = CreateAccessibilityTree(NULL, GetNextChildID(), src, 0);
if (!focus_)
focus_ = root_;
}
@@ -57,6 +57,10 @@ BrowserAccessibility* BrowserAccessibilityManager::GetRoot() {
return root_;
}
+void BrowserAccessibilityManager::Remove(LONG child_id) {
+ child_id_map_.erase(child_id);
+}
+
BrowserAccessibility* BrowserAccessibilityManager::GetFromChildID(
LONG child_id) {
base::hash_map<LONG, BrowserAccessibility*>::iterator iter =
@@ -117,24 +121,79 @@ void BrowserAccessibilityManager::OnAccessibilityObjectStateChange(
return;
LONG child_id = iter->second;
- ::NotifyWinEvent(EVENT_OBJECT_FOCUS, parent_hwnd_, OBJID_CLIENT, child_id);
+ ::NotifyWinEvent(
+ EVENT_OBJECT_STATECHANGE,
+ parent_hwnd_,
+ OBJID_CLIENT,
+ child_id);
}
-BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree(
- BrowserAccessibility* parent,
- const webkit_glue::WebAccessibility& src,
- int index_in_parent) {
- BrowserAccessibility* instance = factory_->Create();
+void BrowserAccessibilityManager::OnAccessibilityObjectChildrenChange(
+ const std::vector<webkit_glue::WebAccessibility>& acc_changes) {
+ if (delegate_)
+ delegate_->AccessibilityObjectChildrenChangeAck();
+
+ // For each accessibility object child change.
+ for (unsigned int index = 0; index < acc_changes.size(); index++) {
+ const webkit_glue::WebAccessibility& acc_obj = acc_changes[index];
+ base::hash_map<int, LONG>::iterator iter =
+ renderer_id_to_child_id_map_.find(acc_obj.id);
+ if (iter == renderer_id_to_child_id_map_.end())
+ continue;
+
+ LONG child_id = iter->second;
+ BrowserAccessibility* old_browser_acc = GetFromChildID(child_id);
+
+ BrowserAccessibility* new_browser_acc = CreateAccessibilityTree(
+ old_browser_acc->GetParent(),
+ child_id,
+ acc_obj,
+ old_browser_acc->index_in_parent());
+
+ if (focus_->IsDescendantOf(old_browser_acc))
+ focus_ = new_browser_acc;
+
+ if (old_browser_acc->GetParent()) {
+ old_browser_acc->GetParent()->ReplaceChild(
+ old_browser_acc,
+ new_browser_acc);
+ } else {
+ DCHECK_EQ(old_browser_acc, root_);
+ root_ = new_browser_acc;
+ }
+ old_browser_acc->InactivateTree();
+ old_browser_acc->Release();
+ child_id_map_[child_id] = new_browser_acc;
+
+ if (root_ != new_browser_acc) {
+ NotifyWinEvent(
+ EVENT_OBJECT_REORDER, parent_hwnd_, OBJID_CLIENT, child_id);
+ } else {
+ NotifyWinEvent(
+ EVENT_OBJECT_REORDER, parent_hwnd_, OBJID_CLIENT, CHILDID_SELF);
+ }
+ }
+}
+LONG BrowserAccessibilityManager::GetNextChildID() {
// Get the next child ID, and wrap around when we get near the end
// of a 32-bit integer range. It's okay to wrap around; we just want
// to avoid it as long as possible because clients may cache the ID of
// an object for a while to determine if they've seen it before.
- LONG child_id = next_child_id_;
next_child_id_--;
if (next_child_id_ == -2000000000)
next_child_id_ = -1;
+ return next_child_id_;
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree(
+ BrowserAccessibility* parent,
+ int child_id,
+ const webkit_glue::WebAccessibility& src,
+ int index_in_parent) {
+ BrowserAccessibility* instance = factory_->Create();
+
instance->Initialize(this, parent, child_id, index_in_parent, src);
child_id_map_[child_id] = instance;
renderer_id_to_child_id_map_[src.id] = child_id;
@@ -142,7 +201,7 @@ BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree(
focus_ = instance;
for (int i = 0; i < static_cast<int>(src.children.size()); ++i) {
BrowserAccessibility* child = CreateAccessibilityTree(
- instance, src.children[i], i);
+ instance, GetNextChildID(), src.children[i], i);
instance->AddChild(child);
}
diff --git a/chrome/browser/browser_accessibility_manager_win.h b/chrome/browser/browser_accessibility_manager_win.h
index 32a8843..838bbda 100644
--- a/chrome/browser/browser_accessibility_manager_win.h
+++ b/chrome/browser/browser_accessibility_manager_win.h
@@ -10,6 +10,9 @@
#include <atlcom.h>
#include <oleacc.h>
+#include <hash_map>
+#include <vector>
+
#include "base/hash_tables.h"
#include "base/scoped_comptr_win.h"
#include "base/scoped_ptr.h"
@@ -32,6 +35,7 @@ class BrowserAccessibilityDelegate {
virtual ~BrowserAccessibilityDelegate() {}
virtual void SetAccessibilityFocus(int acc_obj_id) = 0;
virtual void AccessibilityDoDefaultAction(int acc_obj_id) = 0;
+ virtual void AccessibilityObjectChildrenChangeAck() = 0;
};
// Manages a tree of BrowserAccessibility objects.
@@ -48,6 +52,9 @@ class BrowserAccessibilityManager {
// Return a pointer to the root of the tree, does not make a new reference.
BrowserAccessibility* GetRoot();
+ // Removes the BrowserAccessibility child_id from the manager.
+ void Remove(LONG child_id);
+
// Return a pointer to the object corresponding to the given child_id,
// does not make a new reference.
BrowserAccessibility* GetFromChildID(LONG child_id);
@@ -69,16 +76,22 @@ class BrowserAccessibilityManager {
// Tell the renderer to do the default action for this node.
void DoDefaultAction(const BrowserAccessibility& node);
- // Called when the renderer process has notified us of a focus or state
- // change. Send a notification to MSAA clients of the change.
+ // Called when the renderer process has notified us of a focus, state,
+ // or children change. Send a notification to MSAA clients of the change.
void OnAccessibilityFocusChange(int acc_obj_id);
void OnAccessibilityObjectStateChange(int acc_obj_id);
+ void OnAccessibilityObjectChildrenChange(
+ const std::vector<webkit_glue::WebAccessibility>& acc_changes);
private:
+ // Returns the next MSAA child id.
+ static LONG GetNextChildID();
+
// Recursively build a tree of BrowserAccessibility objects from
// the WebAccessibility tree received from the renderer process.
BrowserAccessibility* CreateAccessibilityTree(
BrowserAccessibility* parent,
+ int child_id,
const webkit_glue::WebAccessibility& src,
int index_in_parent);
diff --git a/chrome/browser/browser_accessibility_win.cc b/chrome/browser/browser_accessibility_win.cc
index aed9bbc..06a67a5 100644
--- a/chrome/browser/browser_accessibility_win.cc
+++ b/chrome/browser/browser_accessibility_win.cc
@@ -55,6 +55,9 @@ void BrowserAccessibility::AddChild(BrowserAccessibility* child) {
}
void BrowserAccessibility::InactivateTree() {
+ if (!instance_active_)
+ return;
+
// Mark this object as inactive, so calls to all COM methods will return
// failure.
instance_active_ = false;
@@ -70,6 +73,7 @@ void BrowserAccessibility::InactivateTree() {
(*iter)->Release();
}
children_.clear();
+ manager_->Remove(child_id_);
}
bool BrowserAccessibility::IsDescendantOf(BrowserAccessibility* ancestor) {
@@ -82,6 +86,10 @@ bool BrowserAccessibility::IsDescendantOf(BrowserAccessibility* ancestor) {
return false;
}
+BrowserAccessibility* BrowserAccessibility::GetParent() {
+ return parent_;
+}
+
BrowserAccessibility* BrowserAccessibility::GetPreviousSibling() {
if (parent_ && index_in_parent_ > 0)
return parent_->children_[index_in_parent_ - 1];
@@ -98,6 +106,14 @@ BrowserAccessibility* BrowserAccessibility::GetNextSibling() {
return NULL;
}
+void BrowserAccessibility::ReplaceChild(
+ const BrowserAccessibility* old_acc, BrowserAccessibility* new_acc) {
+ DCHECK_EQ(children_[old_acc->index_in_parent_], old_acc);
+
+ old_acc = children_[old_acc->index_in_parent_];
+ children_[old_acc->index_in_parent_] = new_acc;
+}
+
BrowserAccessibility* BrowserAccessibility::NewReference() {
AddRef();
return this;
diff --git a/chrome/browser/browser_accessibility_win.h b/chrome/browser/browser_accessibility_win.h
index f66a6fb..17f7107 100644
--- a/chrome/browser/browser_accessibility_win.h
+++ b/chrome/browser/browser_accessibility_win.h
@@ -67,6 +67,10 @@ class ATL_NO_VTABLE BrowserAccessibility
// Return true if this object is equal to or a descendant of |ancestor|.
bool IsDescendantOf(BrowserAccessibility* ancestor);
+ // Returns the parent of this object, or NULL if it's the BrowserAccessibility
+ // root.
+ BrowserAccessibility* GetParent();
+
// Return the previous sibling of this object, or NULL if it's the first
// child of its parent.
BrowserAccessibility* GetPreviousSibling();
@@ -75,9 +79,15 @@ class ATL_NO_VTABLE BrowserAccessibility
// of its parent.
BrowserAccessibility* GetNextSibling();
+ // Replace a child BrowserAccessibility object. Used when updating the
+ // accessibility tree.
+ void ReplaceChild(
+ const BrowserAccessibility* old_acc, BrowserAccessibility* new_acc);
+
// Accessors
LONG child_id() const { return child_id_; }
int32 renderer_id() const { return renderer_id_; }
+ LONG index_in_parent() const { return index_in_parent_; }
// Add one to the reference count and return the same object. Always
// use this method when returning a BrowserAccessibility object as
diff --git a/chrome/browser/browser_accessibility_win_unittest.cc b/chrome/browser/browser_accessibility_win_unittest.cc
index 6d85671..95a89eb 100644
--- a/chrome/browser/browser_accessibility_win_unittest.cc
+++ b/chrome/browser/browser_accessibility_win_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/scoped_ptr.h"
+#include "base/scoped_comptr_win.h"
#include "chrome/browser/browser_accessibility_manager_win.h"
#include "chrome/browser/browser_accessibility_win.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -32,15 +33,35 @@ class CountedBrowserAccessibilityFactory : public BrowserAccessibilityFactory {
}
};
+VARIANT CreateI4Variant(LONG value) {
+ VARIANT variant = {0};
+
+ V_VT(&variant) = VT_I4;
+ V_I4(&variant) = value;
+
+ return variant;
+}
+
+class BrowserAccessibilityTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // ATL needs a pointer to a COM module.
+ static CComModule module;
+ _pAtlModule = &module;
+
+ // Make sure COM is initialized for this thread; it's safe to call twice.
+ ::CoInitialize(NULL);
+ }
+
+ virtual void TearDown()
+ {
+ ::CoUninitialize();
+ }
+};
+
// Test that BrowserAccessibilityManager correctly releases the tree of
// BrowserAccessibility instances upon delete.
-TEST(BrowserAccessibilityTest, TestNoLeaks) {
- // ATL needs a pointer to a COM module.
- CComModule module;
- _pAtlModule = &module;
- // Make sure COM is initialized for this thread; it's safe to call twice.
- ::CoInitialize(NULL);
-
+TEST_F(BrowserAccessibilityTest, TestNoLeaks) {
// Create WebAccessibility objects for a simple document tree,
// representing the accessibility information used to initialize
// BrowserAccessibilityManager.
@@ -109,3 +130,125 @@ TEST(BrowserAccessibilityTest, TestNoLeaks) {
child1_iaccessible->Release();
ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
}
+
+TEST_F(BrowserAccessibilityTest, TestChildrenChange) {
+ // Create WebAccessibility objects for a simple document tree,
+ // representing the accessibility information used to initialize
+ // BrowserAccessibilityManager.
+ WebAccessibility text;
+ text.id = 2;
+ text.role = WebAccessibility::ROLE_STATIC_TEXT;
+ text.value = L"old text";
+ text.state = 0;
+
+ WebAccessibility root;
+ root.id = 1;
+ root.name = L"Document";
+ root.role = WebAccessibility::ROLE_DOCUMENT;
+ root.state = 0;
+ root.children.push_back(text);
+
+ // Construct a BrowserAccessibilityManager with this WebAccessibility tree
+ // and a factory for an instance-counting BrowserAccessibility.
+ CountedBrowserAccessibility::global_obj_count_ = 0;
+ BrowserAccessibilityManager* manager =
+ new BrowserAccessibilityManager(
+ GetDesktopWindow(), root, NULL,
+ new CountedBrowserAccessibilityFactory());
+
+ // Query for the text IAccessible and verify that it returns "old text" as its
+ // value.
+ ScopedComPtr<IDispatch> text_dispatch;
+ HRESULT hr = manager->GetRoot()->get_accChild(CreateI4Variant(1),
+ text_dispatch.Receive());
+ ASSERT_EQ(S_OK, hr);
+
+ ScopedComPtr<IAccessible> text_accessible;
+ hr = text_dispatch.QueryInterface(text_accessible.Receive());
+ ASSERT_EQ(S_OK, hr);
+
+ CComBSTR value;
+ hr = text_accessible->get_accValue(CreateI4Variant(CHILDID_SELF), &value);
+ ASSERT_EQ(S_OK, hr);
+ EXPECT_STREQ(L"old text", value.m_str);
+
+ text_dispatch.Release();
+ text_accessible.Release();
+
+ // Notify the BrowserAccessibilityManager that the text child has changed.
+ text.value = L"new text";
+ std::vector<WebAccessibility> acc_changes;
+ acc_changes.push_back(text);
+ manager->OnAccessibilityObjectChildrenChange(acc_changes);
+
+ // Query for the text IAccessible and verify that it now returns "new text"
+ // as its value.
+ hr = manager->GetRoot()->get_accChild(
+ CreateI4Variant(1),
+ text_dispatch.Receive());
+ ASSERT_EQ(S_OK, hr);
+
+ hr = text_dispatch.QueryInterface(text_accessible.Receive());
+ ASSERT_EQ(S_OK, hr);
+
+ hr = text_accessible->get_accValue(CreateI4Variant(CHILDID_SELF), &value);
+ ASSERT_EQ(S_OK, hr);
+ EXPECT_STREQ(L"new text", value.m_str);
+
+ text_dispatch.Release();
+ text_accessible.Release();
+
+ // Delete the manager and test that all BrowserAccessibility instances are
+ // deleted.
+ delete manager;
+ ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
+}
+
+TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) {
+ // Create WebAccessibility objects for a simple document tree,
+ // representing the accessibility information used to initialize
+ // BrowserAccessibilityManager.
+ WebAccessibility text;
+ text.id = 3;
+ text.role = WebAccessibility::ROLE_STATIC_TEXT;
+ text.state = 0;
+
+ WebAccessibility div;
+ div.id = 2;
+ div.role = WebAccessibility::ROLE_GROUP;
+ div.state = 0;
+
+ div.children.push_back(text);
+ text.id = 4;
+ div.children.push_back(text);
+
+ WebAccessibility root;
+ root.id = 1;
+ root.role = WebAccessibility::ROLE_DOCUMENT;
+ root.state = 0;
+ root.children.push_back(div);
+
+ // Construct a BrowserAccessibilityManager with this WebAccessibility tree
+ // and a factory for an instance-counting BrowserAccessibility and ensure
+ // that exactly 4 instances were created. Note that the manager takes
+ // ownership of the factory.
+ CountedBrowserAccessibility::global_obj_count_ = 0;
+ BrowserAccessibilityManager* manager =
+ new BrowserAccessibilityManager(
+ GetDesktopWindow(), root, NULL,
+ new CountedBrowserAccessibilityFactory());
+ ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
+
+ // Notify the BrowserAccessibilityManager that the div node and its children
+ // were removed and ensure that only one BrowserAccessibility instance exists.
+ root.children.clear();
+ std::vector<WebAccessibility> acc_changes;
+ acc_changes.push_back(root);
+ manager->OnAccessibilityObjectChildrenChange(acc_changes);
+ ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_);
+
+ // Delete the manager and test that all BrowserAccessibility instances are
+ // deleted.
+ delete manager;
+ ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
+}
diff --git a/chrome/browser/renderer_host/render_view_host.cc b/chrome/browser/renderer_host/render_view_host.cc
index 70c6b96..9850ab1 100644
--- a/chrome/browser/renderer_host/render_view_host.cc
+++ b/chrome/browser/renderer_host/render_view_host.cc
@@ -837,6 +837,8 @@ void RenderViewHost::OnMessageReceived(const IPC::Message& msg) {
OnAccessibilityFocusChange)
IPC_MESSAGE_HANDLER(ViewHostMsg_AccessibilityObjectStateChange,
OnAccessibilityObjectStateChange)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_AccessibilityObjectChildrenChange,
+ OnAccessibilityObjectChildrenChange)
IPC_MESSAGE_HANDLER(ViewHostMsg_OnCSSInserted, OnCSSInserted)
IPC_MESSAGE_HANDLER(ViewHostMsg_PageContents, OnPageContents)
IPC_MESSAGE_HANDLER(ViewHostMsg_PageTranslated, OnPageTranslated)
@@ -1970,6 +1972,18 @@ void RenderViewHost::OnAccessibilityObjectStateChange(int acc_obj_id) {
view()->OnAccessibilityObjectStateChange(acc_obj_id);
}
+void RenderViewHost::OnAccessibilityObjectChildrenChange(
+ const std::vector<webkit_glue::WebAccessibility>& acc_changes) {
+ view()->OnAccessibilityObjectChildrenChange(acc_changes);
+
+ if (acc_changes.size() > 0) {
+ NotificationService::current()->Notify(
+ NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED,
+ Source<RenderViewHost>(this),
+ NotificationService::NoDetails());
+ }
+}
+
void RenderViewHost::OnAccessibilityTree(
const webkit_glue::WebAccessibility& tree) {
if (view())
diff --git a/chrome/browser/renderer_host/render_view_host.h b/chrome/browser/renderer_host/render_view_host.h
index 3010910..1d92cd9 100644
--- a/chrome/browser/renderer_host/render_view_host.h
+++ b/chrome/browser/renderer_host/render_view_host.h
@@ -654,6 +654,8 @@ class RenderViewHost : public RenderWidgetHost {
void OnExtensionPostMessage(int port_id, const std::string& message);
void OnAccessibilityFocusChange(int acc_obj_id);
void OnAccessibilityObjectStateChange(int acc_obj_id);
+ void OnAccessibilityObjectChildrenChange(
+ const std::vector<webkit_glue::WebAccessibility>& acc_changes);
void OnAccessibilityTree(const webkit_glue::WebAccessibility& tree);
void OnCSSInserted();
void OnPageContents(const GURL& url,
diff --git a/chrome/browser/renderer_host/render_widget_host.cc b/chrome/browser/renderer_host/render_widget_host.cc
index 0a06602..30766ea 100644
--- a/chrome/browser/renderer_host/render_widget_host.cc
+++ b/chrome/browser/renderer_host/render_widget_host.cc
@@ -1166,6 +1166,10 @@ void RenderWidgetHost::AccessibilityDoDefaultAction(int acc_obj_id) {
Send(new ViewMsg_AccessibilityDoDefaultAction(routing_id(), acc_obj_id));
}
+void RenderWidgetHost::AccessibilityObjectChildrenChangeAck() {
+ Send(new ViewMsg_AccessibilityObjectChildrenChange_ACK(routing_id()));
+}
+
void RenderWidgetHost::ProcessKeyboardEventAck(int type, bool processed) {
if (key_queue_.size() == 0) {
LOG(ERROR) << "Got a KeyEvent back from the renderer but we "
diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h
index eac130e..ad31f2e 100644
--- a/chrome/browser/renderer_host/render_widget_host.h
+++ b/chrome/browser/renderer_host/render_widget_host.h
@@ -385,6 +385,9 @@ class RenderWidgetHost : public IPC::Channel::Listener,
// on a node with this accessibility object id.
void AccessibilityDoDefaultAction(int acc_obj_id);
+ // Acknowledges a ViewHostMsg_AccessibilityObjectChildrenChange message.
+ void AccessibilityObjectChildrenChangeAck();
+
// Sets the active state (i.e., control tints).
virtual void SetActive(bool active);
diff --git a/chrome/browser/renderer_host/render_widget_host_view.h b/chrome/browser/renderer_host/render_widget_host_view.h
index d651943..d4c09de 100644
--- a/chrome/browser/renderer_host/render_widget_host_view.h
+++ b/chrome/browser/renderer_host/render_widget_host_view.h
@@ -10,6 +10,9 @@
#include <OpenGL/OpenGL.h>
#endif
+#include <string>
+#include <vector>
+
#include "app/surface/transport_dib.h"
#include "gfx/native_widget_types.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -250,6 +253,8 @@ class RenderWidgetHostView {
const webkit_glue::WebAccessibility& tree) { }
virtual void OnAccessibilityFocusChange(int acc_obj_id) { }
virtual void OnAccessibilityObjectStateChange(int acc_obj_id) { }
+ virtual void OnAccessibilityObjectChildrenChange(
+ const std::vector<webkit_glue::WebAccessibility>& acc_changes) { }
protected:
// Interface class only, do not construct.
diff --git a/chrome/browser/renderer_host/render_widget_host_view_win.cc b/chrome/browser/renderer_host/render_widget_host_view_win.cc
index ac1be0b..defdc61 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_win.cc
+++ b/chrome/browser/renderer_host/render_widget_host_view_win.cc
@@ -1511,6 +1511,14 @@ void RenderWidgetHostViewWin::OnAccessibilityObjectStateChange(int acc_obj_id) {
}
}
+void RenderWidgetHostViewWin::OnAccessibilityObjectChildrenChange(
+ const std::vector<webkit_glue::WebAccessibility>& acc_changes) {
+ if (browser_accessibility_manager_.get()) {
+ browser_accessibility_manager_->OnAccessibilityObjectChildrenChange(
+ acc_changes);
+ }
+}
+
void RenderWidgetHostViewWin::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
@@ -1553,6 +1561,10 @@ void RenderWidgetHostViewWin::AccessibilityDoDefaultAction(int acc_obj_id) {
render_widget_host_->AccessibilityDoDefaultAction(acc_obj_id);
}
+void RenderWidgetHostViewWin::AccessibilityObjectChildrenChangeAck() {
+ render_widget_host_->AccessibilityObjectChildrenChangeAck();
+}
+
LRESULT RenderWidgetHostViewWin::OnGetObject(UINT message, WPARAM wparam,
LPARAM lparam, BOOL& handled) {
if (lparam != OBJID_CLIENT) {
diff --git a/chrome/browser/renderer_host/render_widget_host_view_win.h b/chrome/browser/renderer_host/render_widget_host_view_win.h
index 9d17aa6..00f8c38 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_win.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_win.h
@@ -157,6 +157,8 @@ class RenderWidgetHostViewWin
const webkit_glue::WebAccessibility& tree);
virtual void OnAccessibilityFocusChange(int acc_obj_id);
virtual void OnAccessibilityObjectStateChange(int acc_obj_id);
+ virtual void OnAccessibilityObjectChildrenChange(
+ const std::vector<webkit_glue::WebAccessibility>& acc_obj);
// Implementation of NotificationObserver:
virtual void Observe(NotificationType type,
@@ -166,6 +168,7 @@ class RenderWidgetHostViewWin
// Implementation of BrowserAccessibilityDelegate:
virtual void SetAccessibilityFocus(int acc_obj_id);
virtual void AccessibilityDoDefaultAction(int acc_obj_id);
+ virtual void AccessibilityObjectChildrenChangeAck();
protected:
// Windows Message Handlers
diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h
index 08bc47f..df520c6 100644
--- a/chrome/common/notification_type.h
+++ b/chrome/common/notification_type.h
@@ -431,9 +431,9 @@ class NotificationType {
// first RenderViewHost is set).
RENDER_VIEW_HOST_CHANGED,
- // Indicates that the render view host has received a new accessibility tree
- // from the render view. The source is the RenderViewHost, the details
- // are not used.
+ // Indicates that the render view host has received an accessibility tree
+ // update, either partial or full, from the render view. The source is the
+ // RenderViewHost, the details are not used.
RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED,
// This is sent when a RenderWidgetHost is being destroyed. The source is
diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h
index 9d4fa57..97b17d9 100644
--- a/chrome/common/render_messages_internal.h
+++ b/chrome/common/render_messages_internal.h
@@ -990,6 +990,11 @@ IPC_BEGIN_MESSAGES(View)
IPC_MESSAGE_ROUTED1(ViewMsg_AccessibilityDoDefaultAction,
int /* object id */)
+ // Tells the render view that a ViewHostMsg_AccessibilityObjectChildrenChange
+ // message was processed, and the render view host is ready for additional
+ // children change messages.
+ IPC_MESSAGE_ROUTED0(ViewMsg_AccessibilityObjectChildrenChange_ACK)
+
// Relay a speech recognition result, either partial or final.
IPC_MESSAGE_ROUTED2(ViewMsg_SpeechInput_SetRecognitionResult,
int /* request id */,
@@ -2216,12 +2221,22 @@ IPC_BEGIN_MESSAGES(ViewHost)
IPC_MESSAGE_ROUTED1(ViewHostMsg_AccessibilityFocusChange,
int /* accessibility object id */)
- // Send as a result of a state change in the renderer (if accessibility is
+ // Sent as a result of a state change in the renderer (if accessibility is
// enabled), to notify the browser side. Takes the id of the accessibility
// object that had a state change
IPC_MESSAGE_ROUTED1(ViewHostMsg_AccessibilityObjectStateChange,
int /* accessibility object id */)
+ // Sent by the renderer as a result of a accessibility node children change.
+ // The browser responds with a ViewMsg_AccessibilityObjectChildrenChange_ACK.
+ IPC_MESSAGE_ROUTED1(ViewHostMsg_AccessibilityObjectChildrenChange,
+ std::vector<webkit_glue::WebAccessibility>)
+
+ // Send the tree of accessibility data to the browser, where it's cached
+ // in order to respond to OS accessibility queries immediately.
+ IPC_MESSAGE_ROUTED1(ViewHostMsg_AccessibilityTree,
+ webkit_glue::WebAccessibility)
+
// Message sent from the renderer to the browser to request that the browser
// close all sockets. Used for debugging/testing.
IPC_MESSAGE_CONTROL0(ViewHostMsg_CloseCurrentConnections)
@@ -2441,9 +2456,9 @@ IPC_BEGIN_MESSAGES(ViewHost)
int32, /* response_id */
string16 /* name */)
- // WebIDBObjectStore::openCursor() message.
- IPC_MESSAGE_CONTROL1(ViewHostMsg_IDBObjectStoreOpenCursor,
- ViewHostMsg_IDBObjectStoreOpenCursor_Params)
+ // WebIDBObjectStore::openCursor() message.
+ IPC_MESSAGE_CONTROL1(ViewHostMsg_IDBObjectStoreOpenCursor,
+ ViewHostMsg_IDBObjectStoreOpenCursor_Params)
// WebIDBObjectStore::~WebIDBObjectStore() message.
IPC_MESSAGE_CONTROL1(ViewHostMsg_IDBObjectStoreDestroyed,
@@ -2668,11 +2683,6 @@ IPC_BEGIN_MESSAGES(ViewHost)
int /* render_view_id */,
int /* bridge_id */)
- // Send the tree of accessibility data to the browser, where it's cached
- // in order to respond to OS accessibility queries immediately.
- IPC_MESSAGE_ROUTED1(ViewHostMsg_AccessibilityTree,
- webkit_glue::WebAccessibility)
-
// Notifies the TabContents that the content being displayed is PDF.
// This allows the browser to handle things such as zooming differently.
IPC_MESSAGE_ROUTED0(ViewHostMsg_SetDisplayingPDFContent)
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index 5af03eb..49e6934 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -782,6 +782,8 @@ void RenderView::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(ViewMsg_SetAccessibilityFocus, OnSetAccessibilityFocus)
IPC_MESSAGE_HANDLER(ViewMsg_AccessibilityDoDefaultAction,
OnAccessibilityDoDefaultAction)
+ IPC_MESSAGE_HANDLER(ViewMsg_AccessibilityObjectChildrenChange_ACK,
+ OnAccessibilityObjectChildrenChangeAck)
IPC_MESSAGE_HANDLER(ViewMsg_OpenFileSystemRequest_Complete,
OnOpenFileSystemRequestComplete)
@@ -1443,6 +1445,7 @@ void RenderView::UpdateURL(WebFrame* frame) {
accessibility_->clear();
accessibility_.reset();
}
+ accessibility_changes_.clear();
}
// Tell the embedding application that the title of the active page has changed
@@ -4311,6 +4314,7 @@ void RenderView::OnGetAccessibilityTree() {
accessibility_->clear();
accessibility_.reset(WebAccessibilityCache::create());
accessibility_->initialize(webview());
+ accessibility_changes_.clear();
WebAccessibilityObject src_tree = webview()->accessibilityObject();
webkit_glue::WebAccessibility dst_tree(src_tree, accessibility_.get());
@@ -4340,6 +4344,19 @@ void RenderView::OnAccessibilityDoDefaultAction(int acc_obj_id) {
}
}
+void RenderView::OnAccessibilityObjectChildrenChangeAck() {
+ if (!accessibility_.get())
+ return;
+
+ if (!accessibility_changes_.empty()) {
+ Send(new ViewHostMsg_AccessibilityObjectChildrenChange(
+ routing_id_,
+ accessibility_changes_));
+ }
+
+ accessibility_changes_.clear();
+}
+
void RenderView::OnGetAllSavableResourceLinksForCurrentPage(
const GURL& page_url) {
// Prepare list to storage all savable resource links.
@@ -5351,6 +5368,21 @@ void RenderView::didChangeAccessibilityObjectState(
#endif
}
+void RenderView::didChangeAccessibilityObjectChildren(
+ const WebKit::WebAccessibilityObject& acc_obj) {
+ if (!accessibility_.get())
+ return;
+
+ if (accessibility_changes_.empty()) {
+ Send(new ViewHostMsg_AccessibilityObjectChildrenChange(
+ routing_id_,
+ std::vector<webkit_glue::WebAccessibility>()));
+ }
+
+ accessibility_changes_.push_back(
+ webkit_glue::WebAccessibility(acc_obj, accessibility_.get()));
+}
+
void RenderView::Print(WebFrame* frame, bool script_initiated) {
DCHECK(frame);
if (print_helper_.get() == NULL) {
diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h
index 326acac..8e4d807 100644
--- a/chrome/renderer/render_view.h
+++ b/chrome/renderer/render_view.h
@@ -398,6 +398,8 @@ class RenderView : public RenderWidget,
const WebKit::WebAccessibilityObject& acc_obj);
virtual void didChangeAccessibilityObjectState(
const WebKit::WebAccessibilityObject& acc_obj);
+ virtual void didChangeAccessibilityObjectChildren(
+ const WebKit::WebAccessibilityObject& acc_obj);
virtual void didUpdateInspectorSetting(const WebKit::WebString& key,
const WebKit::WebString& value);
virtual void queryAutofillSuggestions(const WebKit::WebNode& node,
@@ -707,6 +709,7 @@ class RenderView : public RenderWidget,
// render_messages_internal.h for the message that the function is handling.
void OnAccessibilityDoDefaultAction(int acc_obj_id);
+ void OnAccessibilityObjectChildrenChangeAck();
void OnAllowBindings(int enabled_bindings_flags);
void OnAddMessageToConsole(const string16& frame_xpath,
const string16& message,
@@ -1239,6 +1242,8 @@ class RenderView : public RenderWidget,
// maintains the cache and other features of the accessibility tree.
scoped_ptr<WebKit::WebAccessibilityCache> accessibility_;
+ std::vector<webkit_glue::WebAccessibility> accessibility_changes_;
+
// The speech dispatcher attached to this view, lazily initialized.
scoped_ptr<SpeechInputDispatcher> speech_input_dispatcher_;