// Copyright (c) 2012 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 <vector>
#include "base/memory/scoped_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/scoped_bstr.h"
#include "base/win/scoped_comptr.h"
#include "base/win/scoped_variant.h"
#include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/url_constants.h"
#include "content/shell/browser/shell.h"
#include "content/test/accessibility_browser_test_utils.h"
#include "content/test/content_browser_test.h"
#include "content/test/content_browser_test_utils.h"
#include "third_party/iaccessible2/ia2_api_all.h"
#include "third_party/isimpledom/ISimpleDOMNode.h"
// TODO(dmazzoni): Disabled accessibility tests on Win64. crbug.com/179717
#if defined(ARCH_CPU_X86_64)
#define MAYBE(x) DISABLED_##x
#else
#define MAYBE(x) x
#endif
namespace content {
namespace {
// Helpers --------------------------------------------------------------------
base::win::ScopedComPtr<IAccessible> GetAccessibleFromResultVariant(
IAccessible* parent,
VARIANT* var) {
base::win::ScopedComPtr<IAccessible> ptr;
switch (V_VT(var)) {
case VT_DISPATCH: {
IDispatch* dispatch = V_DISPATCH(var);
if (dispatch)
ptr.QueryFrom(dispatch);
break;
}
case VT_I4: {
base::win::ScopedComPtr<IDispatch> dispatch;
HRESULT hr = parent->get_accChild(*var, dispatch.Receive());
EXPECT_TRUE(SUCCEEDED(hr));
if (dispatch)
dispatch.QueryInterface(ptr.Receive());
break;
}
}
return ptr;
}
HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) {
// TODO(ctguil): For some reason querying the IAccessible2 interface from
// IAccessible fails.
base::win::ScopedComPtr<IServiceProvider> service_provider;
HRESULT hr = accessible->QueryInterface(service_provider.Receive());
return SUCCEEDED(hr) ?
service_provider->QueryService(IID_IAccessible2, accessible2) : hr;
}
// Recursively search through all of the descendants reachable from an
// IAccessible node and return true if we find one with the given role
// and name.
void RecursiveFindNodeInAccessibilityTree(IAccessible* node,
int32 expected_role,
const std::wstring& expected_name,
int32 depth,
bool* found) {
base::win::ScopedBstr name_bstr;
base::win::ScopedVariant childid_self(CHILDID_SELF);
node->get_accName(childid_self, name_bstr.Receive());
std::wstring name(name_bstr, name_bstr.Length());
base::win::ScopedVariant role;
node->get_accRole(childid_self, role.Receive());
ASSERT_EQ(VT_I4, role.type());
// Print the accessibility tree as we go, because if this test fails
// on the bots, this is really helpful in figuring out why.
for (int i = 0; i < depth; i++)
printf(" ");
printf("role=%d name=%s\n", V_I4(&role), WideToUTF8(name).c_str());
if (expected_role == V_I4(&role) && expected_name == name) {
*found = true;
return;
}
LONG child_count = 0;
HRESULT hr = node->get_accChildCount(&child_count);
ASSERT_EQ(S_OK, hr);
scoped_ptr<VARIANT[]> child_array(new VARIANT[child_count]);
LONG obtained_count = 0;
hr = AccessibleChildren(
node, 0, child_count, child_array.get(), &obtained_count);
ASSERT_EQ(S_OK, hr);
ASSERT_EQ(child_count, obtained_count);
for (int index = 0; index < obtained_count; index++) {
base::win::ScopedComPtr<IAccessible> child_accessible(
GetAccessibleFromResultVariant(node, &child_array.get()[index]));
if (child_accessible) {
RecursiveFindNodeInAccessibilityTree(
child_accessible.get(), expected_role, expected_name, depth + 1,
found);
if (*found)
return;
}
}
}
// AccessibilityWinBrowserTest ------------------------------------------------
class AccessibilityWinBrowserTest : public ContentBrowserTest {
public:
AccessibilityWinBrowserTest();
virtual ~AccessibilityWinBrowserTest();
protected:
void LoadInitialAccessibilityTreeFromHtml(const std::string& html);
IAccessible* GetRendererAccessible();
void ExecuteScript(const std::wstring& script);
private:
DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest);
};
AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() {
}
AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() {
}
void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml(
const std::string& html) {
AccessibilityNotificationWaiter waiter(
shell(), AccessibilityModeComplete,
blink::WebAXEventLoadComplete);
GURL html_data_url("data:text/html," + html);
NavigateToURL(shell(), html_data_url);
waiter.WaitForNotification();
}
// Retrieve the MSAA client accessibility object for the Render Widget Host View
// of the selected tab.
IAccessible* AccessibilityWinBrowserTest::GetRendererAccessible() {
HWND hwnd_render_widget_host_view =
shell()->web_contents()->GetRenderWidgetHostView()->GetNativeView();
// Invoke windows screen reader detection by sending the WM_GETOBJECT message
// with kIdCustom as the LPARAM.
const int32 kIdCustom = 1;
SendMessage(
hwnd_render_widget_host_view, WM_GETOBJECT, OBJID_CLIENT, kIdCustom);
IAccessible* accessible;
HRESULT hr = AccessibleObjectFromWindow(
hwnd_render_widget_host_view, OBJID_CLIENT,
IID_IAccessible, reinterpret_cast<void**>(&accessible));
EXPECT_EQ(S_OK, hr);
EXPECT_NE(accessible, reinterpret_cast<IAccessible*>(NULL));
return accessible;
}
void AccessibilityWinBrowserTest::ExecuteScript(const std::wstring& script) {
shell()->web_contents()->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
std::wstring(), script);
}
// AccessibleChecker ----------------------------------------------------------
class AccessibleChecker {
public:
// This constructor can be used if the IA2 role will be the same as the MSAA
// role.
AccessibleChecker(const std::wstring& expected_name,
int32 expected_role,
const std::wstring& expected_value);
AccessibleChecker(const std::wstring& expected_name,
int32 expected_role,
int32 expected_ia2_role,
const std::wstring& expected_value);
AccessibleChecker(const std::wstring& expected_name,
const std::wstring& expected_role,
int32 expected_ia2_role,
const std::wstring& expected_value);
// Append an AccessibleChecker that verifies accessibility information for
// a child IAccessible. Order is important.
void AppendExpectedChild(AccessibleChecker* expected_child);
// Check that the name and role of the given IAccessible instance and its
// descendants match the expected names and roles that this object was
// initialized with.
void CheckAccessible(IAccessible* accessible);
// Set the expected value for this AccessibleChecker.
void SetExpectedValue(const std::wstring& expected_value);
// Set the expected state for this AccessibleChecker.
void SetExpectedState(LONG expected_state);
private:
typedef std::vector<AccessibleChecker*> AccessibleCheckerVector;
void CheckAccessibleName(IAccessible* accessible);
void CheckAccessibleRole(IAccessible* accessible);
void CheckIA2Role(IAccessible* accessible);
void CheckAccessibleValue(IAccessible* accessible);
void CheckAccessibleState(IAccessible* accessible);
void CheckAccessibleChildren(IAccessible* accessible);
base::string16 RoleVariantToString(const base::win::ScopedVariant& role);
// Expected accessible name. Checked against IAccessible::get_accName.
std::wstring name_;
// Expected accessible role. Checked against IAccessible::get_accRole.
base::win::ScopedVariant role_;
// Expected IAccessible2 role. Checked against IAccessible2::role.
int32 ia2_role_;
// Expected accessible value. Checked against IAccessible::get_accValue.
std::wstring value_;
// Expected accessible state. Checked against IAccessible::get_accState.
LONG state_;
// Expected accessible children. Checked using IAccessible::get_accChildCount
// and ::AccessibleChildren.
AccessibleCheckerVector children_;
};
AccessibleChecker::AccessibleChecker(const std::wstring& expected_name,
int32 expected_role,
const std::wstring& expected_value)
: name_(expected_name),
role_(expected_role),
ia2_role_(expected_role),
value_(expected_value),
state_(-1) {
}
AccessibleChecker::AccessibleChecker(const std::wstring& expected_name,
int32 expected_role,
int32 expected_ia2_role,
const std::wstring& expected_value)
: name_(expected_name),
role_(expected_role),
ia2_role_(expected_ia2_role),
value_(expected_value),
state_(-1) {
}
AccessibleChecker::AccessibleChecker(const std::wstring& expected_name,
const std::wstring& expected_role,
int32 expected_ia2_role,
const std::wstring& expected_value)
: name_(expected_name),
role_(expected_role.c_str()),
ia2_role_(expected_ia2_role),
value_(expected_value),
state_(-1) {
}
void AccessibleChecker::AppendExpectedChild(
AccessibleChecker* expected_child) {
children_.push_back(expected_child);
}
void AccessibleChecker::CheckAccessible(IAccessible* accessible) {
SCOPED_TRACE("while checking " + UTF16ToUTF8(RoleVariantToString(role_)));
CheckAccessibleName(accessible);
CheckAccessibleRole(accessible);
CheckIA2Role(accessible);
CheckAccessibleValue(accessible);
CheckAccessibleState(accessible);
CheckAccessibleChildren(accessible);
}
void AccessibleChecker::SetExpectedValue(const std::wstring& expected_value) {
value_ = expected_value;
}
void AccessibleChecker::SetExpectedState(LONG expected_state) {
state_ = expected_state;
}
void AccessibleChecker::CheckAccessibleName(IAccessible* accessible) {
base::win::ScopedBstr name;
base::win::ScopedVariant childid_self(CHILDID_SELF);
HRESULT hr = accessible->get_accName(childid_self, name.Receive());
if (name_.empty()) {
// If the object doesn't have name S_FALSE should be returned.
EXPECT_EQ(S_FALSE, hr);
} else {
// Test that the correct string was returned.
EXPECT_EQ(S_OK, hr);
EXPECT_EQ(name_, std::wstring(name, name.Length()));
}
}
void AccessibleChecker::CheckAccessibleRole(IAccessible* accessible) {
base::win::ScopedVariant role;
base::win::ScopedVariant childid_self(CHILDID_SELF);
HRESULT hr = accessible->get_accRole(childid_self, role.Receive());
ASSERT_EQ(S_OK, hr);
EXPECT_EQ(0, role_.Compare(role))
<< "Expected role: " << RoleVariantToString(role_)
<< "\nGot role: " << RoleVariantToString(role);
}
void AccessibleChecker::CheckIA2Role(IAccessible* accessible) {
base::win::ScopedComPtr<IAccessible2> accessible2;
HRESULT hr = QueryIAccessible2(accessible, accessible2.Receive());
ASSERT_EQ(S_OK, hr);
long ia2_role = 0;
hr = accessible2->role(&ia2_role);
ASSERT_EQ(S_OK, hr);
EXPECT_EQ(ia2_role_, ia2_role)
<< "Expected ia2 role: " << IAccessible2RoleToString(ia2_role_)
<< "\nGot ia2 role: " << IAccessible2RoleToString(ia2_role);
}
void AccessibleChecker::CheckAccessibleValue(IAccessible* accessible) {
// Don't check the value if if's a DOCUMENT role, because the value
// is supposed to be the url (and we don't keep track of that in the
// test expectations).
base::win::ScopedVariant role;
base::win::ScopedVariant childid_self(CHILDID_SELF);
HRESULT hr = accessible->get_accRole(childid_self, role.Receive());
ASSERT_EQ(S_OK, hr);
if (role.type() == VT_I4 && V_I4(&role) == ROLE_SYSTEM_DOCUMENT)
return;
// Get the value.
base::win::ScopedBstr value;
hr = accessible->get_accValue(childid_self, value.Receive());
EXPECT_EQ(S_OK, hr);
// Test that the correct string was returned.
EXPECT_EQ(value_, std::wstring(value, value.Length()));
}
void AccessibleChecker::CheckAccessibleState(IAccessible* accessible) {
if (state_ < 0)
return;
base::win::ScopedVariant state;
base::win::ScopedVariant childid_self(CHILDID_SELF);
HRESULT hr = accessible->get_accState(childid_self, state.Receive());
EXPECT_EQ(S_OK, hr);
ASSERT_EQ(VT_I4, state.type());
LONG obj_state = V_I4(&state);
// Avoid flakiness. The "offscreen" state depends on whether the browser
// window is frontmost or not, and "hottracked" depends on whether the
// mouse cursor happens to be over the element.
obj_state &= ~(STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_HOTTRACKED);
EXPECT_EQ(state_, obj_state)
<< "Expected state: " << IAccessibleStateToString(state_)
<< "\nGot state: " << IAccessibleStateToString(obj_state);
}
void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) {
LONG child_count = 0;
HRESULT hr = parent->get_accChildCount(&child_count);
EXPECT_EQ(S_OK, hr);
ASSERT_EQ(child_count, children_.size());
scoped_ptr<VARIANT[]> child_array(new VARIANT[child_count]);
LONG obtained_count = 0;
hr = AccessibleChildren(parent, 0, child_count,
child_array.get(), &obtained_count);
ASSERT_EQ(S_OK, hr);
ASSERT_EQ(child_count, obtained_count);
VARIANT* child = child_array.get();
for (AccessibleCheckerVector::iterator child_checker = children_.begin();
child_checker != children_.end();
++child_checker, ++child) {
base::win::ScopedComPtr<IAccessible> child_accessible(
GetAccessibleFromResultVariant(parent, child));
ASSERT_TRUE(child_accessible.get());
(*child_checker)->CheckAccessible(child_accessible);
}
}
base::string16 AccessibleChecker::RoleVariantToString(
const base::win::ScopedVariant& role) {
if (role.type() == VT_I4)
return IAccessibleRoleToString(V_I4(&role));
if (role.type() == VT_BSTR)
return base::string16(V_BSTR(&role), SysStringLen(V_BSTR(&role)));
return base::string16();
}
} // namespace
// Tests ----------------------------------------------------------------------
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
MAYBE(TestBusyAccessibilityTree)) {
NavigateToURL(shell(), GURL(kAboutBlankURL));
// The initial accessible returned should have state STATE_SYSTEM_BUSY while
// the accessibility tree is being requested from the renderer.
AccessibleChecker document1_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
std::wstring());
document1_checker.SetExpectedState(
STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED |
STATE_SYSTEM_BUSY);
document1_checker.CheckAccessible(GetRendererAccessible());
}
// Flaky, http://crbug.com/167320 .
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
DISABLED_TestRendererAccessibilityTree) {
LoadInitialAccessibilityTreeFromHtml(
"<html><head><title>Accessibility Win Test</title></head>"
"<body><input type='button' value='push' /><input type='checkbox' />"
"</body></html>");
// Check the browser's copy of the renderer accessibility tree.
AccessibleChecker button_checker(L"push", ROLE_SYSTEM_PUSHBUTTON,
std::wstring());
AccessibleChecker checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON,
std::wstring());
AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
std::wstring());
AccessibleChecker document2_checker(L"Accessibility Win Test",
ROLE_SYSTEM_DOCUMENT, std::wstring());
body_checker.AppendExpectedChild(&button_checker);
body_checker.AppendExpectedChild(&checkbox_checker);
document2_checker.AppendExpectedChild(&body_checker);
document2_checker.CheckAccessible(GetRendererAccessible());
// Check that document accessible has a parent accessible.
base::win::ScopedComPtr<IAccessible> document_accessible(
GetRendererAccessible());
ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
base::win::ScopedComPtr<IDispatch> parent_dispatch;
HRESULT hr = document_accessible->get_accParent(parent_dispatch.Receive());
EXPECT_EQ(S_OK, hr);
EXPECT_NE(parent_dispatch, reinterpret_cast<IDispatch*>(NULL));
// Navigate to another page.
NavigateToURL(shell(), GURL(kAboutBlankURL));
// 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.
base::win::ScopedBstr name;
base::win::ScopedVariant childid_self(CHILDID_SELF);
hr = document_accessible->get_accName(childid_self, name.Receive());
ASSERT_EQ(E_FAIL, hr);
}
// Periodically failing. See crbug.com/145537
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
DISABLED_TestNotificationActiveDescendantChanged) {
LoadInitialAccessibilityTreeFromHtml(
"<ul tabindex='-1' role='radiogroup' aria-label='ul'>"
"<li id='li'>li</li></ul>");
// Check the browser's copy of the renderer accessibility tree.
AccessibleChecker list_marker_checker(L"\x2022", ROLE_SYSTEM_TEXT,
std::wstring());
AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT,
std::wstring());
AccessibleChecker list_item_checker(std::wstring(), ROLE_SYSTEM_LISTITEM,
std::wstring());
list_item_checker.SetExpectedState(STATE_SYSTEM_READONLY);
AccessibleChecker radio_group_checker(L"ul", ROLE_SYSTEM_GROUPING,
IA2_ROLE_SECTION, std::wstring());
radio_group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
std::wstring());
list_item_checker.AppendExpectedChild(&list_marker_checker);
list_item_checker.AppendExpectedChild(&static_text_checker);
radio_group_checker.AppendExpectedChild(&list_item_checker);
document_checker.AppendExpectedChild(&radio_group_checker);
document_checker.CheckAccessible(GetRendererAccessible());
// Set focus to the radio group.
scoped_ptr<AccessibilityNotificationWaiter> waiter(
new AccessibilityNotificationWaiter(
shell(), AccessibilityModeComplete,
blink::WebAXEventFocus));
ExecuteScript(L"document.body.children[0].focus()");
waiter->WaitForNotification();
// Check that the accessibility tree of the browser has been updated.
radio_group_checker.SetExpectedState(
STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED);
document_checker.CheckAccessible(GetRendererAccessible());
// Set the active descendant of the radio group
waiter.reset(new AccessibilityNotificationWaiter(
shell(), AccessibilityModeComplete,
blink::WebAXEventFocus));
ExecuteScript(
L"document.body.children[0].setAttribute('aria-activedescendant', 'li')");
waiter->WaitForNotification();
// Check that the accessibility tree of the browser has been updated.
list_item_checker.SetExpectedState(
STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED);
radio_group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
document_checker.CheckAccessible(GetRendererAccessible());
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
MAYBE(TestNotificationCheckedStateChanged)) {
LoadInitialAccessibilityTreeFromHtml(
"<body><input type='checkbox' /></body>");
// Check the browser's copy of the renderer accessibility tree.
AccessibleChecker checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON,
std::wstring());
checkbox_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
std::wstring());
AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
std::wstring());
body_checker.AppendExpectedChild(&checkbox_checker);
document_checker.AppendExpectedChild(&body_checker);
document_checker.CheckAccessible(GetRendererAccessible());
// Check the checkbox.
scoped_ptr<AccessibilityNotificationWaiter> waiter(
new AccessibilityNotificationWaiter(
shell(), AccessibilityModeComplete,
blink::WebAXEventCheckedStateChanged));
ExecuteScript(L"document.body.children[0].checked=true");
waiter->WaitForNotification();
// Check that the accessibility tree of the browser has been updated.
checkbox_checker.SetExpectedState(
STATE_SYSTEM_CHECKED | STATE_SYSTEM_FOCUSABLE);
document_checker.CheckAccessible(GetRendererAccessible());
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
MAYBE(TestNotificationChildrenChanged)) {
// The role attribute causes the node to be in the accessibility tree.
LoadInitialAccessibilityTreeFromHtml("<body role=group></body>");
// Check the browser's copy of the renderer accessibility tree.
AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
std::wstring());
AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
std::wstring());
document_checker.AppendExpectedChild(&group_checker);
document_checker.CheckAccessible(GetRendererAccessible());
// Change the children of the document body.
scoped_ptr<AccessibilityNotificationWaiter> waiter(
new AccessibilityNotificationWaiter(
shell(),
AccessibilityModeComplete,
blink::WebAXEventChildrenChanged));
ExecuteScript(L"document.body.innerHTML='<b>new text</b>'");
waiter->WaitForNotification();
// Check that the accessibility tree of the browser has been updated.
AccessibleChecker text_checker(L"new text", ROLE_SYSTEM_TEXT, std::wstring());
group_checker.AppendExpectedChild(&text_checker);
document_checker.CheckAccessible(GetRendererAccessible());
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
MAYBE(TestNotificationChildrenChanged2)) {
// The role attribute causes the node to be in the accessibility tree.
LoadInitialAccessibilityTreeFromHtml(
"<div role=group style='visibility: hidden'>text</div>");
// Check the accessible tree of the browser.
AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
std::wstring());
document_checker.CheckAccessible(GetRendererAccessible());
// Change the children of the document body.
scoped_ptr<AccessibilityNotificationWaiter> waiter(
new AccessibilityNotificationWaiter(
shell(), AccessibilityModeComplete,
blink::WebAXEventChildrenChanged));
ExecuteScript(L"document.body.children[0].style.visibility='visible'");
waiter->WaitForNotification();
// Check that the accessibility tree of the browser has been updated.
AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_TEXT,
std::wstring());
AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
std::wstring());
document_checker.AppendExpectedChild(&group_checker);
group_checker.AppendExpectedChild(&static_text_checker);
document_checker.CheckAccessible(GetRendererAccessible());
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
MAYBE(TestNotificationFocusChanged)) {
// The role attribute causes the node to be in the accessibility tree.
LoadInitialAccessibilityTreeFromHtml("<div role=group tabindex='-1'></div>");
// Check the browser's copy of the renderer accessibility tree.
SCOPED_TRACE("Check initial tree");
AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
std::wstring());
group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
std::wstring());
document_checker.AppendExpectedChild(&group_checker);
document_checker.CheckAccessible(GetRendererAccessible());
// Focus the div in the document
scoped_ptr<AccessibilityNotificationWaiter> waiter(
new AccessibilityNotificationWaiter(
shell(), AccessibilityModeComplete,
blink::WebAXEventFocus));
ExecuteScript(L"document.body.children[0].focus()");
waiter->WaitForNotification();
// Check that the accessibility tree of the browser has been updated.
SCOPED_TRACE("Check updated tree after focusing div");
group_checker.SetExpectedState(
STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED);
document_checker.CheckAccessible(GetRendererAccessible());
// Focus the document accessible. This will un-focus the current node.
waiter.reset(
new AccessibilityNotificationWaiter(
shell(), AccessibilityModeComplete,
blink::WebAXEventBlur));
base::win::ScopedComPtr<IAccessible> document_accessible(
GetRendererAccessible());
ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
base::win::ScopedVariant childid_self(CHILDID_SELF);
HRESULT hr = document_accessible->accSelect(SELFLAG_TAKEFOCUS, childid_self);
ASSERT_EQ(S_OK, hr);
waiter->WaitForNotification();
// Check that the accessibility tree of the browser has been updated.
SCOPED_TRACE("Check updated tree after focusing document again");
group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
document_checker.CheckAccessible(GetRendererAccessible());
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
MAYBE(TestNotificationValueChanged)) {
LoadInitialAccessibilityTreeFromHtml(
"<body><input type='text' value='old value'/></body>");
// Check the browser's copy of the renderer accessibility tree.
AccessibleChecker text_field_checker(std::wstring(), ROLE_SYSTEM_TEXT,
L"old value");
text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
std::wstring());
AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
std::wstring());
body_checker.AppendExpectedChild(&text_field_checker);
document_checker.AppendExpectedChild(&body_checker);
document_checker.CheckAccessible(GetRendererAccessible());
// Set the value of the text control
scoped_ptr<AccessibilityNotificationWaiter> waiter(
new AccessibilityNotificationWaiter(
shell(), AccessibilityModeComplete,
blink::WebAXEventValueChanged));
ExecuteScript(L"document.body.children[0].value='new value'");
waiter->WaitForNotification();
// Check that the accessibility tree of the browser has been updated.
text_field_checker.SetExpectedValue(L"new value");
document_checker.CheckAccessible(GetRendererAccessible());
}
// This test verifies that the web content's accessibility tree is a
// descendant of the main browser window's accessibility tree, so that
// tools like AccExplorer32 or AccProbe can be used to examine Chrome's
// accessibility support.
//
// If you made a change and this test now fails, check that the NativeViewHost
// that wraps the tab contents returns the IAccessible implementation
// provided by RenderWidgetHostViewWin in GetNativeViewAccessible().
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
MAYBE(ContainsRendererAccessibilityTree)) {
LoadInitialAccessibilityTreeFromHtml(
"<html><head><title>MyDocument</title></head>"
"<body>Content</body></html>");
// Get the accessibility object for the browser window.
HWND browser_hwnd = shell()->window();
base::win::ScopedComPtr<IAccessible> browser_accessible;
HRESULT hr = AccessibleObjectFromWindow(
browser_hwnd,
OBJID_WINDOW,
IID_IAccessible,
reinterpret_cast<void**>(browser_accessible.Receive()));
ASSERT_EQ(S_OK, hr);
bool found = false;
RecursiveFindNodeInAccessibilityTree(
browser_accessible.get(), ROLE_SYSTEM_DOCUMENT, L"MyDocument", 0, &found);
ASSERT_EQ(found, true);
}
// Disabled because of http://crbug.com/144390.
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
DISABLED_TestToggleButtonRoleAndStates) {
AccessibleChecker* button_checker;
std::string button_html("data:text/html,");
AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
std::wstring());
AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
std::wstring());
document_checker.AppendExpectedChild(&body_checker);
// Temporary macro
#define ADD_BUTTON(html, ia2_role, state) \
button_html += html; \
button_checker = new AccessibleChecker(L"x", ROLE_SYSTEM_PUSHBUTTON, \
ia2_role, std::wstring()); \
button_checker->SetExpectedState(state); \
body_checker.AppendExpectedChild(button_checker)
// If aria-pressed is 'undefined', empty or not present, use PUSHBUTTON
// Otherwise use TOGGLE_BUTTON, even if the value is invalid.
// The spec does this in an attempt future-proof in case new values are added.
ADD_BUTTON("<span role='button' aria-pressed='false'>x</span>",
IA2_ROLE_TOGGLE_BUTTON, 0);
ADD_BUTTON("<span role='button' aria-pressed='true'>x</span>",
IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_PRESSED);
ADD_BUTTON("<span role='button' aria-pressed='mixed'>x</span>",
IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_MIXED);
ADD_BUTTON("<span role='button' aria-pressed='xyz'>x</span>",
IA2_ROLE_TOGGLE_BUTTON, 0);
ADD_BUTTON("<span role='button' aria-pressed=''>x</span>",
ROLE_SYSTEM_PUSHBUTTON, 0);
ADD_BUTTON("<span role='button' aria-pressed>x</span>",
ROLE_SYSTEM_PUSHBUTTON, 0);
ADD_BUTTON("<span role='button' aria-pressed='undefined'>x</span>",
ROLE_SYSTEM_PUSHBUTTON, 0);
ADD_BUTTON("<span role='button'>x</span>", ROLE_SYSTEM_PUSHBUTTON, 0);
ADD_BUTTON("<input type='button' aria-pressed='true' value='x'/>",
IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_PRESSED);
ADD_BUTTON("<input type='button' aria-pressed='false' value='x'/>",
IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE);
ADD_BUTTON("<input type='button' aria-pressed='mixed' value='x'>",
IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_MIXED);
ADD_BUTTON("<input type='button' aria-pressed='xyz' value='x'/>",
IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE);
ADD_BUTTON("<input type='button' aria-pressed='' value='x'/>",
ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
ADD_BUTTON("<input type='button' aria-pressed value='x'>",
ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
ADD_BUTTON("<input type='button' aria-pressed='undefined' value='x'>",
ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
ADD_BUTTON("<input type='button' value='x'>",
ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
ADD_BUTTON("<button aria-pressed='true'>x</button>",
IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_PRESSED);
ADD_BUTTON("<button aria-pressed='false'>x</button>",
IA2_ROLE_TOGGLE_BUTTON, STATE_SYSTEM_FOCUSABLE);
ADD_BUTTON("<button aria-pressed='mixed'>x</button>", IA2_ROLE_TOGGLE_BUTTON,
STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_MIXED);
ADD_BUTTON("<button aria-pressed='xyz'>x</button>", IA2_ROLE_TOGGLE_BUTTON,
STATE_SYSTEM_FOCUSABLE);
ADD_BUTTON("<button aria-pressed=''>x</button>",
ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
ADD_BUTTON("<button aria-pressed>x</button>",
ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
ADD_BUTTON("<button aria-pressed='undefined'>x</button>",
ROLE_SYSTEM_PUSHBUTTON, STATE_SYSTEM_FOCUSABLE);
ADD_BUTTON("<button>x</button>", ROLE_SYSTEM_PUSHBUTTON,
STATE_SYSTEM_FOCUSABLE);
#undef ADD_BUTTON // Temporary macro
LoadInitialAccessibilityTreeFromHtml(button_html);
document_checker.CheckAccessible(GetRendererAccessible());
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
MAYBE(SupportsISimpleDOM)) {
LoadInitialAccessibilityTreeFromHtml(
"<body><input type='checkbox' /></body>");
// Get the IAccessible object for the document.
base::win::ScopedComPtr<IAccessible> document_accessible(
GetRendererAccessible());
ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
// Get the ISimpleDOM object for the document.
base::win::ScopedComPtr<IServiceProvider> service_provider;
HRESULT hr = static_cast<IAccessible*>(document_accessible)->QueryInterface(
service_provider.Receive());
ASSERT_EQ(S_OK, hr);
const GUID refguid = {0x0c539790, 0x12e4, 0x11cf,
0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
base::win::ScopedComPtr<ISimpleDOMNode> document_isimpledomnode;
hr = static_cast<IServiceProvider *>(service_provider)->QueryService(
refguid, IID_ISimpleDOMNode,
reinterpret_cast<void**>(document_isimpledomnode.Receive()));
ASSERT_EQ(S_OK, hr);
base::win::ScopedBstr node_name;
short name_space_id; // NOLINT
base::win::ScopedBstr node_value;
unsigned int num_children;
unsigned int unique_id;
unsigned short node_type; // NOLINT
hr = document_isimpledomnode->get_nodeInfo(
node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
&unique_id, &node_type);
ASSERT_EQ(S_OK, hr);
EXPECT_EQ(NODETYPE_DOCUMENT, node_type);
EXPECT_EQ(1, num_children);
node_name.Reset();
node_value.Reset();
base::win::ScopedComPtr<ISimpleDOMNode> body_isimpledomnode;
hr = document_isimpledomnode->get_firstChild(
body_isimpledomnode.Receive());
ASSERT_EQ(S_OK, hr);
hr = body_isimpledomnode->get_nodeInfo(
node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
&unique_id, &node_type);
ASSERT_EQ(S_OK, hr);
EXPECT_EQ(L"body", std::wstring(node_name, node_name.Length()));
EXPECT_EQ(NODETYPE_ELEMENT, node_type);
EXPECT_EQ(1, num_children);
node_name.Reset();
node_value.Reset();
base::win::ScopedComPtr<ISimpleDOMNode> checkbox_isimpledomnode;
hr = body_isimpledomnode->get_firstChild(
checkbox_isimpledomnode.Receive());
ASSERT_EQ(S_OK, hr);
hr = checkbox_isimpledomnode->get_nodeInfo(
node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
&unique_id, &node_type);
ASSERT_EQ(S_OK, hr);
EXPECT_EQ(L"input", std::wstring(node_name, node_name.Length()));
EXPECT_EQ(NODETYPE_ELEMENT, node_type);
EXPECT_EQ(0, num_children);
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, MAYBE(TestRoleGroup)) {
LoadInitialAccessibilityTreeFromHtml(
"<fieldset></fieldset><div role=group></div>");
// Check the browser's copy of the renderer accessibility tree.
AccessibleChecker grouping1_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
std::wstring());
AccessibleChecker grouping2_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
std::wstring());
AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
std::wstring());
document_checker.AppendExpectedChild(&grouping1_checker);
document_checker.AppendExpectedChild(&grouping2_checker);
document_checker.CheckAccessible(GetRendererAccessible());
}
} // namespace content