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 "base/memory/scoped_ptr.h" 6 #include "base/strings/utf_string_conversions.h" 7 #include "base/win/scoped_bstr.h" 8 #include "base/win/scoped_comptr.h" 9 #include "base/win/scoped_variant.h" 10 #include "content/browser/accessibility/browser_accessibility_manager.h" 11 #include "content/browser/accessibility/browser_accessibility_manager_win.h" 12 #include "content/browser/accessibility/browser_accessibility_state_impl.h" 13 #include "content/browser/accessibility/browser_accessibility_win.h" 14 #include "content/common/accessibility_messages.h" 15 #include "testing/gtest/include/gtest/gtest.h" 16 #include "ui/base/win/atl_module.h" 17 18 namespace content { 19 namespace { 20 21 22 // CountedBrowserAccessibility ------------------------------------------------ 23 24 // Subclass of BrowserAccessibilityWin that counts the number of instances. 25 class CountedBrowserAccessibility : public BrowserAccessibilityWin { 26 public: 27 CountedBrowserAccessibility(); 28 virtual ~CountedBrowserAccessibility(); 29 30 static void reset() { num_instances_ = 0; } 31 static int num_instances() { return num_instances_; } 32 33 private: 34 static int num_instances_; 35 36 DISALLOW_COPY_AND_ASSIGN(CountedBrowserAccessibility); 37 }; 38 39 // static 40 int CountedBrowserAccessibility::num_instances_ = 0; 41 42 CountedBrowserAccessibility::CountedBrowserAccessibility() { 43 ++num_instances_; 44 } 45 46 CountedBrowserAccessibility::~CountedBrowserAccessibility() { 47 --num_instances_; 48 } 49 50 51 // CountedBrowserAccessibilityFactory ----------------------------------------- 52 53 // Factory that creates a CountedBrowserAccessibility. 54 class CountedBrowserAccessibilityFactory : public BrowserAccessibilityFactory { 55 public: 56 CountedBrowserAccessibilityFactory(); 57 58 private: 59 virtual ~CountedBrowserAccessibilityFactory(); 60 61 virtual BrowserAccessibility* Create() OVERRIDE; 62 63 DISALLOW_COPY_AND_ASSIGN(CountedBrowserAccessibilityFactory); 64 }; 65 66 CountedBrowserAccessibilityFactory::CountedBrowserAccessibilityFactory() { 67 } 68 69 CountedBrowserAccessibilityFactory::~CountedBrowserAccessibilityFactory() { 70 } 71 72 BrowserAccessibility* CountedBrowserAccessibilityFactory::Create() { 73 CComObject<CountedBrowserAccessibility>* instance; 74 HRESULT hr = CComObject<CountedBrowserAccessibility>::CreateInstance( 75 &instance); 76 DCHECK(SUCCEEDED(hr)); 77 instance->AddRef(); 78 return instance; 79 } 80 81 } // namespace 82 83 84 // BrowserAccessibilityTest --------------------------------------------------- 85 86 class BrowserAccessibilityTest : public testing::Test { 87 public: 88 BrowserAccessibilityTest(); 89 virtual ~BrowserAccessibilityTest(); 90 91 private: 92 virtual void SetUp() OVERRIDE; 93 94 DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityTest); 95 }; 96 97 BrowserAccessibilityTest::BrowserAccessibilityTest() { 98 } 99 100 BrowserAccessibilityTest::~BrowserAccessibilityTest() { 101 } 102 103 void BrowserAccessibilityTest::SetUp() { 104 ui::win::CreateATLModuleIfNeeded(); 105 } 106 107 108 // Actual tests --------------------------------------------------------------- 109 110 // Test that BrowserAccessibilityManager correctly releases the tree of 111 // BrowserAccessibility instances upon delete. 112 TEST_F(BrowserAccessibilityTest, TestNoLeaks) { 113 // Create AccessibilityNodeData objects for a simple document tree, 114 // representing the accessibility information used to initialize 115 // BrowserAccessibilityManager. 116 AccessibilityNodeData button; 117 button.id = 2; 118 button.SetName("Button"); 119 button.role = blink::WebAXRoleButton; 120 button.state = 0; 121 122 AccessibilityNodeData checkbox; 123 checkbox.id = 3; 124 checkbox.SetName("Checkbox"); 125 checkbox.role = blink::WebAXRoleCheckBox; 126 checkbox.state = 0; 127 128 AccessibilityNodeData root; 129 root.id = 1; 130 root.SetName("Document"); 131 root.role = blink::WebAXRoleRootWebArea; 132 root.state = 0; 133 root.child_ids.push_back(2); 134 root.child_ids.push_back(3); 135 136 // Construct a BrowserAccessibilityManager with this 137 // AccessibilityNodeData tree and a factory for an instance-counting 138 // BrowserAccessibility, and ensure that exactly 3 instances were 139 // created. Note that the manager takes ownership of the factory. 140 CountedBrowserAccessibility::reset(); 141 scoped_ptr<BrowserAccessibilityManager> manager( 142 BrowserAccessibilityManager::Create( 143 root, NULL, new CountedBrowserAccessibilityFactory())); 144 manager->UpdateNodesForTesting(button, checkbox); 145 ASSERT_EQ(3, CountedBrowserAccessibility::num_instances()); 146 147 // Delete the manager and test that all 3 instances are deleted. 148 manager.reset(); 149 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances()); 150 151 // Construct a manager again, and this time use the IAccessible interface 152 // to get new references to two of the three nodes in the tree. 153 manager.reset(BrowserAccessibilityManager::Create( 154 root, NULL, new CountedBrowserAccessibilityFactory())); 155 manager->UpdateNodesForTesting(button, checkbox); 156 ASSERT_EQ(3, CountedBrowserAccessibility::num_instances()); 157 IAccessible* root_accessible = 158 manager->GetRoot()->ToBrowserAccessibilityWin(); 159 IDispatch* root_iaccessible = NULL; 160 IDispatch* child1_iaccessible = NULL; 161 base::win::ScopedVariant childid_self(CHILDID_SELF); 162 HRESULT hr = root_accessible->get_accChild(childid_self, &root_iaccessible); 163 ASSERT_EQ(S_OK, hr); 164 base::win::ScopedVariant one(1); 165 hr = root_accessible->get_accChild(one, &child1_iaccessible); 166 ASSERT_EQ(S_OK, hr); 167 168 // Now delete the manager, and only one of the three nodes in the tree 169 // should be released. 170 manager.reset(); 171 ASSERT_EQ(2, CountedBrowserAccessibility::num_instances()); 172 173 // Release each of our references and make sure that each one results in 174 // the instance being deleted as its reference count hits zero. 175 root_iaccessible->Release(); 176 ASSERT_EQ(1, CountedBrowserAccessibility::num_instances()); 177 child1_iaccessible->Release(); 178 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances()); 179 } 180 181 TEST_F(BrowserAccessibilityTest, TestChildrenChange) { 182 // Create AccessibilityNodeData objects for a simple document tree, 183 // representing the accessibility information used to initialize 184 // BrowserAccessibilityManager. 185 AccessibilityNodeData text; 186 text.id = 2; 187 text.role = blink::WebAXRoleStaticText; 188 text.SetName("old text"); 189 text.state = 0; 190 191 AccessibilityNodeData root; 192 root.id = 1; 193 root.SetName("Document"); 194 root.role = blink::WebAXRoleRootWebArea; 195 root.state = 0; 196 root.child_ids.push_back(2); 197 198 // Construct a BrowserAccessibilityManager with this 199 // AccessibilityNodeData tree and a factory for an instance-counting 200 // BrowserAccessibility. 201 CountedBrowserAccessibility::reset(); 202 scoped_ptr<BrowserAccessibilityManager> manager( 203 BrowserAccessibilityManager::Create( 204 root, NULL, new CountedBrowserAccessibilityFactory())); 205 manager->UpdateNodesForTesting(text); 206 207 // Query for the text IAccessible and verify that it returns "old text" as its 208 // value. 209 base::win::ScopedVariant one(1); 210 base::win::ScopedComPtr<IDispatch> text_dispatch; 211 HRESULT hr = manager->GetRoot()->ToBrowserAccessibilityWin()->get_accChild( 212 one, text_dispatch.Receive()); 213 ASSERT_EQ(S_OK, hr); 214 215 base::win::ScopedComPtr<IAccessible> text_accessible; 216 hr = text_dispatch.QueryInterface(text_accessible.Receive()); 217 ASSERT_EQ(S_OK, hr); 218 219 base::win::ScopedVariant childid_self(CHILDID_SELF); 220 base::win::ScopedBstr name; 221 hr = text_accessible->get_accName(childid_self, name.Receive()); 222 ASSERT_EQ(S_OK, hr); 223 EXPECT_EQ(L"old text", base::string16(name)); 224 name.Reset(); 225 226 text_dispatch.Release(); 227 text_accessible.Release(); 228 229 // Notify the BrowserAccessibilityManager that the text child has changed. 230 AccessibilityNodeData text2; 231 text2.id = 2; 232 text2.role = blink::WebAXRoleStaticText; 233 text2.SetName("new text"); 234 text2.SetName("old text"); 235 AccessibilityHostMsg_EventParams param; 236 param.event_type = blink::WebAXEventChildrenChanged; 237 param.nodes.push_back(text2); 238 param.id = text2.id; 239 std::vector<AccessibilityHostMsg_EventParams> events; 240 events.push_back(param); 241 manager->OnAccessibilityEvents(events); 242 243 // Query for the text IAccessible and verify that it now returns "new text" 244 // as its value. 245 hr = manager->GetRoot()->ToBrowserAccessibilityWin()->get_accChild( 246 one, text_dispatch.Receive()); 247 ASSERT_EQ(S_OK, hr); 248 249 hr = text_dispatch.QueryInterface(text_accessible.Receive()); 250 ASSERT_EQ(S_OK, hr); 251 252 hr = text_accessible->get_accName(childid_self, name.Receive()); 253 ASSERT_EQ(S_OK, hr); 254 EXPECT_EQ(L"new text", base::string16(name)); 255 256 text_dispatch.Release(); 257 text_accessible.Release(); 258 259 // Delete the manager and test that all BrowserAccessibility instances are 260 // deleted. 261 manager.reset(); 262 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances()); 263 } 264 265 TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) { 266 // Create AccessibilityNodeData objects for a simple document tree, 267 // representing the accessibility information used to initialize 268 // BrowserAccessibilityManager. 269 AccessibilityNodeData div; 270 div.id = 2; 271 div.role = blink::WebAXRoleGroup; 272 div.state = 0; 273 274 AccessibilityNodeData text3; 275 text3.id = 3; 276 text3.role = blink::WebAXRoleStaticText; 277 text3.state = 0; 278 279 AccessibilityNodeData text4; 280 text4.id = 4; 281 text4.role = blink::WebAXRoleStaticText; 282 text4.state = 0; 283 284 div.child_ids.push_back(3); 285 div.child_ids.push_back(4); 286 287 AccessibilityNodeData root; 288 root.id = 1; 289 root.role = blink::WebAXRoleRootWebArea; 290 root.state = 0; 291 root.child_ids.push_back(2); 292 293 // Construct a BrowserAccessibilityManager with this 294 // AccessibilityNodeData tree and a factory for an instance-counting 295 // BrowserAccessibility and ensure that exactly 4 instances were 296 // created. Note that the manager takes ownership of the factory. 297 CountedBrowserAccessibility::reset(); 298 scoped_ptr<BrowserAccessibilityManager> manager( 299 BrowserAccessibilityManager::Create( 300 root, NULL, new CountedBrowserAccessibilityFactory())); 301 manager->UpdateNodesForTesting(div, text3, text4); 302 ASSERT_EQ(4, CountedBrowserAccessibility::num_instances()); 303 304 // Notify the BrowserAccessibilityManager that the div node and its children 305 // were removed and ensure that only one BrowserAccessibility instance exists. 306 root.child_ids.clear(); 307 AccessibilityHostMsg_EventParams param; 308 param.event_type = blink::WebAXEventChildrenChanged; 309 param.nodes.push_back(root); 310 param.id = root.id; 311 std::vector<AccessibilityHostMsg_EventParams> events; 312 events.push_back(param); 313 manager->OnAccessibilityEvents(events); 314 ASSERT_EQ(1, CountedBrowserAccessibility::num_instances()); 315 316 // Delete the manager and test that all BrowserAccessibility instances are 317 // deleted. 318 manager.reset(); 319 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances()); 320 } 321 322 TEST_F(BrowserAccessibilityTest, TestTextBoundaries) { 323 std::string text1_value = "One two three.\nFour five six."; 324 325 AccessibilityNodeData text1; 326 text1.id = 11; 327 text1.role = blink::WebAXRoleTextField; 328 text1.state = 0; 329 text1.AddStringAttribute(AccessibilityNodeData::ATTR_VALUE, text1_value); 330 std::vector<int32> line_breaks; 331 line_breaks.push_back(15); 332 text1.AddIntListAttribute( 333 AccessibilityNodeData::ATTR_LINE_BREAKS, line_breaks); 334 335 AccessibilityNodeData root; 336 root.id = 1; 337 root.role = blink::WebAXRoleRootWebArea; 338 root.state = 0; 339 root.child_ids.push_back(11); 340 341 CountedBrowserAccessibility::reset(); 342 scoped_ptr<BrowserAccessibilityManager> manager( 343 BrowserAccessibilityManager::Create( 344 root, NULL, new CountedBrowserAccessibilityFactory())); 345 manager->UpdateNodesForTesting(text1); 346 ASSERT_EQ(2, CountedBrowserAccessibility::num_instances()); 347 348 BrowserAccessibilityWin* root_obj = 349 manager->GetRoot()->ToBrowserAccessibilityWin(); 350 BrowserAccessibilityWin* text1_obj = 351 root_obj->PlatformGetChild(0)->ToBrowserAccessibilityWin(); 352 353 long text1_len; 354 ASSERT_EQ(S_OK, text1_obj->get_nCharacters(&text1_len)); 355 356 base::win::ScopedBstr text; 357 ASSERT_EQ(S_OK, text1_obj->get_text(0, text1_len, text.Receive())); 358 ASSERT_EQ(text1_value, base::UTF16ToUTF8(base::string16(text))); 359 text.Reset(); 360 361 ASSERT_EQ(S_OK, text1_obj->get_text(0, 4, text.Receive())); 362 ASSERT_STREQ(L"One ", text); 363 text.Reset(); 364 365 long start; 366 long end; 367 ASSERT_EQ(S_OK, text1_obj->get_textAtOffset( 368 1, IA2_TEXT_BOUNDARY_CHAR, &start, &end, text.Receive())); 369 ASSERT_EQ(1, start); 370 ASSERT_EQ(2, end); 371 ASSERT_STREQ(L"n", text); 372 text.Reset(); 373 374 ASSERT_EQ(S_FALSE, text1_obj->get_textAtOffset( 375 text1_len, IA2_TEXT_BOUNDARY_CHAR, &start, &end, text.Receive())); 376 ASSERT_EQ(text1_len, start); 377 ASSERT_EQ(text1_len, end); 378 text.Reset(); 379 380 ASSERT_EQ(S_OK, text1_obj->get_textAtOffset( 381 1, IA2_TEXT_BOUNDARY_WORD, &start, &end, text.Receive())); 382 ASSERT_EQ(0, start); 383 ASSERT_EQ(3, end); 384 ASSERT_STREQ(L"One", text); 385 text.Reset(); 386 387 ASSERT_EQ(S_OK, text1_obj->get_textAtOffset( 388 6, IA2_TEXT_BOUNDARY_WORD, &start, &end, text.Receive())); 389 ASSERT_EQ(4, start); 390 ASSERT_EQ(7, end); 391 ASSERT_STREQ(L"two", text); 392 text.Reset(); 393 394 ASSERT_EQ(S_OK, text1_obj->get_textAtOffset( 395 text1_len, IA2_TEXT_BOUNDARY_WORD, &start, &end, text.Receive())); 396 ASSERT_EQ(25, start); 397 ASSERT_EQ(29, end); 398 ASSERT_STREQ(L"six.", text); 399 text.Reset(); 400 401 ASSERT_EQ(S_OK, text1_obj->get_textAtOffset( 402 1, IA2_TEXT_BOUNDARY_LINE, &start, &end, text.Receive())); 403 ASSERT_EQ(0, start); 404 ASSERT_EQ(15, end); 405 ASSERT_STREQ(L"One two three.\n", text); 406 text.Reset(); 407 408 ASSERT_EQ(S_OK, 409 text1_obj->get_text(0, IA2_TEXT_OFFSET_LENGTH, text.Receive())); 410 ASSERT_STREQ(L"One two three.\nFour five six.", text); 411 412 // Delete the manager and test that all BrowserAccessibility instances are 413 // deleted. 414 manager.reset(); 415 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances()); 416 } 417 418 TEST_F(BrowserAccessibilityTest, TestSimpleHypertext) { 419 const std::string text1_name = "One two three."; 420 const std::string text2_name = " Four five six."; 421 422 AccessibilityNodeData text1; 423 text1.id = 11; 424 text1.role = blink::WebAXRoleStaticText; 425 text1.state = 1 << blink::WebAXStateReadonly; 426 text1.SetName(text1_name); 427 428 AccessibilityNodeData text2; 429 text2.id = 12; 430 text2.role = blink::WebAXRoleStaticText; 431 text2.state = 1 << blink::WebAXStateReadonly; 432 text2.SetName(text2_name); 433 434 AccessibilityNodeData root; 435 root.id = 1; 436 root.role = blink::WebAXRoleRootWebArea; 437 root.state = 1 << blink::WebAXStateReadonly; 438 root.child_ids.push_back(11); 439 root.child_ids.push_back(12); 440 441 CountedBrowserAccessibility::reset(); 442 scoped_ptr<BrowserAccessibilityManager> manager( 443 BrowserAccessibilityManager::Create( 444 root, NULL, new CountedBrowserAccessibilityFactory())); 445 manager->UpdateNodesForTesting(root, text1, text2); 446 ASSERT_EQ(3, CountedBrowserAccessibility::num_instances()); 447 448 BrowserAccessibilityWin* root_obj = 449 manager->GetRoot()->ToBrowserAccessibilityWin(); 450 451 long text_len; 452 ASSERT_EQ(S_OK, root_obj->get_nCharacters(&text_len)); 453 454 base::win::ScopedBstr text; 455 ASSERT_EQ(S_OK, root_obj->get_text(0, text_len, text.Receive())); 456 EXPECT_EQ(text1_name + text2_name, base::UTF16ToUTF8(base::string16(text))); 457 458 long hyperlink_count; 459 ASSERT_EQ(S_OK, root_obj->get_nHyperlinks(&hyperlink_count)); 460 EXPECT_EQ(0, hyperlink_count); 461 462 base::win::ScopedComPtr<IAccessibleHyperlink> hyperlink; 463 EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(-1, hyperlink.Receive())); 464 EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(0, hyperlink.Receive())); 465 EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(28, hyperlink.Receive())); 466 EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(29, hyperlink.Receive())); 467 468 long hyperlink_index; 469 EXPECT_EQ(E_FAIL, root_obj->get_hyperlinkIndex(0, &hyperlink_index)); 470 EXPECT_EQ(-1, hyperlink_index); 471 EXPECT_EQ(E_FAIL, root_obj->get_hyperlinkIndex(28, &hyperlink_index)); 472 EXPECT_EQ(-1, hyperlink_index); 473 EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlinkIndex(-1, &hyperlink_index)); 474 EXPECT_EQ(-1, hyperlink_index); 475 EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlinkIndex(29, &hyperlink_index)); 476 EXPECT_EQ(-1, hyperlink_index); 477 478 // Delete the manager and test that all BrowserAccessibility instances are 479 // deleted. 480 manager.reset(); 481 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances()); 482 } 483 484 TEST_F(BrowserAccessibilityTest, TestComplexHypertext) { 485 const std::string text1_name = "One two three."; 486 const std::string text2_name = " Four five six."; 487 const std::string button1_text_name = "red"; 488 const std::string link1_text_name = "blue"; 489 490 AccessibilityNodeData text1; 491 text1.id = 11; 492 text1.role = blink::WebAXRoleStaticText; 493 text1.state = 1 << blink::WebAXStateReadonly; 494 text1.SetName(text1_name); 495 496 AccessibilityNodeData text2; 497 text2.id = 12; 498 text2.role = blink::WebAXRoleStaticText; 499 text2.state = 1 << blink::WebAXStateReadonly; 500 text2.SetName(text2_name); 501 502 AccessibilityNodeData button1, button1_text; 503 button1.id = 13; 504 button1_text.id = 15; 505 button1_text.SetName(button1_text_name); 506 button1.role = blink::WebAXRoleButton; 507 button1_text.role = blink::WebAXRoleStaticText; 508 button1.state = 1 << blink::WebAXStateReadonly; 509 button1_text.state = 1 << blink::WebAXStateReadonly; 510 button1.child_ids.push_back(15); 511 512 AccessibilityNodeData link1, link1_text; 513 link1.id = 14; 514 link1_text.id = 16; 515 link1_text.SetName(link1_text_name); 516 link1.role = blink::WebAXRoleLink; 517 link1_text.role = blink::WebAXRoleStaticText; 518 link1.state = 1 << blink::WebAXStateReadonly; 519 link1_text.state = 1 << blink::WebAXStateReadonly; 520 link1.child_ids.push_back(16); 521 522 AccessibilityNodeData root; 523 root.id = 1; 524 root.role = blink::WebAXRoleRootWebArea; 525 root.state = 1 << blink::WebAXStateReadonly; 526 root.child_ids.push_back(11); 527 root.child_ids.push_back(13); 528 root.child_ids.push_back(12); 529 root.child_ids.push_back(14); 530 531 CountedBrowserAccessibility::reset(); 532 scoped_ptr<BrowserAccessibilityManager> manager( 533 BrowserAccessibilityManager::Create( 534 root, NULL, new CountedBrowserAccessibilityFactory())); 535 manager->UpdateNodesForTesting(root, 536 text1, button1, button1_text, 537 text2, link1, link1_text); 538 539 ASSERT_EQ(7, CountedBrowserAccessibility::num_instances()); 540 541 BrowserAccessibilityWin* root_obj = 542 manager->GetRoot()->ToBrowserAccessibilityWin(); 543 544 long text_len; 545 ASSERT_EQ(S_OK, root_obj->get_nCharacters(&text_len)); 546 547 base::win::ScopedBstr text; 548 ASSERT_EQ(S_OK, root_obj->get_text(0, text_len, text.Receive())); 549 const std::string embed = base::UTF16ToUTF8( 550 BrowserAccessibilityWin::kEmbeddedCharacter); 551 EXPECT_EQ(text1_name + embed + text2_name + embed, 552 UTF16ToUTF8(base::string16(text))); 553 text.Reset(); 554 555 long hyperlink_count; 556 ASSERT_EQ(S_OK, root_obj->get_nHyperlinks(&hyperlink_count)); 557 EXPECT_EQ(2, hyperlink_count); 558 559 base::win::ScopedComPtr<IAccessibleHyperlink> hyperlink; 560 base::win::ScopedComPtr<IAccessibleText> hypertext; 561 EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(-1, hyperlink.Receive())); 562 EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(2, hyperlink.Receive())); 563 EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(28, hyperlink.Receive())); 564 565 EXPECT_EQ(S_OK, root_obj->get_hyperlink(0, hyperlink.Receive())); 566 EXPECT_EQ(S_OK, 567 hyperlink.QueryInterface<IAccessibleText>(hypertext.Receive())); 568 EXPECT_EQ(S_OK, hypertext->get_text(0, 3, text.Receive())); 569 EXPECT_STREQ(button1_text_name.c_str(), 570 base::UTF16ToUTF8(base::string16(text)).c_str()); 571 text.Reset(); 572 hyperlink.Release(); 573 hypertext.Release(); 574 575 EXPECT_EQ(S_OK, root_obj->get_hyperlink(1, hyperlink.Receive())); 576 EXPECT_EQ(S_OK, 577 hyperlink.QueryInterface<IAccessibleText>(hypertext.Receive())); 578 EXPECT_EQ(S_OK, hypertext->get_text(0, 4, text.Receive())); 579 EXPECT_STREQ(link1_text_name.c_str(), 580 base::UTF16ToUTF8(base::string16(text)).c_str()); 581 text.Reset(); 582 hyperlink.Release(); 583 hypertext.Release(); 584 585 long hyperlink_index; 586 EXPECT_EQ(E_FAIL, root_obj->get_hyperlinkIndex(0, &hyperlink_index)); 587 EXPECT_EQ(-1, hyperlink_index); 588 EXPECT_EQ(E_FAIL, root_obj->get_hyperlinkIndex(28, &hyperlink_index)); 589 EXPECT_EQ(-1, hyperlink_index); 590 EXPECT_EQ(S_OK, root_obj->get_hyperlinkIndex(14, &hyperlink_index)); 591 EXPECT_EQ(0, hyperlink_index); 592 EXPECT_EQ(S_OK, root_obj->get_hyperlinkIndex(30, &hyperlink_index)); 593 EXPECT_EQ(1, hyperlink_index); 594 595 // Delete the manager and test that all BrowserAccessibility instances are 596 // deleted. 597 manager.reset(); 598 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances()); 599 } 600 601 TEST_F(BrowserAccessibilityTest, TestCreateEmptyDocument) { 602 // Try creating an empty document with busy state. Readonly is 603 // set automatically. 604 CountedBrowserAccessibility::reset(); 605 const int32 busy_state = 1 << blink::WebAXStateBusy; 606 const int32 readonly_state = 1 << blink::WebAXStateReadonly; 607 const int32 enabled_state = 1 << blink::WebAXStateEnabled; 608 scoped_ptr<BrowserAccessibilityManager> manager( 609 new BrowserAccessibilityManagerWin( 610 GetDesktopWindow(), 611 NULL, 612 BrowserAccessibilityManagerWin::GetEmptyDocument(), 613 NULL, 614 new CountedBrowserAccessibilityFactory())); 615 616 // Verify the root is as we expect by default. 617 BrowserAccessibility* root = manager->GetRoot(); 618 EXPECT_EQ(0, root->renderer_id()); 619 EXPECT_EQ(blink::WebAXRoleRootWebArea, root->role()); 620 EXPECT_EQ(busy_state | readonly_state | enabled_state, root->state()); 621 622 // Tree with a child textfield. 623 AccessibilityNodeData tree1_1; 624 tree1_1.id = 1; 625 tree1_1.role = blink::WebAXRoleRootWebArea; 626 tree1_1.child_ids.push_back(2); 627 628 AccessibilityNodeData tree1_2; 629 tree1_2.id = 2; 630 tree1_2.role = blink::WebAXRoleTextField; 631 632 // Process a load complete. 633 std::vector<AccessibilityHostMsg_EventParams> params; 634 params.push_back(AccessibilityHostMsg_EventParams()); 635 AccessibilityHostMsg_EventParams* msg = ¶ms[0]; 636 msg->event_type = blink::WebAXEventLoadComplete; 637 msg->nodes.push_back(tree1_1); 638 msg->nodes.push_back(tree1_2); 639 msg->id = tree1_1.id; 640 manager->OnAccessibilityEvents(params); 641 642 // Save for later comparison. 643 BrowserAccessibility* acc1_2 = manager->GetFromRendererID(2); 644 645 // Verify the root has changed. 646 EXPECT_NE(root, manager->GetRoot()); 647 648 // And the proper child remains. 649 EXPECT_EQ(blink::WebAXRoleTextField, acc1_2->role()); 650 EXPECT_EQ(2, acc1_2->renderer_id()); 651 652 // Tree with a child button. 653 AccessibilityNodeData tree2_1; 654 tree2_1.id = 1; 655 tree2_1.role = blink::WebAXRoleRootWebArea; 656 tree2_1.child_ids.push_back(3); 657 658 AccessibilityNodeData tree2_2; 659 tree2_2.id = 3; 660 tree2_2.role = blink::WebAXRoleButton; 661 662 msg->nodes.clear(); 663 msg->nodes.push_back(tree2_1); 664 msg->nodes.push_back(tree2_2); 665 msg->id = tree2_1.id; 666 667 // Fire another load complete. 668 manager->OnAccessibilityEvents(params); 669 670 BrowserAccessibility* acc2_2 = manager->GetFromRendererID(3); 671 672 // Verify the root has changed. 673 EXPECT_NE(root, manager->GetRoot()); 674 675 // And the new child exists. 676 EXPECT_EQ(blink::WebAXRoleButton, acc2_2->role()); 677 EXPECT_EQ(3, acc2_2->renderer_id()); 678 679 // Ensure we properly cleaned up. 680 manager.reset(); 681 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances()); 682 } 683 684 #if defined(USE_AURA) 685 TEST(BrowserAccessibilityManagerWinTest, TestAccessibleHWND) { 686 HWND desktop_hwnd = GetDesktopWindow(); 687 base::win::ScopedComPtr<IAccessible> desktop_hwnd_iaccessible; 688 ASSERT_EQ(S_OK, AccessibleObjectFromWindow( 689 desktop_hwnd, OBJID_CLIENT, 690 IID_IAccessible, 691 reinterpret_cast<void**>(desktop_hwnd_iaccessible.Receive()))); 692 693 scoped_ptr<BrowserAccessibilityManagerWin> manager( 694 new BrowserAccessibilityManagerWin( 695 desktop_hwnd, 696 desktop_hwnd_iaccessible, 697 BrowserAccessibilityManagerWin::GetEmptyDocument(), 698 NULL)); 699 ASSERT_EQ(desktop_hwnd, manager->parent_hwnd()); 700 701 // Enabling screen reader support and calling MaybeCallNotifyWinEvent 702 // should trigger creating the AccessibleHWND, and we should now get a 703 // new parent_hwnd with the right window class to fool older screen 704 // readers. 705 BrowserAccessibilityStateImpl::GetInstance()->OnScreenReaderDetected(); 706 manager->MaybeCallNotifyWinEvent(0, 0); 707 HWND new_parent_hwnd = manager->parent_hwnd(); 708 ASSERT_NE(desktop_hwnd, new_parent_hwnd); 709 WCHAR hwnd_class_name[256]; 710 ASSERT_NE(0, GetClassName(new_parent_hwnd, hwnd_class_name, 256)); 711 ASSERT_STREQ(L"Chrome_RenderWidgetHostHWND", hwnd_class_name); 712 713 // Destroy the hwnd explicitly; that should trigger clearing parent_hwnd(). 714 DestroyWindow(new_parent_hwnd); 715 ASSERT_EQ(NULL, manager->parent_hwnd()); 716 717 // Now create it again. 718 manager.reset( 719 new BrowserAccessibilityManagerWin( 720 desktop_hwnd, 721 desktop_hwnd_iaccessible, 722 BrowserAccessibilityManagerWin::GetEmptyDocument(), 723 NULL)); 724 manager->MaybeCallNotifyWinEvent(0, 0); 725 new_parent_hwnd = manager->parent_hwnd(); 726 ASSERT_FALSE(NULL == new_parent_hwnd); 727 728 // This time, destroy the manager first, make sure the AccessibleHWND doesn't 729 // crash on destruction (to be caught by SyzyASAN or other tools). 730 manager.reset(NULL); 731 } 732 #endif 733 734 } // namespace content 735