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_frame_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/public/test/content_browser_test.h"
21 #include "content/public/test/content_browser_test_utils.h"
22 #include "content/shell/browser/shell.h"
23 #include "content/test/accessibility_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()->GetMainFrame()->ExecuteJavaScript(script
);
186 // AccessibleChecker ----------------------------------------------------------
188 class AccessibleChecker
{
190 // This constructor can be used if the IA2 role will be the same as the MSAA
192 AccessibleChecker(const std::wstring
& expected_name
,
194 const std::wstring
& expected_value
);
195 AccessibleChecker(const std::wstring
& expected_name
,
197 int32 expected_ia2_role
,
198 const std::wstring
& expected_value
);
199 AccessibleChecker(const std::wstring
& expected_name
,
200 const std::wstring
& expected_role
,
201 int32 expected_ia2_role
,
202 const std::wstring
& expected_value
);
204 // Append an AccessibleChecker that verifies accessibility information for
205 // a child IAccessible. Order is important.
206 void AppendExpectedChild(AccessibleChecker
* expected_child
);
208 // Check that the name and role of the given IAccessible instance and its
209 // descendants match the expected names and roles that this object was
211 void CheckAccessible(IAccessible
* accessible
);
213 // Set the expected value for this AccessibleChecker.
214 void SetExpectedValue(const std::wstring
& expected_value
);
216 // Set the expected state for this AccessibleChecker.
217 void SetExpectedState(LONG expected_state
);
220 typedef std::vector
<AccessibleChecker
*> AccessibleCheckerVector
;
222 void CheckAccessibleName(IAccessible
* accessible
);
223 void CheckAccessibleRole(IAccessible
* accessible
);
224 void CheckIA2Role(IAccessible
* accessible
);
225 void CheckAccessibleValue(IAccessible
* accessible
);
226 void CheckAccessibleState(IAccessible
* accessible
);
227 void CheckAccessibleChildren(IAccessible
* accessible
);
228 base::string16
RoleVariantToString(const base::win::ScopedVariant
& role
);
230 // Expected accessible name. Checked against IAccessible::get_accName.
233 // Expected accessible role. Checked against IAccessible::get_accRole.
234 base::win::ScopedVariant role_
;
236 // Expected IAccessible2 role. Checked against IAccessible2::role.
239 // Expected accessible value. Checked against IAccessible::get_accValue.
242 // Expected accessible state. Checked against IAccessible::get_accState.
245 // Expected accessible children. Checked using IAccessible::get_accChildCount
246 // and ::AccessibleChildren.
247 AccessibleCheckerVector children_
;
250 AccessibleChecker::AccessibleChecker(const std::wstring
& expected_name
,
252 const std::wstring
& expected_value
)
253 : name_(expected_name
),
254 role_(expected_role
),
255 ia2_role_(expected_role
),
256 value_(expected_value
),
260 AccessibleChecker::AccessibleChecker(const std::wstring
& expected_name
,
262 int32 expected_ia2_role
,
263 const std::wstring
& expected_value
)
264 : name_(expected_name
),
265 role_(expected_role
),
266 ia2_role_(expected_ia2_role
),
267 value_(expected_value
),
271 AccessibleChecker::AccessibleChecker(const std::wstring
& expected_name
,
272 const std::wstring
& expected_role
,
273 int32 expected_ia2_role
,
274 const std::wstring
& expected_value
)
275 : name_(expected_name
),
276 role_(expected_role
.c_str()),
277 ia2_role_(expected_ia2_role
),
278 value_(expected_value
),
282 void AccessibleChecker::AppendExpectedChild(
283 AccessibleChecker
* expected_child
) {
284 children_
.push_back(expected_child
);
287 void AccessibleChecker::CheckAccessible(IAccessible
* accessible
) {
288 SCOPED_TRACE("while checking " +
289 base::UTF16ToUTF8(RoleVariantToString(role_
)));
290 CheckAccessibleName(accessible
);
291 CheckAccessibleRole(accessible
);
292 CheckIA2Role(accessible
);
293 CheckAccessibleValue(accessible
);
294 CheckAccessibleState(accessible
);
295 CheckAccessibleChildren(accessible
);
298 void AccessibleChecker::SetExpectedValue(const std::wstring
& expected_value
) {
299 value_
= expected_value
;
302 void AccessibleChecker::SetExpectedState(LONG expected_state
) {
303 state_
= expected_state
;
306 void AccessibleChecker::CheckAccessibleName(IAccessible
* accessible
) {
307 base::win::ScopedBstr name
;
308 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
309 HRESULT hr
= accessible
->get_accName(childid_self
, name
.Receive());
312 // If the object doesn't have name S_FALSE should be returned.
313 EXPECT_EQ(S_FALSE
, hr
);
315 // Test that the correct string was returned.
317 EXPECT_EQ(name_
, std::wstring(name
, name
.Length()));
321 void AccessibleChecker::CheckAccessibleRole(IAccessible
* accessible
) {
322 base::win::ScopedVariant role
;
323 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
324 HRESULT hr
= accessible
->get_accRole(childid_self
, role
.Receive());
326 EXPECT_EQ(0, role_
.Compare(role
))
327 << "Expected role: " << RoleVariantToString(role_
)
328 << "\nGot role: " << RoleVariantToString(role
);
331 void AccessibleChecker::CheckIA2Role(IAccessible
* accessible
) {
332 base::win::ScopedComPtr
<IAccessible2
> accessible2
;
333 HRESULT hr
= QueryIAccessible2(accessible
, accessible2
.Receive());
336 hr
= accessible2
->role(&ia2_role
);
338 EXPECT_EQ(ia2_role_
, ia2_role
)
339 << "Expected ia2 role: " << IAccessible2RoleToString(ia2_role_
)
340 << "\nGot ia2 role: " << IAccessible2RoleToString(ia2_role
);
343 void AccessibleChecker::CheckAccessibleValue(IAccessible
* accessible
) {
344 // Don't check the value if if's a DOCUMENT role, because the value
345 // is supposed to be the url (and we don't keep track of that in the
346 // test expectations).
347 base::win::ScopedVariant role
;
348 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
349 HRESULT hr
= accessible
->get_accRole(childid_self
, role
.Receive());
351 if (role
.type() == VT_I4
&& V_I4(&role
) == ROLE_SYSTEM_DOCUMENT
)
355 base::win::ScopedBstr value
;
356 hr
= accessible
->get_accValue(childid_self
, value
.Receive());
359 // Test that the correct string was returned.
360 EXPECT_EQ(value_
, std::wstring(value
, value
.Length()));
363 void AccessibleChecker::CheckAccessibleState(IAccessible
* accessible
) {
367 base::win::ScopedVariant state
;
368 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
369 HRESULT hr
= accessible
->get_accState(childid_self
, state
.Receive());
371 ASSERT_EQ(VT_I4
, state
.type());
372 LONG obj_state
= V_I4(&state
);
373 // Avoid flakiness. The "offscreen" state depends on whether the browser
374 // window is frontmost or not, and "hottracked" depends on whether the
375 // mouse cursor happens to be over the element.
376 obj_state
&= ~(STATE_SYSTEM_OFFSCREEN
| STATE_SYSTEM_HOTTRACKED
);
377 EXPECT_EQ(state_
, obj_state
)
378 << "Expected state: " << IAccessibleStateToString(state_
)
379 << "\nGot state: " << IAccessibleStateToString(obj_state
);
382 void AccessibleChecker::CheckAccessibleChildren(IAccessible
* parent
) {
383 LONG child_count
= 0;
384 HRESULT hr
= parent
->get_accChildCount(&child_count
);
386 ASSERT_EQ(child_count
, children_
.size());
388 scoped_ptr
<VARIANT
[]> child_array(new VARIANT
[child_count
]);
389 LONG obtained_count
= 0;
390 hr
= AccessibleChildren(parent
, 0, child_count
,
391 child_array
.get(), &obtained_count
);
393 ASSERT_EQ(child_count
, obtained_count
);
395 VARIANT
* child
= child_array
.get();
396 for (AccessibleCheckerVector::iterator child_checker
= children_
.begin();
397 child_checker
!= children_
.end();
398 ++child_checker
, ++child
) {
399 base::win::ScopedComPtr
<IAccessible
> child_accessible(
400 GetAccessibleFromResultVariant(parent
, child
));
401 ASSERT_TRUE(child_accessible
.get());
402 (*child_checker
)->CheckAccessible(child_accessible
);
406 base::string16
AccessibleChecker::RoleVariantToString(
407 const base::win::ScopedVariant
& role
) {
408 if (role
.type() == VT_I4
)
409 return IAccessibleRoleToString(V_I4(&role
));
410 if (role
.type() == VT_BSTR
)
411 return base::string16(V_BSTR(&role
), SysStringLen(V_BSTR(&role
)));
412 return base::string16();
418 // Tests ----------------------------------------------------------------------
420 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
421 MAYBE(TestBusyAccessibilityTree
)) {
422 NavigateToURL(shell(), GURL(kAboutBlankURL
));
424 // The initial accessible returned should have state STATE_SYSTEM_BUSY while
425 // the accessibility tree is being requested from the renderer.
426 AccessibleChecker
document1_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
428 document1_checker
.SetExpectedState(
429 STATE_SYSTEM_READONLY
| STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_FOCUSED
|
431 document1_checker
.CheckAccessible(GetRendererAccessible());
434 // Flaky, http://crbug.com/167320 .
435 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
436 DISABLED_TestRendererAccessibilityTree
) {
437 LoadInitialAccessibilityTreeFromHtml(
438 "<html><head><title>Accessibility Win Test</title></head>"
439 "<body><input type='button' value='push' /><input type='checkbox' />"
442 // Check the browser's copy of the renderer accessibility tree.
443 AccessibleChecker
button_checker(L
"push", ROLE_SYSTEM_PUSHBUTTON
,
445 AccessibleChecker
checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON
,
447 AccessibleChecker
body_checker(std::wstring(), L
"body", IA2_ROLE_SECTION
,
449 AccessibleChecker
document2_checker(L
"Accessibility Win Test",
450 ROLE_SYSTEM_DOCUMENT
, std::wstring());
451 body_checker
.AppendExpectedChild(&button_checker
);
452 body_checker
.AppendExpectedChild(&checkbox_checker
);
453 document2_checker
.AppendExpectedChild(&body_checker
);
454 document2_checker
.CheckAccessible(GetRendererAccessible());
456 // Check that document accessible has a parent accessible.
457 base::win::ScopedComPtr
<IAccessible
> document_accessible(
458 GetRendererAccessible());
459 ASSERT_NE(document_accessible
.get(), reinterpret_cast<IAccessible
*>(NULL
));
460 base::win::ScopedComPtr
<IDispatch
> parent_dispatch
;
461 HRESULT hr
= document_accessible
->get_accParent(parent_dispatch
.Receive());
463 EXPECT_NE(parent_dispatch
, reinterpret_cast<IDispatch
*>(NULL
));
465 // Navigate to another page.
466 NavigateToURL(shell(), GURL(kAboutBlankURL
));
468 // Verify that the IAccessible reference still points to a valid object and
469 // that calls to its methods fail since the tree is no longer valid after
470 // the page navagation.
471 base::win::ScopedBstr name
;
472 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
473 hr
= document_accessible
->get_accName(childid_self
, name
.Receive());
474 ASSERT_EQ(E_FAIL
, hr
);
477 // Periodically failing. See crbug.com/145537
478 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
479 DISABLED_TestNotificationActiveDescendantChanged
) {
480 LoadInitialAccessibilityTreeFromHtml(
481 "<ul tabindex='-1' role='radiogroup' aria-label='ul'>"
482 "<li id='li'>li</li></ul>");
484 // Check the browser's copy of the renderer accessibility tree.
485 AccessibleChecker
list_marker_checker(L
"\x2022", ROLE_SYSTEM_TEXT
,
487 AccessibleChecker
static_text_checker(L
"li", ROLE_SYSTEM_TEXT
,
489 AccessibleChecker
list_item_checker(std::wstring(), ROLE_SYSTEM_LISTITEM
,
491 list_item_checker
.SetExpectedState(STATE_SYSTEM_READONLY
);
492 AccessibleChecker
radio_group_checker(L
"ul", ROLE_SYSTEM_GROUPING
,
493 IA2_ROLE_SECTION
, std::wstring());
494 radio_group_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
495 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
497 list_item_checker
.AppendExpectedChild(&list_marker_checker
);
498 list_item_checker
.AppendExpectedChild(&static_text_checker
);
499 radio_group_checker
.AppendExpectedChild(&list_item_checker
);
500 document_checker
.AppendExpectedChild(&radio_group_checker
);
501 document_checker
.CheckAccessible(GetRendererAccessible());
503 // Set focus to the radio group.
504 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
505 new AccessibilityNotificationWaiter(
506 shell(), AccessibilityModeComplete
,
507 ui::AX_EVENT_FOCUS
));
508 ExecuteScript(L
"document.body.children[0].focus()");
509 waiter
->WaitForNotification();
511 // Check that the accessibility tree of the browser has been updated.
512 radio_group_checker
.SetExpectedState(
513 STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_FOCUSED
);
514 document_checker
.CheckAccessible(GetRendererAccessible());
516 // Set the active descendant of the radio group
517 waiter
.reset(new AccessibilityNotificationWaiter(
518 shell(), AccessibilityModeComplete
,
519 ui::AX_EVENT_FOCUS
));
521 L
"document.body.children[0].setAttribute('aria-activedescendant', 'li')");
522 waiter
->WaitForNotification();
524 // Check that the accessibility tree of the browser has been updated.
525 list_item_checker
.SetExpectedState(
526 STATE_SYSTEM_READONLY
| STATE_SYSTEM_FOCUSED
);
527 radio_group_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
528 document_checker
.CheckAccessible(GetRendererAccessible());
531 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
532 MAYBE(TestNotificationCheckedStateChanged
)) {
533 LoadInitialAccessibilityTreeFromHtml(
534 "<body><input type='checkbox' /></body>");
536 // Check the browser's copy of the renderer accessibility tree.
537 AccessibleChecker
checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON
,
539 checkbox_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
540 AccessibleChecker
body_checker(std::wstring(), L
"body", IA2_ROLE_SECTION
,
542 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
544 body_checker
.AppendExpectedChild(&checkbox_checker
);
545 document_checker
.AppendExpectedChild(&body_checker
);
546 document_checker
.CheckAccessible(GetRendererAccessible());
548 // Check the checkbox.
549 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
550 new AccessibilityNotificationWaiter(
551 shell(), AccessibilityModeComplete
,
552 ui::AX_EVENT_CHECKED_STATE_CHANGED
));
553 ExecuteScript(L
"document.body.children[0].checked=true");
554 waiter
->WaitForNotification();
556 // Check that the accessibility tree of the browser has been updated.
557 checkbox_checker
.SetExpectedState(
558 STATE_SYSTEM_CHECKED
| STATE_SYSTEM_FOCUSABLE
);
559 document_checker
.CheckAccessible(GetRendererAccessible());
562 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
563 MAYBE(TestNotificationChildrenChanged
)) {
564 // The role attribute causes the node to be in the accessibility tree.
565 LoadInitialAccessibilityTreeFromHtml("<body role=group></body>");
567 // Check the browser's copy of the renderer accessibility tree.
568 AccessibleChecker
group_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
570 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
572 document_checker
.AppendExpectedChild(&group_checker
);
573 document_checker
.CheckAccessible(GetRendererAccessible());
575 // Change the children of the document body.
576 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
577 new AccessibilityNotificationWaiter(
579 AccessibilityModeComplete
,
580 ui::AX_EVENT_CHILDREN_CHANGED
));
581 ExecuteScript(L
"document.body.innerHTML='<b>new text</b>'");
582 waiter
->WaitForNotification();
584 // Check that the accessibility tree of the browser has been updated.
585 AccessibleChecker
text_checker(L
"new text", ROLE_SYSTEM_TEXT
, std::wstring());
586 group_checker
.AppendExpectedChild(&text_checker
);
587 document_checker
.CheckAccessible(GetRendererAccessible());
590 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
591 MAYBE(TestNotificationChildrenChanged2
)) {
592 // The role attribute causes the node to be in the accessibility tree.
593 LoadInitialAccessibilityTreeFromHtml(
594 "<div role=group style='visibility: hidden'>text</div>");
596 // Check the accessible tree of the browser.
597 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
599 document_checker
.CheckAccessible(GetRendererAccessible());
601 // Change the children of the document body.
602 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
603 new AccessibilityNotificationWaiter(
604 shell(), AccessibilityModeComplete
,
605 ui::AX_EVENT_CHILDREN_CHANGED
));
606 ExecuteScript(L
"document.body.children[0].style.visibility='visible'");
607 waiter
->WaitForNotification();
609 // Check that the accessibility tree of the browser has been updated.
610 AccessibleChecker
static_text_checker(L
"text", ROLE_SYSTEM_TEXT
,
612 AccessibleChecker
group_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
614 document_checker
.AppendExpectedChild(&group_checker
);
615 group_checker
.AppendExpectedChild(&static_text_checker
);
616 document_checker
.CheckAccessible(GetRendererAccessible());
619 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
620 MAYBE(TestNotificationFocusChanged
)) {
621 // The role attribute causes the node to be in the accessibility tree.
622 LoadInitialAccessibilityTreeFromHtml("<div role=group tabindex='-1'></div>");
624 // Check the browser's copy of the renderer accessibility tree.
625 SCOPED_TRACE("Check initial tree");
626 AccessibleChecker
group_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
628 group_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
629 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
631 document_checker
.AppendExpectedChild(&group_checker
);
632 document_checker
.CheckAccessible(GetRendererAccessible());
634 // Focus the div in the document
635 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
636 new AccessibilityNotificationWaiter(
637 shell(), AccessibilityModeComplete
,
638 ui::AX_EVENT_FOCUS
));
639 ExecuteScript(L
"document.body.children[0].focus()");
640 waiter
->WaitForNotification();
642 // Check that the accessibility tree of the browser has been updated.
643 SCOPED_TRACE("Check updated tree after focusing div");
644 group_checker
.SetExpectedState(
645 STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_FOCUSED
);
646 document_checker
.CheckAccessible(GetRendererAccessible());
648 // Focus the document accessible. This will un-focus the current node.
650 new AccessibilityNotificationWaiter(
651 shell(), AccessibilityModeComplete
,
653 base::win::ScopedComPtr
<IAccessible
> document_accessible(
654 GetRendererAccessible());
655 ASSERT_NE(document_accessible
.get(), reinterpret_cast<IAccessible
*>(NULL
));
656 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
657 HRESULT hr
= document_accessible
->accSelect(SELFLAG_TAKEFOCUS
, childid_self
);
659 waiter
->WaitForNotification();
661 // Check that the accessibility tree of the browser has been updated.
662 SCOPED_TRACE("Check updated tree after focusing document again");
663 group_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
664 document_checker
.CheckAccessible(GetRendererAccessible());
667 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
668 MAYBE(TestNotificationValueChanged
)) {
669 LoadInitialAccessibilityTreeFromHtml(
670 "<body><input type='text' value='old value'/></body>");
672 // Check the browser's copy of the renderer accessibility tree.
673 AccessibleChecker
text_field_checker(std::wstring(), ROLE_SYSTEM_TEXT
,
675 text_field_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
676 AccessibleChecker
body_checker(std::wstring(), L
"body", IA2_ROLE_SECTION
,
678 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
680 body_checker
.AppendExpectedChild(&text_field_checker
);
681 document_checker
.AppendExpectedChild(&body_checker
);
682 document_checker
.CheckAccessible(GetRendererAccessible());
684 // Set the value of the text control
685 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
686 new AccessibilityNotificationWaiter(
687 shell(), AccessibilityModeComplete
,
688 ui::AX_EVENT_VALUE_CHANGE
));
689 ExecuteScript(L
"document.body.children[0].value='new value'");
690 waiter
->WaitForNotification();
692 // Check that the accessibility tree of the browser has been updated.
693 text_field_checker
.SetExpectedValue(L
"new value");
694 document_checker
.CheckAccessible(GetRendererAccessible());
697 // This test verifies that the web content's accessibility tree is a
698 // descendant of the main browser window's accessibility tree, so that
699 // tools like AccExplorer32 or AccProbe can be used to examine Chrome's
700 // accessibility support.
702 // If you made a change and this test now fails, check that the NativeViewHost
703 // that wraps the tab contents returns the IAccessible implementation
704 // provided by RenderWidgetHostViewWin in GetNativeViewAccessible().
705 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
706 MAYBE(ContainsRendererAccessibilityTree
)) {
707 LoadInitialAccessibilityTreeFromHtml(
708 "<html><head><title>MyDocument</title></head>"
709 "<body>Content</body></html>");
711 // Get the accessibility object for the browser window.
712 HWND browser_hwnd
= shell()->window();
713 base::win::ScopedComPtr
<IAccessible
> browser_accessible
;
714 HRESULT hr
= AccessibleObjectFromWindow(
718 reinterpret_cast<void**>(browser_accessible
.Receive()));
722 RecursiveFindNodeInAccessibilityTree(
723 browser_accessible
.get(), ROLE_SYSTEM_DOCUMENT
, L
"MyDocument", 0, &found
);
724 ASSERT_EQ(found
, true);
727 // Disabled because of http://crbug.com/144390.
728 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
729 DISABLED_TestToggleButtonRoleAndStates
) {
730 AccessibleChecker
* button_checker
;
731 std::string
button_html("data:text/html,");
732 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
734 AccessibleChecker
body_checker(std::wstring(), L
"body", IA2_ROLE_SECTION
,
736 document_checker
.AppendExpectedChild(&body_checker
);
739 #define ADD_BUTTON(html, ia2_role, state) \
740 button_html += html; \
741 button_checker = new AccessibleChecker(L"x", ROLE_SYSTEM_PUSHBUTTON, \
742 ia2_role, std::wstring()); \
743 button_checker->SetExpectedState(state); \
744 body_checker.AppendExpectedChild(button_checker)
746 // If aria-pressed is 'undefined', empty or not present, use PUSHBUTTON
747 // Otherwise use TOGGLE_BUTTON, even if the value is invalid.
748 // The spec does this in an attempt future-proof in case new values are added.
749 ADD_BUTTON("<span role='button' aria-pressed='false'>x</span>",
750 IA2_ROLE_TOGGLE_BUTTON
, 0);
751 ADD_BUTTON("<span role='button' aria-pressed='true'>x</span>",
752 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_PRESSED
);
753 ADD_BUTTON("<span role='button' aria-pressed='mixed'>x</span>",
754 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_MIXED
);
755 ADD_BUTTON("<span role='button' aria-pressed='xyz'>x</span>",
756 IA2_ROLE_TOGGLE_BUTTON
, 0);
757 ADD_BUTTON("<span role='button' aria-pressed=''>x</span>",
758 ROLE_SYSTEM_PUSHBUTTON
, 0);
759 ADD_BUTTON("<span role='button' aria-pressed>x</span>",
760 ROLE_SYSTEM_PUSHBUTTON
, 0);
761 ADD_BUTTON("<span role='button' aria-pressed='undefined'>x</span>",
762 ROLE_SYSTEM_PUSHBUTTON
, 0);
763 ADD_BUTTON("<span role='button'>x</span>", ROLE_SYSTEM_PUSHBUTTON
, 0);
764 ADD_BUTTON("<input type='button' aria-pressed='true' value='x'/>",
765 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_PRESSED
);
766 ADD_BUTTON("<input type='button' aria-pressed='false' value='x'/>",
767 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_FOCUSABLE
);
768 ADD_BUTTON("<input type='button' aria-pressed='mixed' value='x'>",
769 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_MIXED
);
770 ADD_BUTTON("<input type='button' aria-pressed='xyz' value='x'/>",
771 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_FOCUSABLE
);
772 ADD_BUTTON("<input type='button' aria-pressed='' value='x'/>",
773 ROLE_SYSTEM_PUSHBUTTON
, STATE_SYSTEM_FOCUSABLE
);
774 ADD_BUTTON("<input type='button' aria-pressed value='x'>",
775 ROLE_SYSTEM_PUSHBUTTON
, STATE_SYSTEM_FOCUSABLE
);
776 ADD_BUTTON("<input type='button' aria-pressed='undefined' value='x'>",
777 ROLE_SYSTEM_PUSHBUTTON
, STATE_SYSTEM_FOCUSABLE
);
778 ADD_BUTTON("<input type='button' value='x'>",
779 ROLE_SYSTEM_PUSHBUTTON
, STATE_SYSTEM_FOCUSABLE
);
780 ADD_BUTTON("<button aria-pressed='true'>x</button>",
781 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_PRESSED
);
782 ADD_BUTTON("<button aria-pressed='false'>x</button>",
783 IA2_ROLE_TOGGLE_BUTTON
, STATE_SYSTEM_FOCUSABLE
);
784 ADD_BUTTON("<button aria-pressed='mixed'>x</button>", IA2_ROLE_TOGGLE_BUTTON
,
785 STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_MIXED
);
786 ADD_BUTTON("<button aria-pressed='xyz'>x</button>", IA2_ROLE_TOGGLE_BUTTON
,
787 STATE_SYSTEM_FOCUSABLE
);
788 ADD_BUTTON("<button aria-pressed=''>x</button>",
789 ROLE_SYSTEM_PUSHBUTTON
, STATE_SYSTEM_FOCUSABLE
);
790 ADD_BUTTON("<button aria-pressed>x</button>",
791 ROLE_SYSTEM_PUSHBUTTON
, STATE_SYSTEM_FOCUSABLE
);
792 ADD_BUTTON("<button aria-pressed='undefined'>x</button>",
793 ROLE_SYSTEM_PUSHBUTTON
, STATE_SYSTEM_FOCUSABLE
);
794 ADD_BUTTON("<button>x</button>", ROLE_SYSTEM_PUSHBUTTON
,
795 STATE_SYSTEM_FOCUSABLE
);
796 #undef ADD_BUTTON // Temporary macro
798 LoadInitialAccessibilityTreeFromHtml(button_html
);
799 document_checker
.CheckAccessible(GetRendererAccessible());
802 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
803 MAYBE(SupportsISimpleDOM
)) {
804 LoadInitialAccessibilityTreeFromHtml(
805 "<body><input type='checkbox' /></body>");
807 // Get the IAccessible object for the document.
808 base::win::ScopedComPtr
<IAccessible
> document_accessible(
809 GetRendererAccessible());
810 ASSERT_NE(document_accessible
.get(), reinterpret_cast<IAccessible
*>(NULL
));
812 // Get the ISimpleDOM object for the document.
813 base::win::ScopedComPtr
<IServiceProvider
> service_provider
;
814 HRESULT hr
= static_cast<IAccessible
*>(document_accessible
)->QueryInterface(
815 service_provider
.Receive());
817 const GUID refguid
= {0x0c539790, 0x12e4, 0x11cf,
818 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
819 base::win::ScopedComPtr
<ISimpleDOMNode
> document_isimpledomnode
;
820 hr
= static_cast<IServiceProvider
*>(service_provider
)->QueryService(
821 refguid
, IID_ISimpleDOMNode
,
822 reinterpret_cast<void**>(document_isimpledomnode
.Receive()));
825 base::win::ScopedBstr node_name
;
826 short name_space_id
; // NOLINT
827 base::win::ScopedBstr node_value
;
828 unsigned int num_children
;
829 unsigned int unique_id
;
830 unsigned short node_type
; // NOLINT
831 hr
= document_isimpledomnode
->get_nodeInfo(
832 node_name
.Receive(), &name_space_id
, node_value
.Receive(), &num_children
,
833 &unique_id
, &node_type
);
835 EXPECT_EQ(NODETYPE_DOCUMENT
, node_type
);
836 EXPECT_EQ(1, num_children
);
840 base::win::ScopedComPtr
<ISimpleDOMNode
> body_isimpledomnode
;
841 hr
= document_isimpledomnode
->get_firstChild(
842 body_isimpledomnode
.Receive());
844 hr
= body_isimpledomnode
->get_nodeInfo(
845 node_name
.Receive(), &name_space_id
, node_value
.Receive(), &num_children
,
846 &unique_id
, &node_type
);
848 EXPECT_EQ(L
"body", std::wstring(node_name
, node_name
.Length()));
849 EXPECT_EQ(NODETYPE_ELEMENT
, node_type
);
850 EXPECT_EQ(1, num_children
);
854 base::win::ScopedComPtr
<ISimpleDOMNode
> checkbox_isimpledomnode
;
855 hr
= body_isimpledomnode
->get_firstChild(
856 checkbox_isimpledomnode
.Receive());
858 hr
= checkbox_isimpledomnode
->get_nodeInfo(
859 node_name
.Receive(), &name_space_id
, node_value
.Receive(), &num_children
,
860 &unique_id
, &node_type
);
862 EXPECT_EQ(L
"input", std::wstring(node_name
, node_name
.Length()));
863 EXPECT_EQ(NODETYPE_ELEMENT
, node_type
);
864 EXPECT_EQ(0, num_children
);
867 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
, MAYBE(TestRoleGroup
)) {
868 LoadInitialAccessibilityTreeFromHtml(
869 "<fieldset></fieldset><div role=group></div>");
871 // Check the browser's copy of the renderer accessibility tree.
872 AccessibleChecker
grouping1_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
874 AccessibleChecker
grouping2_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
876 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
878 document_checker
.AppendExpectedChild(&grouping1_checker
);
879 document_checker
.AppendExpectedChild(&grouping2_checker
);
880 document_checker
.CheckAccessible(GetRendererAccessible());
883 } // namespace content