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