blob: f9c9ae61d393295b496051f183203fc88c2220fa [file] [log] [blame] [edit]
// 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 <atlbase.h>
#include <vector>
#include "base/memory/scoped_ptr.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "base/win/scoped_comptr.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.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/test/accessibility_test_utils_win.h"
#include "third_party/iaccessible2/ia2_api_all.h"
#include "third_party/isimpledom/ISimpleDOMNode.h"
#include "ui/ui_controls/ui_controls.h"
using content::OpenURLParams;
using content::Referrer;
using std::auto_ptr;
using std::string;
using std::vector;
using std::wstring;
namespace {
class AccessibilityWinBrowserTest : public InProcessBrowserTest {
public:
AccessibilityWinBrowserTest() {}
// InProcessBrowserTest
void SetUpInProcessBrowserTestFixture();
protected:
void LoadInitialAccessibilityTreeFromHtml(string html);
IAccessible* GetRendererAccessible();
void ExecuteScript(wstring script);
};
void AccessibilityWinBrowserTest::SetUpInProcessBrowserTestFixture() {
// If the mouse happens to be on the document then it will have the unexpected
// STATE_SYSTEM_HOTTRACKED state. Move it to a non-document location.
ui_controls::SendMouseMove(0, 0);
}
class AccessibleChecker {
public:
AccessibleChecker(
wstring expected_name,
int32 expected_role,
wstring expected_value);
AccessibleChecker(
wstring expected_name,
wstring expected_role,
int32 expected_ia2_role,
wstring expected_value);
AccessibleChecker(
wstring expected_name,
int32 expected_role,
int32 expected_ia2_role,
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(wstring expected_value);
// Set the expected state for this AccessibleChecker.
void SetExpectedState(LONG expected_state);
private:
void CheckAccessibleName(IAccessible* accessible);
void CheckAccessibleRole(IAccessible* accessible);
void CheckIA2Role(IAccessible* accessible);
void CheckAccessibleValue(IAccessible* accessible);
void CheckAccessibleState(IAccessible* accessible);
void CheckAccessibleChildren(IAccessible* accessible);
string16 RoleVariantToString(VARIANT* role_variant);
private:
typedef vector<AccessibleChecker*> AccessibleCheckerVector;
// Expected accessible name. Checked against IAccessible::get_accName.
wstring name_;
// Expected accessible role. Checked against IAccessible::get_accRole.
CComVariant role_;
// Expected IAccessible2 role. Checked against IAccessible2::role.
int32 ia2_role_;
// Expected accessible value. Checked against IAccessible::get_accValue.
wstring value_;
// Expected accessible state. Checked against IAccessible::get_accState.
LONG state_;
// Expected accessible children. Checked using IAccessible::get_accChildCount
// and ::AccessibleChildren.
AccessibleCheckerVector children_;
};
VARIANT CreateI4Variant(LONG value) {
VARIANT variant = {0};
V_VT(&variant) = VT_I4;
V_I4(&variant) = value;
return variant;
}
IAccessible* GetAccessibleFromResultVariant(IAccessible* parent, VARIANT *var) {
switch (V_VT(var)) {
case VT_DISPATCH:
return CComQIPtr<IAccessible>(V_DISPATCH(var)).Detach();
break;
case VT_I4: {
CComPtr<IDispatch> dispatch;
HRESULT hr = parent->get_accChild(CreateI4Variant(V_I4(var)), &dispatch);
EXPECT_TRUE(SUCCEEDED(hr));
return CComQIPtr<IAccessible>(dispatch).Detach();
break;
}
}
return NULL;
}
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());
if (FAILED(hr))
return hr;
hr = service_provider->QueryService(IID_IAccessible2, accessible2);
return 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 wstring& expected_name,
int32 depth,
bool* found) {
CComBSTR name_bstr;
node->get_accName(CreateI4Variant(CHILDID_SELF), &name_bstr);
wstring name(name_bstr.m_str, SysStringLen(name_bstr));
VARIANT role = {0};
node->get_accRole(CreateI4Variant(CHILDID_SELF), &role);
// 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", role.lVal, WideToUTF8(name).c_str());
if (expected_role == role.lVal && expected_name == name) {
*found = true;
return;
}
LONG child_count = 0;
HRESULT hr = node->get_accChildCount(&child_count);
ASSERT_EQ(S_OK, hr);
scoped_array<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.get()) {
RecursiveFindNodeInAccessibilityTree(
child_accessible.get(), expected_role, expected_name, depth + 1,
found);
if (*found)
return;
}
}
}
void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml(
string html) {
// Load the html using a data url and wait for the navigation to finish.
GURL html_data_url(string("data:text/html,") + html);
ui_test_utils::NavigateToURL(browser(), html_data_url);
// At this point, renderer accessibility is off and the page has completed
// loading. (Both of these must be strictly true or there will be test
// flakiness.) Now call GetRendererAccessible, which will trigger
// changing the accessibility mode to AccessibilityModeComplete. When
// the renderer switches accessibility on, it will send a Layout Complete
// accessibility notification containing the full accessibility tree, which
// we can wait for.
content::WindowedNotificationObserver tree_updated_observer(
content::NOTIFICATION_ACCESSIBILITY_LAYOUT_COMPLETE,
content::NotificationService::AllSources());
GetRendererAccessible();
tree_updated_observer.Wait();
}
// 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 =
chrome::GetActiveWebContents(browser())->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(wstring script) {
chrome::GetActiveWebContents(browser())->GetRenderViewHost()->
ExecuteJavascriptInWebFrame(L"", script);
}
// This constructor can be used if IA2 role will be the same as MSAA role
AccessibleChecker::AccessibleChecker(
wstring expected_name, int32 expected_role, wstring expected_value) :
name_(expected_name),
role_(expected_role),
ia2_role_(expected_role),
value_(expected_value),
state_(-1) {
}
AccessibleChecker::AccessibleChecker(wstring expected_name,
int32 expected_role,
int32 expected_ia2_role,
wstring expected_value) :
name_(expected_name),
role_(expected_role),
ia2_role_(expected_ia2_role),
value_(expected_value),
state_(-1) {
}
AccessibleChecker::AccessibleChecker(wstring expected_name,
wstring expected_role,
int32 expected_ia2_role,
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(base::StringPrintf(
"while checking %s",
UTF16ToUTF8(RoleVariantToString(&role_)).c_str()));
CheckAccessibleName(accessible);
CheckAccessibleRole(accessible);
CheckIA2Role(accessible);
CheckAccessibleValue(accessible);
CheckAccessibleState(accessible);
CheckAccessibleChildren(accessible);
}
void AccessibleChecker::SetExpectedValue(wstring expected_value) {
value_ = expected_value;
}
void AccessibleChecker::SetExpectedState(LONG expected_state) {
state_ = expected_state;
}
void AccessibleChecker::CheckAccessibleName(IAccessible* accessible) {
CComBSTR name;
HRESULT hr =
accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name);
if (name_.empty()) {
// If the object doesn't have name S_FALSE should be returned.
EXPECT_EQ(hr, S_FALSE);
} else {
// Test that the correct string was returned.
EXPECT_EQ(S_OK, hr);
EXPECT_STREQ(name_.c_str(),
wstring(name.m_str, SysStringLen(name)).c_str());
}
}
void AccessibleChecker::CheckAccessibleRole(IAccessible* accessible) {
VARIANT var_role = {0};
HRESULT hr =
accessible->get_accRole(CreateI4Variant(CHILDID_SELF), &var_role);
ASSERT_EQ(S_OK, hr);
EXPECT_EQ(role_, var_role);
if (role_ != var_role) {
LOG(ERROR) << "Expected role: " << RoleVariantToString(&role_);
LOG(ERROR) << "Got role: " << RoleVariantToString(&var_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);
if (ia2_role_ != ia2_role) {
LOG(ERROR) << "Expected ia2 role: " << IAccessible2RoleToString(ia2_role_);
LOG(ERROR) << "Got ia2 role: " << IAccessible2RoleToString(ia2_role);
}
}
void AccessibleChecker::CheckAccessibleValue(IAccessible* accessible) {
CComBSTR value;
HRESULT hr =
accessible->get_accValue(CreateI4Variant(CHILDID_SELF), &value);
EXPECT_EQ(S_OK, hr);
// Test that the correct string was returned.
EXPECT_STREQ(value_.c_str(),
wstring(value.m_str, SysStringLen(value)).c_str());
}
void AccessibleChecker::CheckAccessibleState(IAccessible* accessible) {
if (state_ < 0)
return;
VARIANT var_state = {0};
HRESULT hr =
accessible->get_accState(CreateI4Variant(CHILDID_SELF), &var_state);
EXPECT_EQ(S_OK, hr);
ASSERT_EQ(VT_I4, V_VT(&var_state));
EXPECT_EQ(state_, V_I4(&var_state));
if (state_ != V_I4(&var_state)) {
LOG(ERROR) << "Expected state: " << IAccessibleStateToString(state_);
LOG(ERROR) << "Got state: " << IAccessibleStateToString(V_I4(&var_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());
auto_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;
child_accessible.Attach(GetAccessibleFromResultVariant(parent, child));
ASSERT_TRUE(child_accessible.get());
(*child_checker)->CheckAccessible(child_accessible);
}
}
string16 AccessibleChecker::RoleVariantToString(VARIANT* role_variant) {
if (V_VT(role_variant) == VT_I4)
return IAccessibleRoleToString(V_I4(role_variant));
else if (V_VT(role_variant) == VT_BSTR)
return string16(V_BSTR(role_variant), SysStringLen(V_BSTR(role_variant)));
return string16();
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
TestBusyAccessibilityTree) {
// The initial accessible returned should have state STATE_SYSTEM_BUSY while
// the accessibility tree is being requested from the renderer.
AccessibleChecker document1_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
document1_checker.SetExpectedState(
STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED |
STATE_SYSTEM_BUSY);
document1_checker.CheckAccessible(GetRendererAccessible());
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
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, L"");
AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON, L"");
AccessibleChecker body_checker(L"", L"body", IA2_ROLE_SECTION, L"");
AccessibleChecker document2_checker(
L"Accessibility Win Test", ROLE_SYSTEM_DOCUMENT, L"");
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.
ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIVersionURL));
// 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.
CComBSTR name;
hr = document_accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name);
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, L"");
AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT, L"");
AccessibleChecker list_item_checker(L"", ROLE_SYSTEM_LISTITEM, L"");
list_item_checker.SetExpectedState(
STATE_SYSTEM_READONLY);
AccessibleChecker radio_group_checker(L"ul", ROLE_SYSTEM_GROUPING,
IA2_ROLE_SECTION, L"");
radio_group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
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.
content::WindowedNotificationObserver tree_updated_observer(
content::NOTIFICATION_ACCESSIBILITY_OTHER,
content::NotificationService::AllSources());
ExecuteScript(L"document.body.children[0].focus()");
tree_updated_observer.Wait();
// 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
content::WindowedNotificationObserver tree_updated_observer3(
content::NOTIFICATION_ACCESSIBILITY_OTHER,
content::NotificationService::AllSources());
ExecuteScript(
L"document.body.children[0].setAttribute('aria-activedescendant', 'li')");
tree_updated_observer3.Wait();
// 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,
TestNotificationCheckedStateChanged) {
LoadInitialAccessibilityTreeFromHtml(
"<body><input type='checkbox' /></body>");
// Check the browser's copy of the renderer accessibility tree.
AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON, L"");
checkbox_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
AccessibleChecker body_checker(L"", L"body", IA2_ROLE_SECTION, L"");
AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
body_checker.AppendExpectedChild(&checkbox_checker);
document_checker.AppendExpectedChild(&body_checker);
document_checker.CheckAccessible(GetRendererAccessible());
// Check the checkbox.
content::WindowedNotificationObserver tree_updated_observer(
content::NOTIFICATION_ACCESSIBILITY_OTHER,
content::NotificationService::AllSources());
ExecuteScript(L"document.body.children[0].checked=true");
tree_updated_observer.Wait();
// 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,
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(L"", ROLE_SYSTEM_GROUPING, L"");
AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
document_checker.AppendExpectedChild(&group_checker);
document_checker.CheckAccessible(GetRendererAccessible());
// Change the children of the document body.
content::WindowedNotificationObserver tree_updated_observer(
content::NOTIFICATION_ACCESSIBILITY_OTHER,
content::NotificationService::AllSources());
ExecuteScript(L"document.body.innerHTML='<b>new text</b>'");
tree_updated_observer.Wait();
// Check that the accessibility tree of the browser has been updated.
AccessibleChecker text_checker(L"new text", ROLE_SYSTEM_TEXT, L"");
group_checker.AppendExpectedChild(&text_checker);
document_checker.CheckAccessible(GetRendererAccessible());
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
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(L"", ROLE_SYSTEM_DOCUMENT, L"");
document_checker.CheckAccessible(GetRendererAccessible());
// Change the children of the document body.
content::WindowedNotificationObserver tree_updated_observer2(
content::NOTIFICATION_ACCESSIBILITY_OTHER,
content::NotificationService::AllSources());
ExecuteScript(L"document.body.children[0].style.visibility='visible'");
tree_updated_observer2.Wait();
// Check that the accessibility tree of the browser has been updated.
AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_TEXT, L"");
AccessibleChecker group_checker(L"", ROLE_SYSTEM_GROUPING, L"");
document_checker.AppendExpectedChild(&group_checker);
group_checker.AppendExpectedChild(&static_text_checker);
document_checker.CheckAccessible(GetRendererAccessible());
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
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(L"", ROLE_SYSTEM_GROUPING, L"");
group_checker.SetExpectedState(
STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_OFFSCREEN);
AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
document_checker.AppendExpectedChild(&group_checker);
document_checker.CheckAccessible(GetRendererAccessible());
// Focus the div in the document
content::WindowedNotificationObserver tree_updated_observer(
content::NOTIFICATION_ACCESSIBILITY_OTHER,
content::NotificationService::AllSources());
ExecuteScript(L"document.body.children[0].focus()");
tree_updated_observer.Wait();
// 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.
content::WindowedNotificationObserver tree_updated_observer2(
content::NOTIFICATION_ACCESSIBILITY_OTHER,
content::NotificationService::AllSources());
base::win::ScopedComPtr<IAccessible> document_accessible(
GetRendererAccessible());
ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
HRESULT hr = document_accessible->accSelect(
SELFLAG_TAKEFOCUS, CreateI4Variant(CHILDID_SELF));
ASSERT_EQ(S_OK, hr);
tree_updated_observer2.Wait();
// 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,
TestNotificationValueChanged) {
LoadInitialAccessibilityTreeFromHtml(
"<body><input type='text' value='old value'/></body>");
// Check the browser's copy of the renderer accessibility tree.
AccessibleChecker text_field_checker(L"", ROLE_SYSTEM_TEXT, L"old value");
text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
AccessibleChecker body_checker(L"", L"body", IA2_ROLE_SECTION, L"");
AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
body_checker.AppendExpectedChild(&text_field_checker);
document_checker.AppendExpectedChild(&body_checker);
document_checker.CheckAccessible(GetRendererAccessible());
// Set the value of the text control
content::WindowedNotificationObserver tree_updated_observer(
content::NOTIFICATION_ACCESSIBILITY_OTHER,
content::NotificationService::AllSources());
ExecuteScript(L"document.body.children[0].value='new value'");
tree_updated_observer.Wait();
// 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,
ContainsRendererAccessibilityTree) {
LoadInitialAccessibilityTreeFromHtml(
"<html><head><title>MyDocument</title></head>"
"<body>Content</body></html>");
// Get the accessibility object for the browser window.
HWND browser_hwnd = browser()->window()->GetNativeWindow();
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(L"", ROLE_SYSTEM_DOCUMENT, L"");
AccessibleChecker body_checker(L"", L"body", IA2_ROLE_SECTION, L"");
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, L""); \
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,
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);
BSTR node_name;
short name_space_id; // NOLINT
BSTR node_value;
unsigned int num_children;
unsigned int unique_id;
unsigned short node_type; // NOLINT
hr = document_isimpledomnode->get_nodeInfo(
&node_name, &name_space_id, &node_value, &num_children, &unique_id,
&node_type);
ASSERT_EQ(S_OK, hr);
EXPECT_EQ(NODETYPE_DOCUMENT, node_type);
EXPECT_EQ(1, num_children);
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, &name_space_id, &node_value, &num_children, &unique_id,
&node_type);
ASSERT_EQ(S_OK, hr);
EXPECT_STREQ(L"body", wstring(node_name, SysStringLen(node_name)).c_str());
EXPECT_EQ(NODETYPE_ELEMENT, node_type);
EXPECT_EQ(1, num_children);
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, &name_space_id, &node_value, &num_children, &unique_id,
&node_type);
ASSERT_EQ(S_OK, hr);
EXPECT_STREQ(L"input", wstring(node_name, SysStringLen(node_name)).c_str());
EXPECT_EQ(NODETYPE_ELEMENT, node_type);
EXPECT_EQ(0, num_children);
}
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestRoleGroup) {
LoadInitialAccessibilityTreeFromHtml(
"<fieldset></fieldset><div role=group></div>");
// Check the browser's copy of the renderer accessibility tree.
AccessibleChecker grouping1_checker(L"", ROLE_SYSTEM_GROUPING, L"");
AccessibleChecker grouping2_checker(L"", ROLE_SYSTEM_GROUPING, L"");
AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
document_checker.AppendExpectedChild(&grouping1_checker);
document_checker.AppendExpectedChild(&grouping2_checker);
document_checker.CheckAccessible(GetRendererAccessible());
}
} // namespace.