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. 4 5 #include <vector> 6 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/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" 26 27 // TODO(dmazzoni): Disabled accessibility tests on Win64. crbug.com/179717 28 #if defined(ARCH_CPU_X86_64) 29 #define MAYBE(x) DISABLED_##x 30 #else 31 #define MAYBE(x) x 32 #endif 33 34 namespace content { 35 36 namespace { 37 38 39 // Helpers -------------------------------------------------------------------- 40 41 base::win::ScopedComPtr<IAccessible> GetAccessibleFromResultVariant( 42 IAccessible* parent, 43 VARIANT* var) { 44 base::win::ScopedComPtr<IAccessible> ptr; 45 switch (V_VT(var)) { 46 case VT_DISPATCH: { 47 IDispatch* dispatch = V_DISPATCH(var); 48 if (dispatch) 49 ptr.QueryFrom(dispatch); 50 break; 51 } 52 53 case VT_I4: { 54 base::win::ScopedComPtr<IDispatch> dispatch; 55 HRESULT hr = parent->get_accChild(*var, dispatch.Receive()); 56 EXPECT_TRUE(SUCCEEDED(hr)); 57 if (dispatch) 58 dispatch.QueryInterface(ptr.Receive()); 59 break; 60 } 61 } 62 return ptr; 63 } 64 65 HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) { 66 // TODO(ctguil): For some reason querying the IAccessible2 interface from 67 // IAccessible fails. 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; 72 } 73 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 76 // and name. 77 void RecursiveFindNodeInAccessibilityTree(IAccessible* node, 78 int32 expected_role, 79 const std::wstring& expected_name, 80 int32 depth, 81 bool* found) { 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()); 89 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++) 93 printf(" "); 94 printf("role=%d name=%s\n", V_I4(&role), WideToUTF8(name).c_str()); 95 96 if (expected_role == V_I4(&role) && expected_name == name) { 97 *found = true; 98 return; 99 } 100 101 LONG child_count = 0; 102 HRESULT hr = node->get_accChildCount(&child_count); 103 ASSERT_EQ(S_OK, hr); 104 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); 109 ASSERT_EQ(S_OK, hr); 110 ASSERT_EQ(child_count, obtained_count); 111 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, 118 found); 119 if (*found) 120 return; 121 } 122 } 123 } 124 125 126 // AccessibilityWinBrowserTest ------------------------------------------------ 127 128 class AccessibilityWinBrowserTest : public ContentBrowserTest { 129 public: 130 AccessibilityWinBrowserTest(); 131 virtual ~AccessibilityWinBrowserTest(); 132 133 protected: 134 void LoadInitialAccessibilityTreeFromHtml(const std::string& html); 135 IAccessible* GetRendererAccessible(); 136 void ExecuteScript(const std::wstring& script); 137 138 private: 139 DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest); 140 }; 141 142 AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() { 143 } 144 145 AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() { 146 } 147 148 void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml( 149 const std::string& html) { 150 AccessibilityNotificationWaiter waiter( 151 shell(), AccessibilityModeComplete, 152 AccessibilityNotificationLoadComplete); 153 GURL html_data_url("data:text/html," + html); 154 NavigateToURL(shell(), html_data_url); 155 waiter.WaitForNotification(); 156 } 157 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(); 163 164 // Invoke windows screen reader detection by sending the WM_GETOBJECT message 165 // with kIdCustom as the LPARAM. 166 const int32 kIdCustom = 1; 167 SendMessage( 168 hwnd_render_widget_host_view, WM_GETOBJECT, OBJID_CLIENT, kIdCustom); 169 170 IAccessible* accessible; 171 HRESULT hr = AccessibleObjectFromWindow( 172 hwnd_render_widget_host_view, OBJID_CLIENT, 173 IID_IAccessible, reinterpret_cast<void**>(&accessible)); 174 175 EXPECT_EQ(S_OK, hr); 176 EXPECT_NE(accessible, reinterpret_cast<IAccessible*>(NULL)); 177 178 return accessible; 179 } 180 181 void AccessibilityWinBrowserTest::ExecuteScript(const std::wstring& script) { 182 shell()->web_contents()->GetRenderViewHost()->ExecuteJavascriptInWebFrame( 183 std::wstring(), script); 184 } 185 186 187 // AccessibleChecker ---------------------------------------------------------- 188 189 class AccessibleChecker { 190 public: 191 // This constructor can be used if the IA2 role will be the same as the MSAA 192 // role. 193 AccessibleChecker(const std::wstring& expected_name, 194 int32 expected_role, 195 const std::wstring& expected_value); 196 AccessibleChecker(const std::wstring& expected_name, 197 int32 expected_role, 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); 204 205 // Append an AccessibleChecker that verifies accessibility information for 206 // a child IAccessible. Order is important. 207 void AppendExpectedChild(AccessibleChecker* expected_child); 208 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 211 // initialized with. 212 void CheckAccessible(IAccessible* accessible); 213 214 // Set the expected value for this AccessibleChecker. 215 void SetExpectedValue(const std::wstring& expected_value); 216 217 // Set the expected state for this AccessibleChecker. 218 void SetExpectedState(LONG expected_state); 219 220 private: 221 typedef std::vector<AccessibleChecker*> AccessibleCheckerVector; 222 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 string16 RoleVariantToString(const base::win::ScopedVariant& role); 230 231 // Expected accessible name. Checked against IAccessible::get_accName. 232 std::wstring name_; 233 234 // Expected accessible role. Checked against IAccessible::get_accRole. 235 base::win::ScopedVariant role_; 236 237 // Expected IAccessible2 role. Checked against IAccessible2::role. 238 int32 ia2_role_; 239 240 // Expected accessible value. Checked against IAccessible::get_accValue. 241 std::wstring value_; 242 243 // Expected accessible state. Checked against IAccessible::get_accState. 244 LONG state_; 245 246 // Expected accessible children. Checked using IAccessible::get_accChildCount 247 // and ::AccessibleChildren. 248 AccessibleCheckerVector children_; 249 }; 250 251 AccessibleChecker::AccessibleChecker(const std::wstring& expected_name, 252 int32 expected_role, 253 const std::wstring& expected_value) 254 : name_(expected_name), 255 role_(expected_role), 256 ia2_role_(expected_role), 257 value_(expected_value), 258 state_(-1) { 259 } 260 261 AccessibleChecker::AccessibleChecker(const std::wstring& expected_name, 262 int32 expected_role, 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), 269 state_(-1) { 270 } 271 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), 280 state_(-1) { 281 } 282 283 void AccessibleChecker::AppendExpectedChild( 284 AccessibleChecker* expected_child) { 285 children_.push_back(expected_child); 286 } 287 288 void AccessibleChecker::CheckAccessible(IAccessible* accessible) { 289 SCOPED_TRACE("while checking " + UTF16ToUTF8(RoleVariantToString(role_))); 290 CheckAccessibleName(accessible); 291 CheckAccessibleRole(accessible); 292 CheckIA2Role(accessible); 293 CheckAccessibleValue(accessible); 294 CheckAccessibleState(accessible); 295 CheckAccessibleChildren(accessible); 296 } 297 298 void AccessibleChecker::SetExpectedValue(const std::wstring& expected_value) { 299 value_ = expected_value; 300 } 301 302 void AccessibleChecker::SetExpectedState(LONG expected_state) { 303 state_ = expected_state; 304 } 305 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()); 310 311 if (name_.empty()) { 312 // If the object doesn't have name S_FALSE should be returned. 313 EXPECT_EQ(S_FALSE, hr); 314 } else { 315 // Test that the correct string was returned. 316 EXPECT_EQ(S_OK, hr); 317 EXPECT_EQ(name_, std::wstring(name, name.Length())); 318 } 319 } 320 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()); 325 ASSERT_EQ(S_OK, hr); 326 EXPECT_EQ(0, role_.Compare(role)) 327 << "Expected role: " << RoleVariantToString(role_) 328 << "\nGot role: " << RoleVariantToString(role); 329 } 330 331 void AccessibleChecker::CheckIA2Role(IAccessible* accessible) { 332 base::win::ScopedComPtr<IAccessible2> accessible2; 333 HRESULT hr = QueryIAccessible2(accessible, accessible2.Receive()); 334 ASSERT_EQ(S_OK, hr); 335 long ia2_role = 0; 336 hr = accessible2->role(&ia2_role); 337 ASSERT_EQ(S_OK, hr); 338 EXPECT_EQ(ia2_role_, ia2_role) 339 << "Expected ia2 role: " << IAccessible2RoleToString(ia2_role_) 340 << "\nGot ia2 role: " << IAccessible2RoleToString(ia2_role); 341 } 342 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()); 350 ASSERT_EQ(S_OK, hr); 351 if (role.type() == VT_I4 && V_I4(&role) == ROLE_SYSTEM_DOCUMENT) 352 return; 353 354 // Get the value. 355 base::win::ScopedBstr value; 356 hr = accessible->get_accValue(childid_self, value.Receive()); 357 EXPECT_EQ(S_OK, hr); 358 359 // Test that the correct string was returned. 360 EXPECT_EQ(value_, std::wstring(value, value.Length())); 361 } 362 363 void AccessibleChecker::CheckAccessibleState(IAccessible* accessible) { 364 if (state_ < 0) 365 return; 366 367 base::win::ScopedVariant state; 368 base::win::ScopedVariant childid_self(CHILDID_SELF); 369 HRESULT hr = accessible->get_accState(childid_self, state.Receive()); 370 EXPECT_EQ(S_OK, hr); 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); 380 } 381 382 void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) { 383 LONG child_count = 0; 384 HRESULT hr = parent->get_accChildCount(&child_count); 385 EXPECT_EQ(S_OK, hr); 386 ASSERT_EQ(child_count, children_.size()); 387 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); 392 ASSERT_EQ(S_OK, hr); 393 ASSERT_EQ(child_count, obtained_count); 394 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); 403 } 404 } 405 406 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 string16(V_BSTR(&role), SysStringLen(V_BSTR(&role))); 412 return string16(); 413 } 414 415 } // namespace 416 417 418 // Tests ---------------------------------------------------------------------- 419 420 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 421 MAYBE(TestBusyAccessibilityTree)) { 422 NavigateToURL(shell(), GURL(kAboutBlankURL)); 423 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, 427 std::wstring()); 428 document1_checker.SetExpectedState( 429 STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED | 430 STATE_SYSTEM_BUSY); 431 document1_checker.CheckAccessible(GetRendererAccessible()); 432 } 433 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' />" 440 "</body></html>"); 441 442 // Check the browser's copy of the renderer accessibility tree. 443 AccessibleChecker button_checker(L"push", ROLE_SYSTEM_PUSHBUTTON, 444 std::wstring()); 445 AccessibleChecker checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON, 446 std::wstring()); 447 AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION, 448 std::wstring()); 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()); 455 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()); 462 EXPECT_EQ(S_OK, hr); 463 EXPECT_NE(parent_dispatch, reinterpret_cast<IDispatch*>(NULL)); 464 465 // Navigate to another page. 466 NavigateToURL(shell(), GURL(kAboutBlankURL)); 467 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); 475 } 476 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>"); 483 484 // Check the browser's copy of the renderer accessibility tree. 485 AccessibleChecker list_marker_checker(L"\x2022", ROLE_SYSTEM_TEXT, 486 std::wstring()); 487 AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT, 488 std::wstring()); 489 AccessibleChecker list_item_checker(std::wstring(), ROLE_SYSTEM_LISTITEM, 490 std::wstring()); 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, 496 std::wstring()); 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()); 502 503 // Set focus to the radio group. 504 scoped_ptr<AccessibilityNotificationWaiter> waiter( 505 new AccessibilityNotificationWaiter( 506 shell(), AccessibilityModeComplete, 507 AccessibilityNotificationFocusChanged)); 508 ExecuteScript(L"document.body.children[0].focus()"); 509 waiter->WaitForNotification(); 510 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()); 515 516 // Set the active descendant of the radio group 517 waiter.reset(new AccessibilityNotificationWaiter( 518 shell(), AccessibilityModeComplete, 519 AccessibilityNotificationFocusChanged)); 520 ExecuteScript( 521 L"document.body.children[0].setAttribute('aria-activedescendant', 'li')"); 522 waiter->WaitForNotification(); 523 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()); 529 } 530 531 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 532 MAYBE(TestNotificationCheckedStateChanged)) { 533 LoadInitialAccessibilityTreeFromHtml( 534 "<body><input type='checkbox' /></body>"); 535 536 // Check the browser's copy of the renderer accessibility tree. 537 AccessibleChecker checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON, 538 std::wstring()); 539 checkbox_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); 540 AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION, 541 std::wstring()); 542 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, 543 std::wstring()); 544 body_checker.AppendExpectedChild(&checkbox_checker); 545 document_checker.AppendExpectedChild(&body_checker); 546 document_checker.CheckAccessible(GetRendererAccessible()); 547 548 // Check the checkbox. 549 scoped_ptr<AccessibilityNotificationWaiter> waiter( 550 new AccessibilityNotificationWaiter( 551 shell(), AccessibilityModeComplete, 552 AccessibilityNotificationCheckStateChanged)); 553 ExecuteScript(L"document.body.children[0].checked=true"); 554 waiter->WaitForNotification(); 555 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()); 560 } 561 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>"); 566 567 // Check the browser's copy of the renderer accessibility tree. 568 AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING, 569 std::wstring()); 570 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, 571 std::wstring()); 572 document_checker.AppendExpectedChild(&group_checker); 573 document_checker.CheckAccessible(GetRendererAccessible()); 574 575 // Change the children of the document body. 576 scoped_ptr<AccessibilityNotificationWaiter> waiter( 577 new AccessibilityNotificationWaiter( 578 shell(), 579 AccessibilityModeComplete, 580 AccessibilityNotificationChildrenChanged)); 581 ExecuteScript(L"document.body.innerHTML='<b>new text</b>'"); 582 waiter->WaitForNotification(); 583 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()); 588 } 589 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>"); 595 596 // Check the accessible tree of the browser. 597 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, 598 std::wstring()); 599 document_checker.CheckAccessible(GetRendererAccessible()); 600 601 // Change the children of the document body. 602 scoped_ptr<AccessibilityNotificationWaiter> waiter( 603 new AccessibilityNotificationWaiter( 604 shell(), AccessibilityModeComplete, 605 AccessibilityNotificationChildrenChanged)); 606 ExecuteScript(L"document.body.children[0].style.visibility='visible'"); 607 waiter->WaitForNotification(); 608 609 // Check that the accessibility tree of the browser has been updated. 610 AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_TEXT, 611 std::wstring()); 612 AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING, 613 std::wstring()); 614 document_checker.AppendExpectedChild(&group_checker); 615 group_checker.AppendExpectedChild(&static_text_checker); 616 document_checker.CheckAccessible(GetRendererAccessible()); 617 } 618 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>"); 623 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, 627 std::wstring()); 628 group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); 629 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, 630 std::wstring()); 631 document_checker.AppendExpectedChild(&group_checker); 632 document_checker.CheckAccessible(GetRendererAccessible()); 633 634 // Focus the div in the document 635 scoped_ptr<AccessibilityNotificationWaiter> waiter( 636 new AccessibilityNotificationWaiter( 637 shell(), AccessibilityModeComplete, 638 AccessibilityNotificationFocusChanged)); 639 ExecuteScript(L"document.body.children[0].focus()"); 640 waiter->WaitForNotification(); 641 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()); 647 648 // Focus the document accessible. This will un-focus the current node. 649 waiter.reset( 650 new AccessibilityNotificationWaiter( 651 shell(), AccessibilityModeComplete, 652 AccessibilityNotificationBlur)); 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); 658 ASSERT_EQ(S_OK, hr); 659 waiter->WaitForNotification(); 660 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()); 665 } 666 667 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 668 MAYBE(TestNotificationValueChanged)) { 669 LoadInitialAccessibilityTreeFromHtml( 670 "<body><input type='text' value='old value'/></body>"); 671 672 // Check the browser's copy of the renderer accessibility tree. 673 AccessibleChecker text_field_checker(std::wstring(), ROLE_SYSTEM_TEXT, 674 L"old value"); 675 text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); 676 AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION, 677 std::wstring()); 678 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, 679 std::wstring()); 680 body_checker.AppendExpectedChild(&text_field_checker); 681 document_checker.AppendExpectedChild(&body_checker); 682 document_checker.CheckAccessible(GetRendererAccessible()); 683 684 // Set the value of the text control 685 scoped_ptr<AccessibilityNotificationWaiter> waiter( 686 new AccessibilityNotificationWaiter( 687 shell(), AccessibilityModeComplete, 688 AccessibilityNotificationValueChanged)); 689 ExecuteScript(L"document.body.children[0].value='new value'"); 690 waiter->WaitForNotification(); 691 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()); 695 } 696 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. 701 // 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>"); 710 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( 715 browser_hwnd, 716 OBJID_WINDOW, 717 IID_IAccessible, 718 reinterpret_cast<void**>(browser_accessible.Receive())); 719 ASSERT_EQ(S_OK, hr); 720 721 bool found = false; 722 RecursiveFindNodeInAccessibilityTree( 723 browser_accessible.get(), ROLE_SYSTEM_DOCUMENT, L"MyDocument", 0, &found); 724 ASSERT_EQ(found, true); 725 } 726 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, 733 std::wstring()); 734 AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION, 735 std::wstring()); 736 document_checker.AppendExpectedChild(&body_checker); 737 738 // Temporary macro 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) 745 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 797 798 LoadInitialAccessibilityTreeFromHtml(button_html); 799 document_checker.CheckAccessible(GetRendererAccessible()); 800 } 801 802 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 803 MAYBE(SupportsISimpleDOM)) { 804 LoadInitialAccessibilityTreeFromHtml( 805 "<body><input type='checkbox' /></body>"); 806 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)); 811 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()); 816 ASSERT_EQ(S_OK, hr); 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())); 823 ASSERT_EQ(S_OK, hr); 824 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); 834 ASSERT_EQ(S_OK, hr); 835 EXPECT_EQ(NODETYPE_DOCUMENT, node_type); 836 EXPECT_EQ(1, num_children); 837 node_name.Reset(); 838 node_value.Reset(); 839 840 base::win::ScopedComPtr<ISimpleDOMNode> body_isimpledomnode; 841 hr = document_isimpledomnode->get_firstChild( 842 body_isimpledomnode.Receive()); 843 ASSERT_EQ(S_OK, hr); 844 hr = body_isimpledomnode->get_nodeInfo( 845 node_name.Receive(), &name_space_id, node_value.Receive(), &num_children, 846 &unique_id, &node_type); 847 ASSERT_EQ(S_OK, hr); 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); 851 node_name.Reset(); 852 node_value.Reset(); 853 854 base::win::ScopedComPtr<ISimpleDOMNode> checkbox_isimpledomnode; 855 hr = body_isimpledomnode->get_firstChild( 856 checkbox_isimpledomnode.Receive()); 857 ASSERT_EQ(S_OK, hr); 858 hr = checkbox_isimpledomnode->get_nodeInfo( 859 node_name.Receive(), &name_space_id, node_value.Receive(), &num_children, 860 &unique_id, &node_type); 861 ASSERT_EQ(S_OK, hr); 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); 865 } 866 867 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, MAYBE(TestRoleGroup)) { 868 LoadInitialAccessibilityTreeFromHtml( 869 "<fieldset></fieldset><div role=group></div>"); 870 871 // Check the browser's copy of the renderer accessibility tree. 872 AccessibleChecker grouping1_checker(std::wstring(), ROLE_SYSTEM_GROUPING, 873 std::wstring()); 874 AccessibleChecker grouping2_checker(std::wstring(), ROLE_SYSTEM_GROUPING, 875 std::wstring()); 876 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, 877 std::wstring()); 878 document_checker.AppendExpectedChild(&grouping1_checker); 879 document_checker.AppendExpectedChild(&grouping2_checker); 880 document_checker.CheckAccessible(GetRendererAccessible()); 881 } 882 883 } // namespace content 884