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