1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #include "base/memory/scoped_ptr.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/win/scoped_bstr.h"
10 #include "base/win/scoped_comptr.h"
11 #include "base/win/scoped_variant.h"
12 #include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
13 #include "content/browser/renderer_host/render_view_host_impl.h"
14 #include "content/public/browser/notification_service.h"
15 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/render_view_host.h"
17 #include "content/public/browser/render_widget_host_view.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/common/url_constants.h"
20 #include "content/shell/browser/shell.h"
21 #include "content/test/accessibility_browser_test_utils.h"
22 #include "content/test/content_browser_test.h"
23 #include "content/test/content_browser_test_utils.h"
24 #include "third_party/iaccessible2/ia2_api_all.h"
25 #include "third_party/isimpledom/ISimpleDOMNode.h"
27 // TODO(dmazzoni): Disabled accessibility tests on Win64. crbug.com/179717
28 #if defined(ARCH_CPU_X86_64)
29 #define MAYBE(x) DISABLED_##x
39 // Helpers --------------------------------------------------------------------
41 base::win::ScopedComPtr
<IAccessible
> GetAccessibleFromResultVariant(
44 base::win::ScopedComPtr
<IAccessible
> ptr
;
47 IDispatch
* dispatch
= V_DISPATCH(var
);
49 ptr
.QueryFrom(dispatch
);
54 base::win::ScopedComPtr
<IDispatch
> dispatch
;
55 HRESULT hr
= parent
->get_accChild(*var
, dispatch
.Receive());
56 EXPECT_TRUE(SUCCEEDED(hr
));
58 dispatch
.QueryInterface(ptr
.Receive());
65 HRESULT
QueryIAccessible2(IAccessible
* accessible
, IAccessible2
** accessible2
) {
66 // TODO(ctguil): For some reason querying the IAccessible2 interface from
68 base::win::ScopedComPtr
<IServiceProvider
> service_provider
;
69 HRESULT hr
= accessible
->QueryInterface(service_provider
.Receive());
70 return SUCCEEDED(hr
) ?
71 service_provider
->QueryService(IID_IAccessible2
, accessible2
) : hr
;
74 // Recursively search through all of the descendants reachable from an
75 // IAccessible node and return true if we find one with the given role
77 void RecursiveFindNodeInAccessibilityTree(IAccessible
* node
,
79 const std::wstring
& expected_name
,
82 base::win::ScopedBstr name_bstr
;
83 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
84 node
->get_accName(childid_self
, name_bstr
.Receive());
85 std::wstring
name(name_bstr
, name_bstr
.Length());
86 base::win::ScopedVariant role
;
87 node
->get_accRole(childid_self
, role
.Receive());
88 ASSERT_EQ(VT_I4
, role
.type());
90 // Print the accessibility tree as we go, because if this test fails
91 // on the bots, this is really helpful in figuring out why.
92 for (int i
= 0; i
< depth
; i
++)
94 printf("role=%d name=%s\n", V_I4(&role
), base::WideToUTF8(name
).c_str());
96 if (expected_role
== V_I4(&role
) && expected_name
== name
) {
101 LONG child_count
= 0;
102 HRESULT hr
= node
->get_accChildCount(&child_count
);
105 scoped_ptr
<VARIANT
[]> child_array(new VARIANT
[child_count
]);
106 LONG obtained_count
= 0;
107 hr
= AccessibleChildren(
108 node
, 0, child_count
, child_array
.get(), &obtained_count
);
110 ASSERT_EQ(child_count
, obtained_count
);
112 for (int index
= 0; index
< obtained_count
; index
++) {
113 base::win::ScopedComPtr
<IAccessible
> child_accessible(
114 GetAccessibleFromResultVariant(node
, &child_array
.get()[index
]));
115 if (child_accessible
) {
116 RecursiveFindNodeInAccessibilityTree(
117 child_accessible
.get(), expected_role
, expected_name
, depth
+ 1,
126 // AccessibilityWinBrowserTest ------------------------------------------------
128 class AccessibilityWinBrowserTest
: public ContentBrowserTest
{
130 AccessibilityWinBrowserTest();
131 virtual ~AccessibilityWinBrowserTest();
134 void LoadInitialAccessibilityTreeFromHtml(const std::string
& html
);
135 IAccessible
* GetRendererAccessible();
136 void ExecuteScript(const std::wstring
& script
);
139 DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest
);
142 AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() {
145 AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() {
148 void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml(
149 const std::string
& html
) {
150 AccessibilityNotificationWaiter
waiter(
151 shell(), AccessibilityModeComplete
,
152 ui::AX_EVENT_LOAD_COMPLETE
);
153 GURL
html_data_url("data:text/html," + html
);
154 NavigateToURL(shell(), html_data_url
);
155 waiter
.WaitForNotification();
158 // Retrieve the MSAA client accessibility object for the Render Widget Host View
159 // of the selected tab.
160 IAccessible
* AccessibilityWinBrowserTest::GetRendererAccessible() {
161 HWND hwnd_render_widget_host_view
=
162 shell()->web_contents()->GetRenderWidgetHostView()->GetNativeView();
164 // Invoke windows screen reader detection by sending the WM_GETOBJECT message
165 // with kIdCustom as the LPARAM.
166 const int32 kIdCustom
= 1;
168 hwnd_render_widget_host_view
, WM_GETOBJECT
, OBJID_CLIENT
, kIdCustom
);
170 IAccessible
* accessible
;
171 HRESULT hr
= AccessibleObjectFromWindow(
172 hwnd_render_widget_host_view
, OBJID_CLIENT
,
173 IID_IAccessible
, reinterpret_cast<void**>(&accessible
));
176 EXPECT_NE(accessible
, reinterpret_cast<IAccessible
*>(NULL
));
181 void AccessibilityWinBrowserTest::ExecuteScript(const std::wstring
& script
) {
182 shell()->web_contents()->GetRenderViewHost()->ExecuteJavascriptInWebFrame(
183 std::wstring(), script
);
187 // AccessibleChecker ----------------------------------------------------------
189 class AccessibleChecker
{
191 // This constructor can be used if the IA2 role will be the same as the MSAA
193 AccessibleChecker(const std::wstring
& expected_name
,
195 const std::wstring
& expected_value
);
196 AccessibleChecker(const std::wstring
& expected_name
,
198 int32 expected_ia2_role
,
199 const std::wstring
& expected_value
);
200 AccessibleChecker(const std::wstring
& expected_name
,
201 const std::wstring
& expected_role
,
202 int32 expected_ia2_role
,
203 const std::wstring
& expected_value
);
205 // Append an AccessibleChecker that verifies accessibility information for
206 // a child IAccessible. Order is important.
207 void AppendExpectedChild(AccessibleChecker
* expected_child
);
209 // Check that the name and role of the given IAccessible instance and its
210 // descendants match the expected names and roles that this object was
212 void CheckAccessible(IAccessible
* accessible
);
214 // Set the expected value for this AccessibleChecker.
215 void SetExpectedValue(const std::wstring
& expected_value
);
217 // Set the expected state for this AccessibleChecker.
218 void SetExpectedState(LONG expected_state
);
221 typedef std::vector
<AccessibleChecker
*> AccessibleCheckerVector
;
223 void CheckAccessibleName(IAccessible
* accessible
);
224 void CheckAccessibleRole(IAccessible
* accessible
);
225 void CheckIA2Role(IAccessible
* accessible
);
226 void CheckAccessibleValue(IAccessible
* accessible
);
227 void CheckAccessibleState(IAccessible
* accessible
);
228 void CheckAccessibleChildren(IAccessible
* accessible
);
229 base::string16
RoleVariantToString(const base::win::ScopedVariant
& role
);
231 // Expected accessible name. Checked against IAccessible::get_accName.
234 // Expected accessible role. Checked against IAccessible::get_accRole.
235 base::win::ScopedVariant role_
;
237 // Expected IAccessible2 role. Checked against IAccessible2::role.
240 // Expected accessible value. Checked against IAccessible::get_accValue.
243 // Expected accessible state. Checked against IAccessible::get_accState.
246 // Expected accessible children. Checked using IAccessible::get_accChildCount
247 // and ::AccessibleChildren.
248 AccessibleCheckerVector children_
;
251 AccessibleChecker::AccessibleChecker(const std::wstring
& expected_name
,
253 const std::wstring
& expected_value
)
254 : name_(expected_name
),
255 role_(expected_role
),
256 ia2_role_(expected_role
),
257 value_(expected_value
),
261 AccessibleChecker::AccessibleChecker(const std::wstring
& expected_name
,
263 int32 expected_ia2_role
,
264 const std::wstring
& expected_value
)
265 : name_(expected_name
),
266 role_(expected_role
),
267 ia2_role_(expected_ia2_role
),
268 value_(expected_value
),
272 AccessibleChecker::AccessibleChecker(const std::wstring
& expected_name
,
273 const std::wstring
& expected_role
,
274 int32 expected_ia2_role
,
275 const std::wstring
& expected_value
)
276 : name_(expected_name
),
277 role_(expected_role
.c_str()),
278 ia2_role_(expected_ia2_role
),
279 value_(expected_value
),
283 void AccessibleChecker::AppendExpectedChild(
284 AccessibleChecker
* expected_child
) {
285 children_
.push_back(expected_child
);
288 void AccessibleChecker::CheckAccessible(IAccessible
* accessible
) {
289 SCOPED_TRACE("while checking " +
290 base::UTF16ToUTF8(RoleVariantToString(role_
)));
291 CheckAccessibleName(accessible
);
292 CheckAccessibleRole(accessible
);
293 CheckIA2Role(accessible
);
294 CheckAccessibleValue(accessible
);
295 CheckAccessibleState(accessible
);
296 CheckAccessibleChildren(accessible
);
299 void AccessibleChecker::SetExpectedValue(const std::wstring
& expected_value
) {
300 value_
= expected_value
;
303 void AccessibleChecker::SetExpectedState(LONG expected_state
) {
304 state_
= expected_state
;
307 void AccessibleChecker::CheckAccessibleName(IAccessible
* accessible
) {
308 base::win::ScopedBstr name
;
309 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
310 HRESULT hr
= accessible
->get_accName(childid_self
, name
.Receive());
313 // If the object doesn't have name S_FALSE should be returned.
314 EXPECT_EQ(S_FALSE
, hr
);
316 // Test that the correct string was returned.
318 EXPECT_EQ(name_
, std::wstring(name
, name
.Length()));
322 void AccessibleChecker::CheckAccessibleRole(IAccessible
* accessible
) {
323 base::win::ScopedVariant role
;
324 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
325 HRESULT hr
= accessible
->get_accRole(childid_self
, role
.Receive());
327 EXPECT_EQ(0, role_
.Compare(role
))
328 << "Expected role: " << RoleVariantToString(role_
)
329 << "\nGot role: " << RoleVariantToString(role
);
332 void AccessibleChecker::CheckIA2Role(IAccessible
* accessible
) {
333 base::win::ScopedComPtr
<IAccessible2
> accessible2
;
334 HRESULT hr
= QueryIAccessible2(accessible
, accessible2
.Receive());
337 hr
= accessible2
->role(&ia2_role
);
339 EXPECT_EQ(ia2_role_
, ia2_role
)
340 << "Expected ia2 role: " << IAccessible2RoleToString(ia2_role_
)
341 << "\nGot ia2 role: " << IAccessible2RoleToString(ia2_role
);
344 void AccessibleChecker::CheckAccessibleValue(IAccessible
* accessible
) {
345 // Don't check the value if if's a DOCUMENT role, because the value
346 // is supposed to be the url (and we don't keep track of that in the
347 // test expectations).
348 base::win::ScopedVariant role
;
349 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
350 HRESULT hr
= accessible
->get_accRole(childid_self
, role
.Receive());
352 if (role
.type() == VT_I4
&& V_I4(&role
) == ROLE_SYSTEM_DOCUMENT
)
356 base::win::ScopedBstr value
;
357 hr
= accessible
->get_accValue(childid_self
, value
.Receive());
360 // Test that the correct string was returned.
361 EXPECT_EQ(value_
, std::wstring(value
, value
.Length()));
364 void AccessibleChecker::CheckAccessibleState(IAccessible
* accessible
) {
368 base::win::ScopedVariant state
;
369 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
370 HRESULT hr
= accessible
->get_accState(childid_self
, state
.Receive());
372 ASSERT_EQ(VT_I4
, state
.type());
373 LONG obj_state
= V_I4(&state
);
374 // Avoid flakiness. The "offscreen" state depends on whether the browser
375 // window is frontmost or not, and "hottracked" depends on whether the
376 // mouse cursor happens to be over the element.
377 obj_state
&= ~(STATE_SYSTEM_OFFSCREEN
| STATE_SYSTEM_HOTTRACKED
);
378 EXPECT_EQ(state_
, obj_state
)
379 << "Expected state: " << IAccessibleStateToString(state_
)
380 << "\nGot state: " << IAccessibleStateToString(obj_state
);
383 void AccessibleChecker::CheckAccessibleChildren(IAccessible
* parent
) {
384 LONG child_count
= 0;
385 HRESULT hr
= parent
->get_accChildCount(&child_count
);
387 ASSERT_EQ(child_count
, children_
.size());
389 scoped_ptr
<VARIANT
[]> child_array(new VARIANT
[child_count
]);
390 LONG obtained_count
= 0;
391 hr
= AccessibleChildren(parent
, 0, child_count
,
392 child_array
.get(), &obtained_count
);
394 ASSERT_EQ(child_count
, obtained_count
);
396 VARIANT
* child
= child_array
.get();
397 for (AccessibleCheckerVector::iterator child_checker
= children_
.begin();
398 child_checker
!= children_
.end();
399 ++child_checker
, ++child
) {
400 base::win::ScopedComPtr
<IAccessible
> child_accessible(
401 GetAccessibleFromResultVariant(parent
, child
));
402 ASSERT_TRUE(child_accessible
.get());
403 (*child_checker
)->CheckAccessible(child_accessible
);
407 base::string16
AccessibleChecker::RoleVariantToString(
408 const base::win::ScopedVariant
& role
) {
409 if (role
.type() == VT_I4
)
410 return IAccessibleRoleToString(V_I4(&role
));
411 if (role
.type() == VT_BSTR
)
412 return base::string16(V_BSTR(&role
), SysStringLen(V_BSTR(&role
)));
413 return base::string16();
419 // Tests ----------------------------------------------------------------------
421 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
422 MAYBE(TestBusyAccessibilityTree
)) {
423 NavigateToURL(shell(), GURL(kAboutBlankURL
));
425 // The initial accessible returned should have state STATE_SYSTEM_BUSY while
426 // the accessibility tree is being requested from the renderer.
427 AccessibleChecker
document1_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
429 document1_checker
.SetExpectedState(
430 STATE_SYSTEM_READONLY
| STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_FOCUSED
|
432 document1_checker
.CheckAccessible(GetRendererAccessible());
435 // Flaky, http://crbug.com/167320 .
436 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
437 DISABLED_TestRendererAccessibilityTree
) {
438 LoadInitialAccessibilityTreeFromHtml(
439 "<html><head><title>Accessibility Win Test</title></head>"
440 "<body><input type='button' value='push' /><input type='checkbox' />"
443 // Check the browser's copy of the renderer accessibility tree.
444 AccessibleChecker
button_checker(L
"push", ROLE_SYSTEM_PUSHBUTTON
,
446 AccessibleChecker
checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON
,
448 AccessibleChecker
body_checker(std::wstring(), L
"body", IA2_ROLE_SECTION
,
450 AccessibleChecker
document2_checker(L
"Accessibility Win Test",
451 ROLE_SYSTEM_DOCUMENT
, std::wstring());
452 body_checker
.AppendExpectedChild(&button_checker
);
453 body_checker
.AppendExpectedChild(&checkbox_checker
);
454 document2_checker
.AppendExpectedChild(&body_checker
);
455 document2_checker
.CheckAccessible(GetRendererAccessible());
457 // Check that document accessible has a parent accessible.
458 base::win::ScopedComPtr
<IAccessible
> document_accessible(
459 GetRendererAccessible());
460 ASSERT_NE(document_accessible
.get(), reinterpret_cast<IAccessible
*>(NULL
));
461 base::win::ScopedComPtr
<IDispatch
> parent_dispatch
;
462 HRESULT hr
= document_accessible
->get_accParent(parent_dispatch
.Receive());
464 EXPECT_NE(parent_dispatch
, reinterpret_cast<IDispatch
*>(NULL
));
466 // Navigate to another page.
467 NavigateToURL(shell(), GURL(kAboutBlankURL
));
469 // Verify that the IAccessible reference still points to a valid object and
470 // that calls to its methods fail since the tree is no longer valid after
471 // the page navagation.
472 base::win::ScopedBstr name
;
473 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
474 hr
= document_accessible
->get_accName(childid_self
, name
.Receive());
475 ASSERT_EQ(E_FAIL
, hr
);
478 // Periodically failing. See crbug.com/145537
479 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
480 DISABLED_TestNotificationActiveDescendantChanged
) {
481 LoadInitialAccessibilityTreeFromHtml(
482 "<ul tabindex='-1' role='radiogroup' aria-label='ul'>"
483 "<li id='li'>li</li></ul>");
485 // Check the browser's copy of the renderer accessibility tree.
486 AccessibleChecker
list_marker_checker(L
"\x2022", ROLE_SYSTEM_TEXT
,
488 AccessibleChecker
static_text_checker(L
"li", ROLE_SYSTEM_TEXT
,
490 AccessibleChecker
list_item_checker(std::wstring(), ROLE_SYSTEM_LISTITEM
,
492 list_item_checker
.SetExpectedState(STATE_SYSTEM_READONLY
);
493 AccessibleChecker
radio_group_checker(L
"ul", ROLE_SYSTEM_GROUPING
,
494 IA2_ROLE_SECTION
, std::wstring());
495 radio_group_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
496 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
498 list_item_checker
.AppendExpectedChild(&list_marker_checker
);
499 list_item_checker
.AppendExpectedChild(&static_text_checker
);
500 radio_group_checker
.AppendExpectedChild(&list_item_checker
);
501 document_checker
.AppendExpectedChild(&radio_group_checker
);
502 document_checker
.CheckAccessible(GetRendererAccessible());
504 // Set focus to the radio group.
505 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
506 new AccessibilityNotificationWaiter(
507 shell(), AccessibilityModeComplete
,
508 ui::AX_EVENT_FOCUS
));
509 ExecuteScript(L
"document.body.children[0].focus()");
510 waiter
->WaitForNotification();
512 // Check that the accessibility tree of the browser has been updated.
513 radio_group_checker
.SetExpectedState(
514 STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_FOCUSED
);
515 document_checker
.CheckAccessible(GetRendererAccessible());
517 // Set the active descendant of the radio group
518 waiter
.reset(new AccessibilityNotificationWaiter(
519 shell(), AccessibilityModeComplete
,
520 ui::AX_EVENT_FOCUS
));
522 L
"document.body.children[0].setAttribute('aria-activedescendant', 'li')");
523 waiter
->WaitForNotification();
525 // Check that the accessibility tree of the browser has been updated.
526 list_item_checker
.SetExpectedState(
527 STATE_SYSTEM_READONLY
| STATE_SYSTEM_FOCUSED
);
528 radio_group_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
529 document_checker
.CheckAccessible(GetRendererAccessible());
532 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
533 MAYBE(TestNotificationCheckedStateChanged
)) {
534 LoadInitialAccessibilityTreeFromHtml(
535 "<body><input type='checkbox' /></body>");
537 // Check the browser's copy of the renderer accessibility tree.
538 AccessibleChecker
checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON
,
540 checkbox_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
541 AccessibleChecker
body_checker(std::wstring(), L
"body", IA2_ROLE_SECTION
,
543 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
545 body_checker
.AppendExpectedChild(&checkbox_checker
);
546 document_checker
.AppendExpectedChild(&body_checker
);
547 document_checker
.CheckAccessible(GetRendererAccessible());
549 // Check the checkbox.
550 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
551 new AccessibilityNotificationWaiter(
552 shell(), AccessibilityModeComplete
,
553 ui::AX_EVENT_CHECKED_STATE_CHANGED
));
554 ExecuteScript(L
"document.body.children[0].checked=true");
555 waiter
->WaitForNotification();
557 // Check that the accessibility tree of the browser has been updated.
558 checkbox_checker
.SetExpectedState(
559 STATE_SYSTEM_CHECKED
| STATE_SYSTEM_FOCUSABLE
);
560 document_checker
.CheckAccessible(GetRendererAccessible());
563 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
564 MAYBE(TestNotificationChildrenChanged
)) {
565 // The role attribute causes the node to be in the accessibility tree.
566 LoadInitialAccessibilityTreeFromHtml("<body role=group></body>");
568 // Check the browser's copy of the renderer accessibility tree.
569 AccessibleChecker
group_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
571 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
573 document_checker
.AppendExpectedChild(&group_checker
);
574 document_checker
.CheckAccessible(GetRendererAccessible());
576 // Change the children of the document body.
577 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
578 new AccessibilityNotificationWaiter(
580 AccessibilityModeComplete
,
581 ui::AX_EVENT_CHILDREN_CHANGED
));
582 ExecuteScript(L
"document.body.innerHTML='<b>new text</b>'");
583 waiter
->WaitForNotification();
585 // Check that the accessibility tree of the browser has been updated.
586 AccessibleChecker
text_checker(L
"new text", ROLE_SYSTEM_TEXT
, std::wstring());
587 group_checker
.AppendExpectedChild(&text_checker
);
588 document_checker
.CheckAccessible(GetRendererAccessible());
591 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
592 MAYBE(TestNotificationChildrenChanged2
)) {
593 // The role attribute causes the node to be in the accessibility tree.
594 LoadInitialAccessibilityTreeFromHtml(
595 "<div role=group style='visibility: hidden'>text</div>");
597 // Check the accessible tree of the browser.
598 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
600 document_checker
.CheckAccessible(GetRendererAccessible());
602 // Change the children of the document body.
603 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
604 new AccessibilityNotificationWaiter(
605 shell(), AccessibilityModeComplete
,
606 ui::AX_EVENT_CHILDREN_CHANGED
));
607 ExecuteScript(L
"document.body.children[0].style.visibility='visible'");
608 waiter
->WaitForNotification();
610 // Check that the accessibility tree of the browser has been updated.
611 AccessibleChecker
static_text_checker(L
"text", ROLE_SYSTEM_TEXT
,
613 AccessibleChecker
group_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
615 document_checker
.AppendExpectedChild(&group_checker
);
616 group_checker
.AppendExpectedChild(&static_text_checker
);
617 document_checker
.CheckAccessible(GetRendererAccessible());
620 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
621 MAYBE(TestNotificationFocusChanged
)) {
622 // The role attribute causes the node to be in the accessibility tree.
623 LoadInitialAccessibilityTreeFromHtml("<div role=group tabindex='-1'></div>");
625 // Check the browser's copy of the renderer accessibility tree.
626 SCOPED_TRACE("Check initial tree");
627 AccessibleChecker
group_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
629 group_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
630 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
632 document_checker
.AppendExpectedChild(&group_checker
);
633 document_checker
.CheckAccessible(GetRendererAccessible());
635 // Focus the div in the document
636 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
637 new AccessibilityNotificationWaiter(
638 shell(), AccessibilityModeComplete
,
639 ui::AX_EVENT_FOCUS
));
640 ExecuteScript(L
"document.body.children[0].focus()");
641 waiter
->WaitForNotification();
643 // Check that the accessibility tree of the browser has been updated.
644 SCOPED_TRACE("Check updated tree after focusing div");
645 group_checker
.SetExpectedState(
646 STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_FOCUSED
);
647 document_checker
.CheckAccessible(GetRendererAccessible());
649 // Focus the document accessible. This will un-focus the current node.
651 new AccessibilityNotificationWaiter(
652 shell(), AccessibilityModeComplete
,
654 base::win::ScopedComPtr
<IAccessible
> document_accessible(
655 GetRendererAccessible());
656 ASSERT_NE(document_accessible
.get(), reinterpret_cast<IAccessible
*>(NULL
));
657 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
658 HRESULT hr
= document_accessible
->accSelect(SELFLAG_TAKEFOCUS
, childid_self
);
660 waiter
->WaitForNotification();
662 // Check that the accessibility tree of the browser has been updated.
663 SCOPED_TRACE("Check updated tree after focusing document again");
664 group_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
665 document_checker
.CheckAccessible(GetRendererAccessible());
668 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
669 MAYBE(TestNotificationValueChanged
)) {
670 LoadInitialAccessibilityTreeFromHtml(
671 "<body><input type='text' value='old value'/></body>");
673 // Check the browser's copy of the renderer accessibility tree.
674 AccessibleChecker
text_field_checker(std::wstring(), ROLE_SYSTEM_TEXT
,
676 text_field_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
677 AccessibleChecker
body_checker(std::wstring(), L
"body", IA2_ROLE_SECTION
,
679 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
681 body_checker
.AppendExpectedChild(&text_field_checker
);
682 document_checker
.AppendExpectedChild(&body_checker
);
683 document_checker
.CheckAccessible(GetRendererAccessible());
685 // Set the value of the text control
686 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
687 new AccessibilityNotificationWaiter(
688 shell(), AccessibilityModeComplete
,
689 ui::AX_EVENT_VALUE_CHANGE
));
690 ExecuteScript(L
"document.body.children[0].value='new value'");
691 waiter
->WaitForNotification();
693 // Check that the accessibility tree of the browser has been updated.
694 text_field_checker
.SetExpectedValue(L
"new value");
695 document_checker
.CheckAccessible(GetRendererAccessible());
698 // This test verifies that the web content's accessibility tree is a
699 // descendant of the main browser window's accessibility tree, so that
700 // tools like AccExplorer32 or AccProbe can be used to examine Chrome's
701 // accessibility support.
703 // If you made a change and this test now fails, check that the NativeViewHost
704 // that wraps the tab contents returns the IAccessible implementation
705 // provided by RenderWidgetHostViewWin in GetNativeViewAccessible().
706 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
707 MAYBE(ContainsRendererAccessibilityTree
)) {
708 LoadInitialAccessibilityTreeFromHtml(
709 "<html><head><title>MyDocument</title></head>"
710 "<body>Content</body></html>");
712 // Get the accessibility object for the browser window.
713 HWND browser_hwnd
= shell()->window();
714 base::win::ScopedComPtr
<IAccessible
> browser_accessible
;
715 HRESULT hr
= AccessibleObjectFromWindow(
719 reinterpret_cast<void**>(browser_accessible
.Receive()));
723 RecursiveFindNodeInAccessibilityTree(
724 browser_accessible
.get(), ROLE_SYSTEM_DOCUMENT
, L
"MyDocument", 0, &found
);
725 ASSERT_EQ(found
, true);
728 // Disabled because of http://crbug.com/144390.
729 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
730 DISABLED_TestToggleButtonRoleAndStates
) {
731 AccessibleChecker
* button_checker
;
732 std::string
button_html("data:text/html,");
733 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
735 AccessibleChecker
body_checker(std::wstring(), L
"body", IA2_ROLE_SECTION
,
737 document_checker
.AppendExpectedChild(&body_checker
);
740 #define ADD_BUTTON(html, ia2_role, state) \
741 button_html += html; \
742 button_checker = new AccessibleChecker(L"x", ROLE_SYSTEM_PUSHBUTTON, \
743 ia2_role, std::wstring()); \
744 button_checker->SetExpectedState(state); \
745 body_checker.AppendExpectedChild(button_checker)
747 // If aria-pressed is 'undefined', empty or not present, use PUSHBUTTON
748 // Otherwise use TOGGLE_BUTTON, even if the value is invalid.
749 // The spec does this in an attempt future-proof in case new values are added.
750 ADD_BUTTON("<span role='button' aria-pressed='false'>x</span>",
751 IA2_ROLE_TOGGLE_BUTTON
, 0);
752 ADD_BUTTON("<span role='button' aria-pressed='true'>x</span>",
753 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_PRESSED
);
754 ADD_BUTTON("<span role='button' aria-pressed='mixed'>x</span>",
755 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_MIXED
);
756 ADD_BUTTON("<span role='button' aria-pressed='xyz'>x</span>",
757 IA2_ROLE_TOGGLE_BUTTON
, 0);
758 ADD_BUTTON("<span role='button' aria-pressed=''>x</span>",
759 ROLE_SYSTEM_PUSHBUTTON
, 0);
760 ADD_BUTTON("<span role='button' aria-pressed>x</span>",
761 ROLE_SYSTEM_PUSHBUTTON
, 0);
762 ADD_BUTTON("<span role='button' aria-pressed='undefined'>x</span>",
763 ROLE_SYSTEM_PUSHBUTTON
, 0);
764 ADD_BUTTON("<span role='button'>x</span>", ROLE_SYSTEM_PUSHBUTTON
, 0);
765 ADD_BUTTON("<input type='button' aria-pressed='true' value='x'/>",
766 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_PRESSED
);
767 ADD_BUTTON("<input type='button' aria-pressed='false' value='x'/>",
768 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_FOCUSABLE
);
769 ADD_BUTTON("<input type='button' aria-pressed='mixed' value='x'>",
770 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_MIXED
);
771 ADD_BUTTON("<input type='button' aria-pressed='xyz' value='x'/>",
772 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_FOCUSABLE
);
773 ADD_BUTTON("<input type='button' aria-pressed='' value='x'/>",
774 ROLE_SYSTEM_PUSHBUTTON
, STATE_SYSTEM_FOCUSABLE
);
775 ADD_BUTTON("<input type='button' aria-pressed value='x'>",
776 ROLE_SYSTEM_PUSHBUTTON
, STATE_SYSTEM_FOCUSABLE
);
777 ADD_BUTTON("<input type='button' aria-pressed='undefined' value='x'>",
778 ROLE_SYSTEM_PUSHBUTTON
, STATE_SYSTEM_FOCUSABLE
);
779 ADD_BUTTON("<input type='button' value='x'>",
780 ROLE_SYSTEM_PUSHBUTTON
, STATE_SYSTEM_FOCUSABLE
);
781 ADD_BUTTON("<button aria-pressed='true'>x</button>",
782 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_PRESSED
);
783 ADD_BUTTON("<button aria-pressed='false'>x</button>",
784 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_FOCUSABLE
);
785 ADD_BUTTON("<button aria-pressed='mixed'>x</button>", IA2_ROLE_TOGGLE_BUTTON
,
786 STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_MIXED
);
787 ADD_BUTTON("<button aria-pressed='xyz'>x</button>", IA2_ROLE_TOGGLE_BUTTON
,
788 STATE_SYSTEM_FOCUSABLE
);
789 ADD_BUTTON("<button aria-pressed=''>x</button>",
790 ROLE_SYSTEM_PUSHBUTTON
, STATE_SYSTEM_FOCUSABLE
);
791 ADD_BUTTON("<button aria-pressed>x</button>",
792 ROLE_SYSTEM_PUSHBUTTON
, STATE_SYSTEM_FOCUSABLE
);
793 ADD_BUTTON("<button aria-pressed='undefined'>x</button>",
794 ROLE_SYSTEM_PUSHBUTTON
, STATE_SYSTEM_FOCUSABLE
);
795 ADD_BUTTON("<button>x</button>", ROLE_SYSTEM_PUSHBUTTON
,
796 STATE_SYSTEM_FOCUSABLE
);
797 #undef ADD_BUTTON // Temporary macro
799 LoadInitialAccessibilityTreeFromHtml(button_html
);
800 document_checker
.CheckAccessible(GetRendererAccessible());
803 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
804 MAYBE(SupportsISimpleDOM
)) {
805 LoadInitialAccessibilityTreeFromHtml(
806 "<body><input type='checkbox' /></body>");
808 // Get the IAccessible object for the document.
809 base::win::ScopedComPtr
<IAccessible
> document_accessible(
810 GetRendererAccessible());
811 ASSERT_NE(document_accessible
.get(), reinterpret_cast<IAccessible
*>(NULL
));
813 // Get the ISimpleDOM object for the document.
814 base::win::ScopedComPtr
<IServiceProvider
> service_provider
;
815 HRESULT hr
= static_cast<IAccessible
*>(document_accessible
)->QueryInterface(
816 service_provider
.Receive());
818 const GUID refguid
= {0x0c539790, 0x12e4, 0x11cf,
819 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
820 base::win::ScopedComPtr
<ISimpleDOMNode
> document_isimpledomnode
;
821 hr
= static_cast<IServiceProvider
*>(service_provider
)->QueryService(
822 refguid
, IID_ISimpleDOMNode
,
823 reinterpret_cast<void**>(document_isimpledomnode
.Receive()));
826 base::win::ScopedBstr node_name
;
827 short name_space_id
; // NOLINT
828 base::win::ScopedBstr node_value
;
829 unsigned int num_children
;
830 unsigned int unique_id
;
831 unsigned short node_type
; // NOLINT
832 hr
= document_isimpledomnode
->get_nodeInfo(
833 node_name
.Receive(), &name_space_id
, node_value
.Receive(), &num_children
,
834 &unique_id
, &node_type
);
836 EXPECT_EQ(NODETYPE_DOCUMENT
, node_type
);
837 EXPECT_EQ(1, num_children
);
841 base::win::ScopedComPtr
<ISimpleDOMNode
> body_isimpledomnode
;
842 hr
= document_isimpledomnode
->get_firstChild(
843 body_isimpledomnode
.Receive());
845 hr
= body_isimpledomnode
->get_nodeInfo(
846 node_name
.Receive(), &name_space_id
, node_value
.Receive(), &num_children
,
847 &unique_id
, &node_type
);
849 EXPECT_EQ(L
"body", std::wstring(node_name
, node_name
.Length()));
850 EXPECT_EQ(NODETYPE_ELEMENT
, node_type
);
851 EXPECT_EQ(1, num_children
);
855 base::win::ScopedComPtr
<ISimpleDOMNode
> checkbox_isimpledomnode
;
856 hr
= body_isimpledomnode
->get_firstChild(
857 checkbox_isimpledomnode
.Receive());
859 hr
= checkbox_isimpledomnode
->get_nodeInfo(
860 node_name
.Receive(), &name_space_id
, node_value
.Receive(), &num_children
,
861 &unique_id
, &node_type
);
863 EXPECT_EQ(L
"input", std::wstring(node_name
, node_name
.Length()));
864 EXPECT_EQ(NODETYPE_ELEMENT
, node_type
);
865 EXPECT_EQ(0, num_children
);
868 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
, MAYBE(TestRoleGroup
)) {
869 LoadInitialAccessibilityTreeFromHtml(
870 "<fieldset></fieldset><div role=group></div>");
872 // Check the browser's copy of the renderer accessibility tree.
873 AccessibleChecker
grouping1_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
875 AccessibleChecker
grouping2_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
877 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
879 document_checker
.AppendExpectedChild(&grouping1_checker
);
880 document_checker
.AppendExpectedChild(&grouping2_checker
);
881 document_checker
.CheckAccessible(GetRendererAccessible());
884 } // namespace content