1 // Copyright (c) 2011 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 <atlbase.h> 6 #include <vector> 7 8 #include "base/win/scoped_comptr.h" 9 #include "chrome/browser/automation/ui_controls.h" 10 #include "chrome/browser/renderer_host/render_widget_host_view_win.h" 11 #include "chrome/browser/ui/browser.h" 12 #include "chrome/browser/ui/browser_window.h" 13 #include "chrome/test/in_process_browser_test.h" 14 #include "chrome/test/ui_test_utils.h" 15 #include "content/browser/renderer_host/render_view_host.h" 16 #include "content/browser/tab_contents/tab_contents.h" 17 #include "content/common/notification_type.h" 18 #include "ia2_api_all.h" // Generated NOLINT 19 #include "ISimpleDOMNode.h" // Generated NOLINT 20 21 using std::auto_ptr; 22 using std::vector; 23 using std::wstring; 24 25 namespace { 26 27 class AccessibilityWinBrowserTest : public InProcessBrowserTest { 28 public: 29 AccessibilityWinBrowserTest() {} 30 31 // InProcessBrowserTest 32 void SetUpInProcessBrowserTestFixture(); 33 34 protected: 35 IAccessible* GetRendererAccessible(); 36 void ExecuteScript(wstring script); 37 }; 38 39 void AccessibilityWinBrowserTest::SetUpInProcessBrowserTestFixture() { 40 // If the mouse happens to be on the document then it will have the unexpected 41 // STATE_SYSTEM_HOTTRACKED state. Move it to a non-document location. 42 ui_controls::SendMouseMove(0, 0); 43 } 44 45 class AccessibleChecker { 46 public: 47 AccessibleChecker( 48 wstring expected_name, 49 int32 expected_role, 50 wstring expected_value); 51 AccessibleChecker( 52 wstring expected_name, 53 wstring expected_role, 54 wstring expected_value); 55 56 // Append an AccessibleChecker that verifies accessibility information for 57 // a child IAccessible. Order is important. 58 void AppendExpectedChild(AccessibleChecker* expected_child); 59 60 // Check that the name and role of the given IAccessible instance and its 61 // descendants match the expected names and roles that this object was 62 // initialized with. 63 void CheckAccessible(IAccessible* accessible); 64 65 // Set the expected value for this AccessibleChecker. 66 void SetExpectedValue(wstring expected_value); 67 68 // Set the expected state for this AccessibleChecker. 69 void SetExpectedState(LONG expected_state); 70 71 private: 72 void CheckAccessibleName(IAccessible* accessible); 73 void CheckAccessibleRole(IAccessible* accessible); 74 void CheckAccessibleValue(IAccessible* accessible); 75 void CheckAccessibleState(IAccessible* accessible); 76 void CheckAccessibleChildren(IAccessible* accessible); 77 78 private: 79 typedef vector<AccessibleChecker*> AccessibleCheckerVector; 80 81 // Expected accessible name. Checked against IAccessible::get_accName. 82 wstring name_; 83 84 // Expected accessible role. Checked against IAccessible::get_accRole. 85 CComVariant role_; 86 87 // Expected accessible value. Checked against IAccessible::get_accValue. 88 wstring value_; 89 90 // Expected accessible state. Checked against IAccessible::get_accState. 91 LONG state_; 92 93 // Expected accessible children. Checked using IAccessible::get_accChildCount 94 // and ::AccessibleChildren. 95 AccessibleCheckerVector children_; 96 }; 97 98 VARIANT CreateI4Variant(LONG value) { 99 VARIANT variant = {0}; 100 101 V_VT(&variant) = VT_I4; 102 V_I4(&variant) = value; 103 104 return variant; 105 } 106 107 IAccessible* GetAccessibleFromResultVariant(IAccessible* parent, VARIANT *var) { 108 switch (V_VT(var)) { 109 case VT_DISPATCH: 110 return CComQIPtr<IAccessible>(V_DISPATCH(var)).Detach(); 111 break; 112 113 case VT_I4: { 114 CComPtr<IDispatch> dispatch; 115 HRESULT hr = parent->get_accChild(CreateI4Variant(V_I4(var)), &dispatch); 116 EXPECT_TRUE(SUCCEEDED(hr)); 117 return CComQIPtr<IAccessible>(dispatch).Detach(); 118 break; 119 } 120 } 121 122 return NULL; 123 } 124 125 HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) { 126 // TODO(ctguil): For some reason querying the IAccessible2 interface from 127 // IAccessible fails. 128 base::win::ScopedComPtr<IServiceProvider> service_provider; 129 HRESULT hr = accessible->QueryInterface(service_provider.Receive()); 130 if (FAILED(hr)) 131 return hr; 132 133 hr = service_provider->QueryService(IID_IAccessible2, accessible2); 134 return hr; 135 } 136 137 // Sets result to true if the child is located in the parent's tree. An 138 // exhustive search is perform here because we determine equality using 139 // IAccessible2::get_unique_id which is only supported by the child node. 140 void AccessibleContainsAccessible( 141 IAccessible* parent, IAccessible2* child, bool* result) { 142 vector<base::win::ScopedComPtr<IAccessible>> accessible_list; 143 accessible_list.push_back(base::win::ScopedComPtr<IAccessible>(parent)); 144 145 LONG unique_id; 146 HRESULT hr = child->get_uniqueID(&unique_id); 147 ASSERT_EQ(S_OK, hr); 148 *result = false; 149 150 while (accessible_list.size()) { 151 base::win::ScopedComPtr<IAccessible> accessible = accessible_list.back(); 152 accessible_list.pop_back(); 153 154 base::win::ScopedComPtr<IAccessible2> accessible2; 155 hr = QueryIAccessible2(accessible, accessible2.Receive()); 156 if (SUCCEEDED(hr)) { 157 LONG child_id; 158 accessible2->get_uniqueID(&child_id); 159 if (child_id == unique_id) { 160 *result = true; 161 break; 162 } 163 } 164 165 LONG child_count; 166 hr = accessible->get_accChildCount(&child_count); 167 ASSERT_EQ(S_OK, hr); 168 if (child_count == 0) 169 continue; 170 171 auto_ptr<VARIANT> child_array(new VARIANT[child_count]); 172 LONG obtained_count = 0; 173 hr = AccessibleChildren( 174 accessible, 0, child_count, child_array.get(), &obtained_count); 175 ASSERT_EQ(S_OK, hr); 176 ASSERT_EQ(child_count, obtained_count); 177 178 for (int index = 0; index < obtained_count; index++) { 179 base::win::ScopedComPtr<IAccessible> child_accessible( 180 GetAccessibleFromResultVariant(accessible, &child_array.get()[index])); 181 if (child_accessible.get()) { 182 accessible_list.push_back( 183 base::win::ScopedComPtr<IAccessible>(child_accessible)); 184 } 185 } 186 } 187 } 188 189 // Retrieve the MSAA client accessibility object for the Render Widget Host View 190 // of the selected tab. 191 IAccessible* 192 AccessibilityWinBrowserTest::GetRendererAccessible() { 193 HWND hwnd_render_widget_host_view = 194 browser()->GetSelectedTabContents()->GetRenderWidgetHostView()-> 195 GetNativeView(); 196 197 // Invoke windows screen reader detection by sending the WM_GETOBJECT message 198 // with kIdCustom as the LPARAM. 199 const int32 kIdCustom = 1; 200 SendMessage( 201 hwnd_render_widget_host_view, WM_GETOBJECT, OBJID_CLIENT, kIdCustom); 202 203 IAccessible* accessible; 204 HRESULT hr = AccessibleObjectFromWindow( 205 hwnd_render_widget_host_view, OBJID_CLIENT, 206 IID_IAccessible, reinterpret_cast<void**>(&accessible)); 207 EXPECT_EQ(S_OK, hr); 208 EXPECT_NE(accessible, reinterpret_cast<IAccessible*>(NULL)); 209 210 return accessible; 211 } 212 213 void AccessibilityWinBrowserTest::ExecuteScript(wstring script) { 214 browser()->GetSelectedTabContents()->render_view_host()-> 215 ExecuteJavascriptInWebFrame(L"", script); 216 } 217 218 AccessibleChecker::AccessibleChecker( 219 wstring expected_name, int32 expected_role, wstring expected_value) : 220 name_(expected_name), 221 role_(expected_role), 222 value_(expected_value), 223 state_(-1) { 224 } 225 226 AccessibleChecker::AccessibleChecker( 227 wstring expected_name, wstring expected_role, wstring expected_value) : 228 name_(expected_name), 229 role_(expected_role.c_str()), 230 value_(expected_value), 231 state_(-1) { 232 } 233 234 void AccessibleChecker::AppendExpectedChild( 235 AccessibleChecker* expected_child) { 236 children_.push_back(expected_child); 237 } 238 239 void AccessibleChecker::CheckAccessible(IAccessible* accessible) { 240 CheckAccessibleName(accessible); 241 CheckAccessibleRole(accessible); 242 CheckAccessibleValue(accessible); 243 CheckAccessibleState(accessible); 244 CheckAccessibleChildren(accessible); 245 } 246 247 void AccessibleChecker::SetExpectedValue(wstring expected_value) { 248 value_ = expected_value; 249 } 250 251 void AccessibleChecker::SetExpectedState(LONG expected_state) { 252 state_ = expected_state; 253 } 254 255 void AccessibleChecker::CheckAccessibleName(IAccessible* accessible) { 256 CComBSTR name; 257 HRESULT hr = 258 accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name); 259 260 if (name_.empty()) { 261 // If the object doesn't have name S_FALSE should be returned. 262 EXPECT_EQ(hr, S_FALSE); 263 } else { 264 // Test that the correct string was returned. 265 EXPECT_EQ(S_OK, hr); 266 EXPECT_STREQ(name_.c_str(), 267 wstring(name.m_str, SysStringLen(name)).c_str()); 268 } 269 } 270 271 void AccessibleChecker::CheckAccessibleRole(IAccessible* accessible) { 272 VARIANT var_role = {0}; 273 HRESULT hr = 274 accessible->get_accRole(CreateI4Variant(CHILDID_SELF), &var_role); 275 ASSERT_EQ(S_OK, hr); 276 EXPECT_TRUE(role_ == var_role); 277 } 278 279 void AccessibleChecker::CheckAccessibleValue(IAccessible* accessible) { 280 CComBSTR value; 281 HRESULT hr = 282 accessible->get_accValue(CreateI4Variant(CHILDID_SELF), &value); 283 EXPECT_EQ(S_OK, hr); 284 285 // Test that the correct string was returned. 286 EXPECT_STREQ(value_.c_str(), 287 wstring(value.m_str, SysStringLen(value)).c_str()); 288 } 289 290 void AccessibleChecker::CheckAccessibleState(IAccessible* accessible) { 291 if (state_ < 0) 292 return; 293 294 VARIANT var_state = {0}; 295 HRESULT hr = 296 accessible->get_accState(CreateI4Variant(CHILDID_SELF), &var_state); 297 EXPECT_EQ(S_OK, hr); 298 ASSERT_EQ(VT_I4, V_VT(&var_state)); 299 EXPECT_EQ(state_, V_I4(&var_state)); 300 } 301 302 void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) { 303 LONG child_count = 0; 304 HRESULT hr = parent->get_accChildCount(&child_count); 305 EXPECT_EQ(S_OK, hr); 306 ASSERT_EQ(child_count, children_.size()); 307 308 auto_ptr<VARIANT> child_array(new VARIANT[child_count]); 309 LONG obtained_count = 0; 310 hr = AccessibleChildren(parent, 0, child_count, 311 child_array.get(), &obtained_count); 312 ASSERT_EQ(S_OK, hr); 313 ASSERT_EQ(child_count, obtained_count); 314 315 VARIANT* child = child_array.get(); 316 for (AccessibleCheckerVector::iterator child_checker = children_.begin(); 317 child_checker != children_.end(); 318 ++child_checker, ++child) { 319 base::win::ScopedComPtr<IAccessible> child_accessible; 320 child_accessible.Attach(GetAccessibleFromResultVariant(parent, child)); 321 ASSERT_TRUE(child_accessible.get()); 322 (*child_checker)->CheckAccessible(child_accessible); 323 } 324 } 325 326 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 327 TestRendererAccessibilityTree) { 328 // The initial accessible returned should have state STATE_SYSTEM_BUSY while 329 // the accessibility tree is being requested from the renderer. 330 AccessibleChecker document1_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); 331 document1_checker.SetExpectedState( 332 STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED | 333 STATE_SYSTEM_BUSY); 334 document1_checker.CheckAccessible(GetRendererAccessible()); 335 336 // Wait for the initial accessibility tree to load. Busy state should clear. 337 ui_test_utils::WaitForNotification( 338 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 339 document1_checker.SetExpectedState( 340 STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED); 341 document1_checker.CheckAccessible(GetRendererAccessible()); 342 343 GURL tree_url( 344 "data:text/html,<html><head><title>Accessibility Win Test</title></head>" 345 "<body><input type='button' value='push' /><input type='checkbox' />" 346 "</body></html>"); 347 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); 348 ui_test_utils::WaitForNotification( 349 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 350 351 // Check the browser's copy of the renderer accessibility tree. 352 AccessibleChecker button_checker(L"push", ROLE_SYSTEM_PUSHBUTTON, L"push"); 353 AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON, L""); 354 AccessibleChecker body_checker(L"", L"body", L""); 355 AccessibleChecker document2_checker( 356 L"Accessibility Win Test", ROLE_SYSTEM_DOCUMENT, L""); 357 body_checker.AppendExpectedChild(&button_checker); 358 body_checker.AppendExpectedChild(&checkbox_checker); 359 document2_checker.AppendExpectedChild(&body_checker); 360 document2_checker.CheckAccessible(GetRendererAccessible()); 361 362 // Check that document accessible has a parent accessible. 363 base::win::ScopedComPtr<IAccessible> document_accessible( 364 GetRendererAccessible()); 365 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL)); 366 base::win::ScopedComPtr<IDispatch> parent_dispatch; 367 HRESULT hr = document_accessible->get_accParent(parent_dispatch.Receive()); 368 EXPECT_EQ(S_OK, hr); 369 EXPECT_NE(parent_dispatch, reinterpret_cast<IDispatch*>(NULL)); 370 371 // Navigate to another page. 372 GURL about_url("about:"); 373 ui_test_utils::NavigateToURL(browser(), about_url); 374 375 // Verify that the IAccessible reference still points to a valid object and 376 // that calls to its methods fail since the tree is no longer valid after 377 // the page navagation. 378 CComBSTR name; 379 hr = document_accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name); 380 ASSERT_EQ(E_FAIL, hr); 381 } 382 383 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 384 TestNotificationActiveDescendantChanged) { 385 GURL tree_url("data:text/html,<ul tabindex='-1' role='radiogroup'><li id='li'" 386 ">li</li></ul>"); 387 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); 388 GetRendererAccessible(); 389 ui_test_utils::WaitForNotification( 390 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 391 392 // Check the browser's copy of the renderer accessibility tree. 393 AccessibleChecker list_marker_checker(L"", ROLE_SYSTEM_LISTITEM, L"\x2022"); 394 AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT, L""); 395 AccessibleChecker list_item_checker(L"", ROLE_SYSTEM_LISTITEM, L""); 396 list_item_checker.SetExpectedState( 397 STATE_SYSTEM_READONLY); 398 AccessibleChecker radio_group_checker(L"", ROLE_SYSTEM_GROUPING, L""); 399 radio_group_checker.SetExpectedState( 400 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY); 401 AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); 402 list_item_checker.AppendExpectedChild(&list_marker_checker); 403 list_item_checker.AppendExpectedChild(&static_text_checker); 404 radio_group_checker.AppendExpectedChild(&list_item_checker); 405 document_checker.AppendExpectedChild(&radio_group_checker); 406 document_checker.CheckAccessible(GetRendererAccessible()); 407 408 // Set focus to the radio group. 409 ExecuteScript(L"document.body.children[0].focus()"); 410 ui_test_utils::WaitForNotification( 411 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 412 413 // Check that the accessibility tree of the browser has been updated. 414 radio_group_checker.SetExpectedState( 415 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED); 416 document_checker.CheckAccessible(GetRendererAccessible()); 417 418 // Set the active descendant of the radio group 419 ExecuteScript( 420 L"document.body.children[0].setAttribute('aria-activedescendant', 'li')"); 421 ui_test_utils::WaitForNotification( 422 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 423 424 // Check that the accessibility tree of the browser has been updated. 425 list_item_checker.SetExpectedState( 426 STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED); 427 radio_group_checker.SetExpectedState( 428 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY); 429 document_checker.CheckAccessible(GetRendererAccessible()); 430 } 431 432 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 433 TestNotificationCheckedStateChanged) { 434 GURL tree_url("data:text/html,<body><input type='checkbox' /></body>"); 435 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); 436 GetRendererAccessible(); 437 ui_test_utils::WaitForNotification( 438 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 439 440 // Check the browser's copy of the renderer accessibility tree. 441 AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON, L""); 442 checkbox_checker.SetExpectedState( 443 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY); 444 AccessibleChecker body_checker(L"", L"body", L""); 445 AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); 446 body_checker.AppendExpectedChild(&checkbox_checker); 447 document_checker.AppendExpectedChild(&body_checker); 448 document_checker.CheckAccessible(GetRendererAccessible()); 449 450 // Check the checkbox. 451 ExecuteScript(L"document.body.children[0].checked=true"); 452 ui_test_utils::WaitForNotification( 453 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 454 455 // Check that the accessibility tree of the browser has been updated. 456 checkbox_checker.SetExpectedState( 457 STATE_SYSTEM_CHECKED | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY); 458 document_checker.CheckAccessible(GetRendererAccessible()); 459 } 460 461 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 462 TestNotificationChildrenChanged) { 463 // The role attribute causes the node to be in the accessibility tree. 464 GURL tree_url( 465 "data:text/html,<body role=group></body>"); 466 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); 467 GetRendererAccessible(); 468 ui_test_utils::WaitForNotification( 469 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 470 471 // Check the browser's copy of the renderer accessibility tree. 472 AccessibleChecker body_checker(L"", L"body", L""); 473 AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); 474 document_checker.AppendExpectedChild(&body_checker); 475 document_checker.CheckAccessible(GetRendererAccessible()); 476 477 // Change the children of the document body. 478 ExecuteScript(L"document.body.innerHTML='<b>new text</b>'"); 479 ui_test_utils::WaitForNotification( 480 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 481 482 // Check that the accessibility tree of the browser has been updated. 483 AccessibleChecker text_checker(L"new text", ROLE_SYSTEM_TEXT, L""); 484 body_checker.AppendExpectedChild(&text_checker); 485 document_checker.CheckAccessible(GetRendererAccessible()); 486 } 487 488 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 489 TestNotificationChildrenChanged2) { 490 // The role attribute causes the node to be in the accessibility tree. 491 GURL tree_url( 492 "data:text/html,<div role=group style='visibility: hidden'>text" 493 "</div>"); 494 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); 495 GetRendererAccessible(); 496 ui_test_utils::WaitForNotification( 497 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 498 499 // Check the accessible tree of the browser. 500 AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); 501 document_checker.CheckAccessible(GetRendererAccessible()); 502 503 // Change the children of the document body. 504 ExecuteScript(L"document.body.children[0].style.visibility='visible'"); 505 ui_test_utils::WaitForNotification( 506 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 507 508 // Check that the accessibility tree of the browser has been updated. 509 AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_TEXT, L""); 510 AccessibleChecker div_checker(L"", L"div", L""); 511 document_checker.AppendExpectedChild(&div_checker); 512 div_checker.AppendExpectedChild(&static_text_checker); 513 document_checker.CheckAccessible(GetRendererAccessible()); 514 } 515 516 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 517 TestNotificationFocusChanged) { 518 // The role attribute causes the node to be in the accessibility tree. 519 GURL tree_url( 520 "data:text/html,<div role=group tabindex='-1'></div>"); 521 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); 522 GetRendererAccessible(); 523 ui_test_utils::WaitForNotification( 524 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 525 526 // Check the browser's copy of the renderer accessibility tree. 527 AccessibleChecker div_checker(L"", L"div", L""); 528 div_checker.SetExpectedState( 529 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_READONLY); 530 AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); 531 document_checker.AppendExpectedChild(&div_checker); 532 document_checker.CheckAccessible(GetRendererAccessible()); 533 534 // Focus the div in the document 535 ExecuteScript(L"document.body.children[0].focus()"); 536 ui_test_utils::WaitForNotification( 537 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 538 539 // Check that the accessibility tree of the browser has been updated. 540 div_checker.SetExpectedState( 541 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED); 542 document_checker.CheckAccessible(GetRendererAccessible()); 543 544 // Focus the document accessible. This will un-focus the current node. 545 base::win::ScopedComPtr<IAccessible> document_accessible( 546 GetRendererAccessible()); 547 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL)); 548 HRESULT hr = document_accessible->accSelect( 549 SELFLAG_TAKEFOCUS, CreateI4Variant(CHILDID_SELF)); 550 ASSERT_EQ(S_OK, hr); 551 ui_test_utils::WaitForNotification( 552 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 553 554 // Check that the accessibility tree of the browser has been updated. 555 div_checker.SetExpectedState( 556 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY); 557 document_checker.CheckAccessible(GetRendererAccessible()); 558 } 559 560 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 561 TestNotificationValueChanged) { 562 GURL tree_url("data:text/html,<body><input type='text' value='old value'/>" 563 "</body>"); 564 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); 565 GetRendererAccessible(); 566 ui_test_utils::WaitForNotification( 567 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 568 569 // Check the browser's copy of the renderer accessibility tree. 570 571 AccessibleChecker text_field_div_checker(L"", L"div", L""); 572 AccessibleChecker text_field_checker(L"", ROLE_SYSTEM_TEXT, L"old value"); 573 text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); 574 AccessibleChecker body_checker(L"", L"body", L""); 575 AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); 576 text_field_checker.AppendExpectedChild(&text_field_div_checker); 577 body_checker.AppendExpectedChild(&text_field_checker); 578 document_checker.AppendExpectedChild(&body_checker); 579 document_checker.CheckAccessible(GetRendererAccessible()); 580 581 // Set the value of the text control 582 ExecuteScript(L"document.body.children[0].value='new value'"); 583 ui_test_utils::WaitForNotification( 584 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 585 586 // Check that the accessibility tree of the browser has been updated. 587 text_field_checker.SetExpectedValue(L"new value"); 588 document_checker.CheckAccessible(GetRendererAccessible()); 589 } 590 591 // FAILS crbug.com/54220 592 // This test verifies that browser-side cache of the renderer accessibility 593 // tree is reachable from the browser's tree. Tools that analyze windows 594 // accessibility trees like AccExplorer32 should be able to drill into the 595 // cached renderer accessibility tree. 596 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 597 DISABLED_ContainsRendererAccessibilityTree) { 598 GURL tree_url("data:text/html,<body><input type='checkbox' /></body>"); 599 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); 600 GetRendererAccessible(); 601 ui_test_utils::WaitForNotification( 602 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 603 604 // Get the accessibility object for the browser window. 605 HWND browser_hwnd = browser()->window()->GetNativeHandle(); 606 base::win::ScopedComPtr<IAccessible> browser_accessible; 607 HRESULT hr = AccessibleObjectFromWindow( 608 browser_hwnd, 609 OBJID_WINDOW, 610 IID_IAccessible, 611 reinterpret_cast<void**>(browser_accessible.Receive())); 612 ASSERT_EQ(S_OK, hr); 613 614 // Get the accessibility object for the renderer client document. 615 base::win::ScopedComPtr<IAccessible> document_accessible( 616 GetRendererAccessible()); 617 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL)); 618 base::win::ScopedComPtr<IAccessible2> document_accessible2; 619 hr = QueryIAccessible2(document_accessible, document_accessible2.Receive()); 620 ASSERT_EQ(S_OK, hr); 621 622 // TODO(ctguil): Pointer comparison of retrieved IAccessible pointers dosen't 623 // seem to work for here. Perhaps make IAccessible2 available in views to make 624 // unique id comparison available. 625 bool found = false; 626 base::win::ScopedComPtr<IAccessible> parent = document_accessible; 627 while (parent.get()) { 628 base::win::ScopedComPtr<IDispatch> parent_dispatch; 629 hr = parent->get_accParent(parent_dispatch.Receive()); 630 ASSERT_TRUE(SUCCEEDED(hr)); 631 if (!parent_dispatch.get()) { 632 ASSERT_EQ(hr, S_FALSE); 633 break; 634 } 635 636 parent.Release(); 637 hr = parent_dispatch.QueryInterface(parent.Receive()); 638 ASSERT_EQ(S_OK, hr); 639 640 if (parent.get() == browser_accessible.get()) { 641 found = true; 642 break; 643 } 644 } 645 646 // If pointer comparison fails resort to the exhuasive search that can use 647 // IAccessible2::get_unique_id for equality comparison. 648 if (!found) { 649 AccessibleContainsAccessible( 650 browser_accessible, document_accessible2, &found); 651 } 652 653 ASSERT_EQ(found, true); 654 } 655 656 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, 657 SupportsISimpleDOM) { 658 GURL tree_url("data:text/html,<body><input type='checkbox' /></body>"); 659 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); 660 GetRendererAccessible(); 661 ui_test_utils::WaitForNotification( 662 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); 663 664 // Get the IAccessible object for the document. 665 base::win::ScopedComPtr<IAccessible> document_accessible( 666 GetRendererAccessible()); 667 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL)); 668 669 // Get the ISimpleDOM object for the document. 670 base::win::ScopedComPtr<IServiceProvider> service_provider; 671 HRESULT hr = static_cast<IAccessible*>(document_accessible)->QueryInterface( 672 service_provider.Receive()); 673 ASSERT_EQ(S_OK, hr); 674 const GUID refguid = {0x0c539790, 0x12e4, 0x11cf, 675 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8}; 676 base::win::ScopedComPtr<ISimpleDOMNode> document_isimpledomnode; 677 hr = static_cast<IServiceProvider *>(service_provider)->QueryService( 678 refguid, IID_ISimpleDOMNode, 679 reinterpret_cast<void**>(document_isimpledomnode.Receive())); 680 ASSERT_EQ(S_OK, hr); 681 682 BSTR node_name; 683 short name_space_id; // NOLINT 684 BSTR node_value; 685 unsigned int num_children; 686 unsigned int unique_id; 687 unsigned short node_type; // NOLINT 688 hr = document_isimpledomnode->get_nodeInfo( 689 &node_name, &name_space_id, &node_value, &num_children, &unique_id, 690 &node_type); 691 ASSERT_EQ(S_OK, hr); 692 EXPECT_EQ(NODETYPE_DOCUMENT, node_type); 693 EXPECT_EQ(1, num_children); 694 695 base::win::ScopedComPtr<ISimpleDOMNode> body_isimpledomnode; 696 hr = document_isimpledomnode->get_firstChild( 697 body_isimpledomnode.Receive()); 698 ASSERT_EQ(S_OK, hr); 699 hr = body_isimpledomnode->get_nodeInfo( 700 &node_name, &name_space_id, &node_value, &num_children, &unique_id, 701 &node_type); 702 ASSERT_EQ(S_OK, hr); 703 EXPECT_STREQ(L"body", wstring(node_name, SysStringLen(node_name)).c_str()); 704 EXPECT_EQ(NODETYPE_ELEMENT, node_type); 705 EXPECT_EQ(1, num_children); 706 707 base::win::ScopedComPtr<ISimpleDOMNode> checkbox_isimpledomnode; 708 hr = body_isimpledomnode->get_firstChild( 709 checkbox_isimpledomnode.Receive()); 710 ASSERT_EQ(S_OK, hr); 711 hr = checkbox_isimpledomnode->get_nodeInfo( 712 &node_name, &name_space_id, &node_value, &num_children, &unique_id, 713 &node_type); 714 ASSERT_EQ(S_OK, hr); 715 EXPECT_STREQ(L"input", wstring(node_name, SysStringLen(node_name)).c_str()); 716 EXPECT_EQ(NODETYPE_ELEMENT, node_type); 717 EXPECT_EQ(0, num_children); 718 } 719 } // namespace. 720