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_mode_helper.h" 13 #include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h" 14 #include "content/browser/renderer_host/render_view_host_impl.h" 15 #include "content/public/browser/notification_service.h" 16 #include "content/public/browser/notification_types.h" 17 #include "content/public/browser/render_frame_host.h" 18 #include "content/public/browser/render_widget_host_view.h" 19 #include "content/public/browser/web_contents.h" 20 #include "content/public/common/url_constants.h" 21 #include "content/public/test/content_browser_test.h" 22 #include "content/public/test/content_browser_test_utils.h" 23 #include "content/shell/browser/shell.h" 24 #include "content/test/accessibility_browser_test_utils.h" 25 #include "third_party/iaccessible2/ia2_api_all.h" 26 #include "third_party/isimpledom/ISimpleDOMNode.h" 27 #include "ui/aura/window.h" 28 #include "ui/aura/window_tree_host.h" 29 30 namespace content { 31 32 namespace { 33 34 // Helpers -------------------------------------------------------------------- 35 36 base::win::ScopedComPtr<IAccessible> GetAccessibleFromResultVariant( 37 IAccessible* parent, 38 VARIANT* var) { 39 base::win::ScopedComPtr<IAccessible> ptr; 40 switch (V_VT(var)) { 41 case VT_DISPATCH: { 42 IDispatch* dispatch = V_DISPATCH(var); 43 if (dispatch) 44 ptr.QueryFrom(dispatch); 45 break; 46 } 47 48 case VT_I4: { 49 base::win::ScopedComPtr<IDispatch> dispatch; 50 HRESULT hr = parent->get_accChild(*var, dispatch.Receive()); 51 EXPECT_TRUE(SUCCEEDED(hr)); 52 if (dispatch) 53 dispatch.QueryInterface(ptr.Receive()); 54 break; 55 } 56 } 57 return ptr; 58 } 59 60 HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) { 61 // TODO(ctguil): For some reason querying the IAccessible2 interface from 62 // IAccessible fails. 63 base::win::ScopedComPtr<IServiceProvider> service_provider; 64 HRESULT hr = accessible->QueryInterface(service_provider.Receive()); 65 return SUCCEEDED(hr) ? 66 service_provider->QueryService(IID_IAccessible2, accessible2) : hr; 67 } 68 69 // Recursively search through all of the descendants reachable from an 70 // IAccessible node and return true if we find one with the given role 71 // and name. 72 void RecursiveFindNodeInAccessibilityTree(IAccessible* node, 73 int32 expected_role, 74 const std::wstring& expected_name, 75 int32 depth, 76 bool* found) { 77 base::win::ScopedBstr name_bstr; 78 base::win::ScopedVariant childid_self(CHILDID_SELF); 79 node->get_accName(childid_self, name_bstr.Receive()); 80 std::wstring name(name_bstr, name_bstr.Length()); 81 base::win::ScopedVariant role; 82 node->get_accRole(childid_self, role.Receive()); 83 ASSERT_EQ(VT_I4, role.type()); 84 85 // Print the accessibility tree as we go, because if this test fails 86 // on the bots, this is really helpful in figuring out why. 87 for (int i = 0; i < depth; i++) 88 printf(" "); 89 printf("role=%s name=%s\n", 90 base::WideToUTF8(IAccessibleRoleToString(V_I4(&role))).c_str(), 91 base::WideToUTF8(name).c_str()); 92 93 if (expected_role == V_I4(&role) && expected_name == name) { 94 *found = true; 95 return; 96 } 97 98 LONG child_count = 0; 99 HRESULT hr = node->get_accChildCount(&child_count); 100 ASSERT_EQ(S_OK, hr); 101 102 scoped_ptr<VARIANT[]> child_array(new VARIANT[child_count]); 103 LONG obtained_count = 0; 104 hr = AccessibleChildren( 105 node, 0, child_count, child_array.get(), &obtained_count); 106 ASSERT_EQ(S_OK, hr); 107 ASSERT_EQ(child_count, obtained_count); 108 109 for (int index = 0; index < obtained_count; index++) { 110 base::win::ScopedComPtr<IAccessible> child_accessible( 111 GetAccessibleFromResultVariant(node, &child_array.get()[index])); 112 if (child_accessible) { 113 RecursiveFindNodeInAccessibilityTree( 114 child_accessible.get(), expected_role, expected_name, depth + 1, 115 found); 116 if (*found) 117 return; 118 } 119 } 120 } 121 122 123 // AccessibilityWinBrowserTest ------------------------------------------------ 124 125 class AccessibilityWinBrowserTest : public ContentBrowserTest { 126 public: 127 AccessibilityWinBrowserTest(); 128 virtual ~AccessibilityWinBrowserTest(); 129 130 protected: 131 void LoadInitialAccessibilityTreeFromHtml(const std::string& html); 132 IAccessible* GetRendererAccessible(); 133 void ExecuteScript(const std::wstring& script); 134 135 private: 136 DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest); 137 }; 138 139 AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() { 140 } 141 142 AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() { 143 } 144 145 void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml( 146 const std::string& html) { 147 AccessibilityNotificationWaiter waiter( 148 shell(), AccessibilityModeComplete, 149 ui::AX_EVENT_LOAD_COMPLETE); 150 GURL html_data_url("data:text/html," + html); 151 NavigateToURL(shell(), html_data_url); 152 waiter.WaitForNotification(); 153 } 154 155 // Retrieve the MSAA client accessibility object for the Render Widget Host View 156 // of the selected tab. 157 IAccessible* AccessibilityWinBrowserTest::GetRendererAccessible() { 158 content::WebContents* web_contents = shell()->web_contents(); 159 return web_contents->GetRenderWidgetHostView()->GetNativeViewAccessible(); 160 } 161 162 void AccessibilityWinBrowserTest::ExecuteScript(const std::wstring& script) { 163 shell()->web_contents()->GetMainFrame()->ExecuteJavaScript(script); 164 } 165 166 167 // AccessibleChecker ---------------------------------------------------------- 168 169 class AccessibleChecker { 170 public: 171 // This constructor can be used if the IA2 role will be the same as the MSAA 172 // role. 173 AccessibleChecker(const std::wstring& expected_name, 174 int32 expected_role, 175 const std::wstring& expected_value); 176 AccessibleChecker(const std::wstring& expected_name, 177 int32 expected_role, 178 int32 expected_ia2_role, 179 const std::wstring& expected_value); 180 AccessibleChecker(const std::wstring& expected_name, 181 const std::wstring& expected_role, 182 int32 expected_ia2_role, 183 const std::wstring& expected_value); 184 185 // Append an AccessibleChecker that verifies accessibility information for 186 // a child IAccessible. Order is important. 187 void AppendExpectedChild(AccessibleChecker* expected_child); 188 189 // Check that the name and role of the given IAccessible instance and its 190 // descendants match the expected names and roles that this object was 191 // initialized with. 192 void CheckAccessible(IAccessible* accessible); 193 194 // Set the expected value for this AccessibleChecker. 195 void SetExpectedValue(const std::wstring& expected_value); 196 197 // Set the expected state for this AccessibleChecker. 198 void SetExpectedState(LONG expected_state); 199 200 private: 201 typedef std::vector<AccessibleChecker*> AccessibleCheckerVector; 202 203 void CheckAccessibleName(IAccessible* accessible); 204 void CheckAccessibleRole(IAccessible* accessible); 205 void CheckIA2Role(IAccessible* accessible); 206 void CheckAccessibleValue(IAccessible* accessible); 207 void CheckAccessibleState(IAccessible* accessible); 208 void CheckAccessibleChildren(IAccessible* accessible); 209 base::string16 RoleVariantToString(const base::win::ScopedVariant& role); 210 211 // Expected accessible name. Checked against IAccessible::get_accName. 212 std::wstring name_; 213 214 // Expected accessible role. Checked against IAccessible::get_accRole. 215 base::win::ScopedVariant role_; 216 217 // Expected IAccessible2 role. Checked against IAccessible2::role. 218 int32 ia2_role_; 219 220 // Expected accessible value. Checked against IAccessible::get_accValue. 221 std::wstring value_; 222 223 // Expected accessible state. Checked against IAccessible::get_accState. 224 LONG state_; 225 226 // Expected accessible children. Checked using IAccessible::get_accChildCount 227 // and ::AccessibleChildren. 228 AccessibleCheckerVector children_; 229 }; 230 231 AccessibleChecker::AccessibleChecker(const std::wstring& expected_name, 232 int32 expected_role, 233 const std::wstring& expected_value) 234 : name_(expected_name), 235 role_(expected_role), 236 ia2_role_(expected_role), 237 value_(expected_value), 238 state_(-1) { 239 } 240 241 AccessibleChecker::AccessibleChecker(const std::wstring& expected_name, 242 int32 expected_role, 243 int32 expected_ia2_role, 244 const std::wstring& expected_value) 245 : name_(expected_name), 246 role_(expected_role), 247 ia2_role_(expected_ia2_role), 248 value_(expected_value), 249 state_(-1) { 250 } 251 252 AccessibleChecker::AccessibleChecker(const std::wstring& expected_name, 253 const std::wstring& expected_role, 254 int32 expected_ia2_role, 255 const std::wstring& expected_value) 256 : name_(expected_name), 257 role_(expected_role.c_str()), 258 ia2_role_(expected_ia2_role), 259 value_(expected_value), 260 state_(-1) { 261 } 262 263 void AccessibleChecker::AppendExpectedChild( 264 AccessibleChecker* expected_child) { 265 children_.push_back(expected_child); 266 } 267 268 void AccessibleChecker::CheckAccessible(IAccessible* accessible) { 269 SCOPED_TRACE("while checking " + 270 base::UTF16ToUTF8(RoleVariantToString(role_))); 271 CheckAccessibleName(accessible); 272 CheckAccessibleRole(accessible); 273 CheckIA2Role(accessible); 274 CheckAccessibleValue(accessible); 275 CheckAccessibleState(accessible); 276 CheckAccessibleChildren(accessible); 277 } 278 279 void AccessibleChecker::SetExpectedValue(const std::wstring& expected_value) { 280 value_ = expected_value; 281 } 282 283 void AccessibleChecker::SetExpectedState(LONG expected_state) { 284 state_ = expected_state; 285 } 286 287 void AccessibleChecker::CheckAccessibleName(IAccessible* accessible) { 288 base::win::ScopedBstr name; 289 base::win::ScopedVariant childid_self(CHILDID_SELF); 290 HRESULT hr = accessible->get_accName(childid_self, name.Receive()); 291 292 if (name_.empty()) { 293 // If the object doesn't have name S_FALSE should be returned. 294 EXPECT_EQ(S_FALSE, hr); 295 } else { 296 // Test that the correct string was returned. 297 EXPECT_EQ(S_OK, hr); 298 EXPECT_EQ(name_, std::wstring(name, name.Length())); 299 } 300 } 301 302 void AccessibleChecker::CheckAccessibleRole(IAccessible* accessible) { 303 base::win::ScopedVariant role; 304 base::win::ScopedVariant childid_self(CHILDID_SELF); 305 HRESULT hr = accessible->get_accRole(childid_self, role.Receive()); 306 ASSERT_EQ(S_OK, hr); 307 EXPECT_EQ(0, role_.Compare(role)) 308 << "Expected role: " << RoleVariantToString(role_) 309 << "\nGot role: " << RoleVariantToString(role); 310 } 311 312 void AccessibleChecker::CheckIA2Role(IAccessible* accessible) { 313 base::win::ScopedComPtr<IAccessible2> accessible2; 314 HRESULT hr = QueryIAccessible2(accessible, accessible2.Receive()); 315 ASSERT_EQ(S_OK, hr); 316 long ia2_role = 0; 317 hr = accessible2->role(&ia2_role); 318 ASSERT_EQ(S_OK, hr); 319 EXPECT_EQ(ia2_role_, ia2_role) 320 << "Expected ia2 role: " << IAccessible2RoleToString(ia2_role_) 321 << "\nGot ia2 role: " << IAccessible2RoleToString(ia2_role); 322 } 323 324 void AccessibleChecker::CheckAccessibleValue(IAccessible* accessible) { 325 // Don't check the value if if's a DOCUMENT role, because the value 326 // is supposed to be the url (and we don't keep track of that in the 327 // test expectations). 328 base::win::ScopedVariant role; 329 base::win::ScopedVariant childid_self(CHILDID_SELF); 330 HRESULT hr = accessible->get_accRole(childid_self, role.Receive()); 331 ASSERT_EQ(S_OK, hr); 332 if (role.type() == VT_I4 && V_I4(&role) == ROLE_SYSTEM_DOCUMENT) 333 return; 334 335 // Get the value. 336 base::win::ScopedBstr value; 337 hr = accessible->get_accValue(childid_self, value.Receive()); 338 EXPECT_EQ(S_OK, hr); 339 340 // Test that the correct string was returned. 341 EXPECT_EQ(value_, std::wstring(value, value.Length())); 342 } 343 344 void AccessibleChecker::CheckAccessibleState(IAccessible* accessible) { 345 if (state_ < 0) 346 return; 347 348 base::win::ScopedVariant state; 349 base::win::ScopedVariant childid_self(CHILDID_SELF); 350 HRESULT hr = accessible->get_accState(childid_self, state.Receive()); 351 EXPECT_EQ(S_OK, hr); 352 ASSERT_EQ(VT_I4, state.type()); 353 LONG obj_state = V_I4(&state); 354 // Avoid flakiness. The "offscreen" state depends on whether the browser 355 // window is frontmost or not, and "hottracked" depends on whether the 356 // mouse cursor happens to be over the element. 357 obj_state &= ~(STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_HOTTRACKED); 358 EXPECT_EQ(state_, obj_state) 359 << "Expected state: " << IAccessibleStateToString(state_) 360 << "\nGot state: " << IAccessibleStateToString(obj_state); 361 } 362 363 void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) { 364 LONG child_count = 0; 365 HRESULT hr = parent->get_accChildCount(&child_count); 366 EXPECT_EQ(S_OK, hr); 367 ASSERT_EQ(child_count, children_.size()); 368 369 scoped_ptr<VARIANT[]> child_array(new VARIANT[child_count]); 370 LONG obtained_count = 0; 371 hr = AccessibleChildren(parent, 0, child_count, 372 child_array.get(), &obtained_count); 373 ASSERT_EQ(S_OK, hr); 374 ASSERT_EQ(child_count, obtained_count); 375 376 VARIANT* child = child_array.get(); 377 for (AccessibleCheckerVector::iterator child_checker = children_.begin(); 378 child_checker != children_.end(); 379 ++child_checker, ++child) { 380 base::win::ScopedComPtr<IAccessible> child_accessible( 381 GetAccessibleFromResultVariant(parent, child)); 382 ASSERT_TRUE(child_accessible.get()); 383 (*child_checker)->CheckAccessible(child_accessible); 384 } 385 } 386 387 base::string16 AccessibleChecker::RoleVariantToString( 388 const base::win::ScopedVariant& role) { 389 if (role.type() == VT_I4) 390 return IAccessibleRoleToString(V_I4(&role)); 391 if (role.type() == VT_BSTR) 392 return base::string16(V_BSTR(&role), SysStringLen(V_BSTR(&role))); 393 return base::string16(); 394 } 395 396 } // namespace 397 398 399 // Tests ---------------------------------------------------------------------- 400 401 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 402 TestBusyAccessibilityTree) { 403 if (GetBaseAccessibilityMode() != AccessibilityModeOff) 404 return; 405 406 NavigateToURL(shell(), GURL(url::kAboutBlankURL)); 407 408 // The initial accessible returned should have state STATE_SYSTEM_BUSY while 409 // the accessibility tree is being requested from the renderer. 410 AccessibleChecker document1_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, 411 std::wstring()); 412 document1_checker.SetExpectedState( 413 STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED | 414 STATE_SYSTEM_BUSY); 415 document1_checker.CheckAccessible(GetRendererAccessible()); 416 } 417 418 // Periodically failing. See crbug.com/145537 419 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 420 DISABLED_TestNotificationActiveDescendantChanged) { 421 LoadInitialAccessibilityTreeFromHtml( 422 "<ul tabindex='-1' role='radiogroup' aria-label='ul'>" 423 "<li id='li'>li</li></ul>"); 424 425 // Check the browser's copy of the renderer accessibility tree. 426 AccessibleChecker list_marker_checker(L"\x2022", ROLE_SYSTEM_TEXT, 427 std::wstring()); 428 AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT, 429 std::wstring()); 430 AccessibleChecker list_item_checker(std::wstring(), ROLE_SYSTEM_LISTITEM, 431 std::wstring()); 432 list_item_checker.SetExpectedState(STATE_SYSTEM_READONLY); 433 AccessibleChecker radio_group_checker(L"ul", ROLE_SYSTEM_GROUPING, 434 IA2_ROLE_SECTION, std::wstring()); 435 radio_group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); 436 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, 437 std::wstring()); 438 list_item_checker.AppendExpectedChild(&list_marker_checker); 439 list_item_checker.AppendExpectedChild(&static_text_checker); 440 radio_group_checker.AppendExpectedChild(&list_item_checker); 441 document_checker.AppendExpectedChild(&radio_group_checker); 442 document_checker.CheckAccessible(GetRendererAccessible()); 443 444 // Set focus to the radio group. 445 scoped_ptr<AccessibilityNotificationWaiter> waiter( 446 new AccessibilityNotificationWaiter( 447 shell(), AccessibilityModeComplete, 448 ui::AX_EVENT_FOCUS)); 449 ExecuteScript(L"document.body.children[0].focus()"); 450 waiter->WaitForNotification(); 451 452 // Check that the accessibility tree of the browser has been updated. 453 radio_group_checker.SetExpectedState( 454 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED); 455 document_checker.CheckAccessible(GetRendererAccessible()); 456 457 // Set the active descendant of the radio group 458 waiter.reset(new AccessibilityNotificationWaiter( 459 shell(), AccessibilityModeComplete, 460 ui::AX_EVENT_FOCUS)); 461 ExecuteScript( 462 L"document.body.children[0].setAttribute('aria-activedescendant', 'li')"); 463 waiter->WaitForNotification(); 464 465 // Check that the accessibility tree of the browser has been updated. 466 list_item_checker.SetExpectedState( 467 STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED); 468 radio_group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); 469 document_checker.CheckAccessible(GetRendererAccessible()); 470 } 471 472 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 473 TestNotificationCheckedStateChanged) { 474 LoadInitialAccessibilityTreeFromHtml( 475 "<body><input type='checkbox' /></body>"); 476 477 // Check the browser's copy of the renderer accessibility tree. 478 AccessibleChecker checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON, 479 std::wstring()); 480 checkbox_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); 481 AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION, 482 std::wstring()); 483 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, 484 std::wstring()); 485 body_checker.AppendExpectedChild(&checkbox_checker); 486 document_checker.AppendExpectedChild(&body_checker); 487 document_checker.CheckAccessible(GetRendererAccessible()); 488 489 // Check the checkbox. 490 scoped_ptr<AccessibilityNotificationWaiter> waiter( 491 new AccessibilityNotificationWaiter( 492 shell(), AccessibilityModeComplete, 493 ui::AX_EVENT_CHECKED_STATE_CHANGED)); 494 ExecuteScript(L"document.body.children[0].checked=true"); 495 waiter->WaitForNotification(); 496 497 // Check that the accessibility tree of the browser has been updated. 498 checkbox_checker.SetExpectedState( 499 STATE_SYSTEM_CHECKED | STATE_SYSTEM_FOCUSABLE); 500 document_checker.CheckAccessible(GetRendererAccessible()); 501 } 502 503 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 504 TestNotificationChildrenChanged) { 505 // The role attribute causes the node to be in the accessibility tree. 506 LoadInitialAccessibilityTreeFromHtml("<body role=group></body>"); 507 508 // Check the browser's copy of the renderer accessibility tree. 509 AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING, 510 std::wstring()); 511 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, 512 std::wstring()); 513 document_checker.AppendExpectedChild(&group_checker); 514 document_checker.CheckAccessible(GetRendererAccessible()); 515 516 // Change the children of the document body. 517 scoped_ptr<AccessibilityNotificationWaiter> waiter( 518 new AccessibilityNotificationWaiter( 519 shell(), 520 AccessibilityModeComplete, 521 ui::AX_EVENT_CHILDREN_CHANGED)); 522 ExecuteScript(L"document.body.innerHTML='<b>new text</b>'"); 523 waiter->WaitForNotification(); 524 525 // Check that the accessibility tree of the browser has been updated. 526 AccessibleChecker text_checker( 527 L"new text", ROLE_SYSTEM_STATICTEXT, std::wstring()); 528 group_checker.AppendExpectedChild(&text_checker); 529 document_checker.CheckAccessible(GetRendererAccessible()); 530 } 531 532 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 533 TestNotificationChildrenChanged2) { 534 // The role attribute causes the node to be in the accessibility tree. 535 LoadInitialAccessibilityTreeFromHtml( 536 "<div role=group style='visibility: hidden'>text</div>"); 537 538 // Check the accessible tree of the browser. 539 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, 540 std::wstring()); 541 document_checker.CheckAccessible(GetRendererAccessible()); 542 543 // Change the children of the document body. 544 scoped_ptr<AccessibilityNotificationWaiter> waiter( 545 new AccessibilityNotificationWaiter( 546 shell(), AccessibilityModeComplete, 547 ui::AX_EVENT_CHILDREN_CHANGED)); 548 ExecuteScript(L"document.body.children[0].style.visibility='visible'"); 549 waiter->WaitForNotification(); 550 551 // Check that the accessibility tree of the browser has been updated. 552 AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_STATICTEXT, 553 std::wstring()); 554 AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING, 555 std::wstring()); 556 document_checker.AppendExpectedChild(&group_checker); 557 group_checker.AppendExpectedChild(&static_text_checker); 558 document_checker.CheckAccessible(GetRendererAccessible()); 559 } 560 561 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 562 TestNotificationFocusChanged) { 563 // The role attribute causes the node to be in the accessibility tree. 564 LoadInitialAccessibilityTreeFromHtml("<div role=group tabindex='-1'></div>"); 565 566 // Check the browser's copy of the renderer accessibility tree. 567 SCOPED_TRACE("Check initial tree"); 568 AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING, 569 std::wstring()); 570 group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); 571 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, 572 std::wstring()); 573 document_checker.AppendExpectedChild(&group_checker); 574 document_checker.CheckAccessible(GetRendererAccessible()); 575 576 // Focus the div in the document 577 scoped_ptr<AccessibilityNotificationWaiter> waiter( 578 new AccessibilityNotificationWaiter( 579 shell(), AccessibilityModeComplete, 580 ui::AX_EVENT_FOCUS)); 581 ExecuteScript(L"document.body.children[0].focus()"); 582 waiter->WaitForNotification(); 583 584 // Check that the accessibility tree of the browser has been updated. 585 SCOPED_TRACE("Check updated tree after focusing div"); 586 group_checker.SetExpectedState( 587 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED); 588 document_checker.CheckAccessible(GetRendererAccessible()); 589 590 // Focus the document accessible. This will un-focus the current node. 591 waiter.reset( 592 new AccessibilityNotificationWaiter( 593 shell(), AccessibilityModeComplete, 594 ui::AX_EVENT_BLUR)); 595 base::win::ScopedComPtr<IAccessible> document_accessible( 596 GetRendererAccessible()); 597 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL)); 598 base::win::ScopedVariant childid_self(CHILDID_SELF); 599 HRESULT hr = document_accessible->accSelect(SELFLAG_TAKEFOCUS, childid_self); 600 ASSERT_EQ(S_OK, hr); 601 waiter->WaitForNotification(); 602 603 // Check that the accessibility tree of the browser has been updated. 604 SCOPED_TRACE("Check updated tree after focusing document again"); 605 group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); 606 document_checker.CheckAccessible(GetRendererAccessible()); 607 } 608 609 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 610 TestNotificationValueChanged) { 611 LoadInitialAccessibilityTreeFromHtml( 612 "<body><input type='text' value='old value'/></body>"); 613 614 // Check the browser's copy of the renderer accessibility tree. 615 AccessibleChecker text_field_checker(std::wstring(), ROLE_SYSTEM_TEXT, 616 L"old value"); 617 text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); 618 AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION, 619 std::wstring()); 620 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, 621 std::wstring()); 622 body_checker.AppendExpectedChild(&text_field_checker); 623 document_checker.AppendExpectedChild(&body_checker); 624 document_checker.CheckAccessible(GetRendererAccessible()); 625 626 // Set the value of the text control 627 scoped_ptr<AccessibilityNotificationWaiter> waiter( 628 new AccessibilityNotificationWaiter( 629 shell(), AccessibilityModeComplete, 630 ui::AX_EVENT_VALUE_CHANGED)); 631 ExecuteScript(L"document.body.children[0].value='new value'"); 632 waiter->WaitForNotification(); 633 634 // Check that the accessibility tree of the browser has been updated. 635 text_field_checker.SetExpectedValue(L"new value"); 636 document_checker.CheckAccessible(GetRendererAccessible()); 637 } 638 639 // This test verifies that the web content's accessibility tree is a 640 // descendant of the main browser window's accessibility tree, so that 641 // tools like AccExplorer32 or AccProbe can be used to examine Chrome's 642 // accessibility support. 643 // 644 // If you made a change and this test now fails, check that the NativeViewHost 645 // that wraps the tab contents returns the IAccessible implementation 646 // provided by RenderWidgetHostViewWin in GetNativeViewAccessible(). 647 // flaky: http://crbug.com/402190 648 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 649 DISABLED_ContainsRendererAccessibilityTree) { 650 LoadInitialAccessibilityTreeFromHtml( 651 "<html><head><title>MyDocument</title></head>" 652 "<body>Content</body></html>"); 653 654 // Get the accessibility object for the window tree host. 655 aura::Window* window = shell()->window(); 656 CHECK(window); 657 aura::WindowTreeHost* window_tree_host = window->GetHost(); 658 CHECK(window_tree_host); 659 HWND hwnd = window_tree_host->GetAcceleratedWidget(); 660 CHECK(hwnd); 661 base::win::ScopedComPtr<IAccessible> browser_accessible; 662 HRESULT hr = AccessibleObjectFromWindow( 663 hwnd, 664 OBJID_WINDOW, 665 IID_IAccessible, 666 reinterpret_cast<void**>(browser_accessible.Receive())); 667 ASSERT_EQ(S_OK, hr); 668 669 bool found = false; 670 RecursiveFindNodeInAccessibilityTree( 671 browser_accessible.get(), ROLE_SYSTEM_DOCUMENT, L"MyDocument", 0, &found); 672 ASSERT_EQ(found, true); 673 } 674 675 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 676 SupportsISimpleDOM) { 677 LoadInitialAccessibilityTreeFromHtml( 678 "<body><input type='checkbox' /></body>"); 679 680 // Get the IAccessible object for the document. 681 base::win::ScopedComPtr<IAccessible> document_accessible( 682 GetRendererAccessible()); 683 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL)); 684 685 // Get the ISimpleDOM object for the document. 686 base::win::ScopedComPtr<IServiceProvider> service_provider; 687 HRESULT hr = static_cast<IAccessible*>(document_accessible)->QueryInterface( 688 service_provider.Receive()); 689 ASSERT_EQ(S_OK, hr); 690 const GUID refguid = {0x0c539790, 0x12e4, 0x11cf, 691 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8}; 692 base::win::ScopedComPtr<ISimpleDOMNode> document_isimpledomnode; 693 hr = static_cast<IServiceProvider *>(service_provider)->QueryService( 694 refguid, IID_ISimpleDOMNode, 695 reinterpret_cast<void**>(document_isimpledomnode.Receive())); 696 ASSERT_EQ(S_OK, hr); 697 698 base::win::ScopedBstr node_name; 699 short name_space_id; // NOLINT 700 base::win::ScopedBstr node_value; 701 unsigned int num_children; 702 unsigned int unique_id; 703 unsigned short node_type; // NOLINT 704 hr = document_isimpledomnode->get_nodeInfo( 705 node_name.Receive(), &name_space_id, node_value.Receive(), &num_children, 706 &unique_id, &node_type); 707 ASSERT_EQ(S_OK, hr); 708 EXPECT_EQ(NODETYPE_DOCUMENT, node_type); 709 EXPECT_EQ(1, num_children); 710 node_name.Reset(); 711 node_value.Reset(); 712 713 base::win::ScopedComPtr<ISimpleDOMNode> body_isimpledomnode; 714 hr = document_isimpledomnode->get_firstChild( 715 body_isimpledomnode.Receive()); 716 ASSERT_EQ(S_OK, hr); 717 hr = body_isimpledomnode->get_nodeInfo( 718 node_name.Receive(), &name_space_id, node_value.Receive(), &num_children, 719 &unique_id, &node_type); 720 ASSERT_EQ(S_OK, hr); 721 EXPECT_EQ(L"body", std::wstring(node_name, node_name.Length())); 722 EXPECT_EQ(NODETYPE_ELEMENT, node_type); 723 EXPECT_EQ(1, num_children); 724 node_name.Reset(); 725 node_value.Reset(); 726 727 base::win::ScopedComPtr<ISimpleDOMNode> checkbox_isimpledomnode; 728 hr = body_isimpledomnode->get_firstChild( 729 checkbox_isimpledomnode.Receive()); 730 ASSERT_EQ(S_OK, hr); 731 hr = checkbox_isimpledomnode->get_nodeInfo( 732 node_name.Receive(), &name_space_id, node_value.Receive(), &num_children, 733 &unique_id, &node_type); 734 ASSERT_EQ(S_OK, hr); 735 EXPECT_EQ(L"input", std::wstring(node_name, node_name.Length())); 736 EXPECT_EQ(NODETYPE_ELEMENT, node_type); 737 EXPECT_EQ(0, num_children); 738 } 739 740 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestRoleGroup) { 741 LoadInitialAccessibilityTreeFromHtml( 742 "<fieldset></fieldset><div role=group></div>"); 743 744 // Check the browser's copy of the renderer accessibility tree. 745 AccessibleChecker grouping1_checker(std::wstring(), ROLE_SYSTEM_GROUPING, 746 std::wstring()); 747 AccessibleChecker grouping2_checker(std::wstring(), ROLE_SYSTEM_GROUPING, 748 std::wstring()); 749 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, 750 std::wstring()); 751 document_checker.AppendExpectedChild(&grouping1_checker); 752 document_checker.AppendExpectedChild(&grouping2_checker); 753 document_checker.CheckAccessible(GetRendererAccessible()); 754 } 755 756 } // namespace content 757