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.
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"
22 // CountedBrowserAccessibility ------------------------------------------------
24 // Subclass of BrowserAccessibilityWin that counts the number of instances.
25 class CountedBrowserAccessibility : public BrowserAccessibilityWin {
27 CountedBrowserAccessibility();
28 virtual ~CountedBrowserAccessibility();
30 static void reset() { num_instances_ = 0; }
31 static int num_instances() { return num_instances_; }
34 static int num_instances_;
36 DISALLOW_COPY_AND_ASSIGN(CountedBrowserAccessibility);
40 int CountedBrowserAccessibility::num_instances_ = 0;
42 CountedBrowserAccessibility::CountedBrowserAccessibility() {
46 CountedBrowserAccessibility::~CountedBrowserAccessibility() {
51 // CountedBrowserAccessibilityFactory -----------------------------------------
53 // Factory that creates a CountedBrowserAccessibility.
54 class CountedBrowserAccessibilityFactory : public BrowserAccessibilityFactory {
56 CountedBrowserAccessibilityFactory();
59 virtual ~CountedBrowserAccessibilityFactory();
61 virtual BrowserAccessibility* Create() OVERRIDE;
63 DISALLOW_COPY_AND_ASSIGN(CountedBrowserAccessibilityFactory);
66 CountedBrowserAccessibilityFactory::CountedBrowserAccessibilityFactory() {
69 CountedBrowserAccessibilityFactory::~CountedBrowserAccessibilityFactory() {
72 BrowserAccessibility* CountedBrowserAccessibilityFactory::Create() {
73 CComObject<CountedBrowserAccessibility>* instance;
74 HRESULT hr = CComObject<CountedBrowserAccessibility>::CreateInstance(
76 DCHECK(SUCCEEDED(hr));
84 // BrowserAccessibilityTest ---------------------------------------------------
86 class BrowserAccessibilityTest : public testing::Test {
88 BrowserAccessibilityTest();
89 virtual ~BrowserAccessibilityTest();
92 virtual void SetUp() OVERRIDE;
94 DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityTest);
97 BrowserAccessibilityTest::BrowserAccessibilityTest() {
100 BrowserAccessibilityTest::~BrowserAccessibilityTest() {
103 void BrowserAccessibilityTest::SetUp() {
104 ui::win::CreateATLModuleIfNeeded();
108 // Actual tests ---------------------------------------------------------------
110 // Test that BrowserAccessibilityManager correctly releases the tree of
111 // BrowserAccessibility instances upon delete.
112 TEST_F(BrowserAccessibilityTest, TestNoLeaks) {
113 // Create ui::AXNodeData objects for a simple document tree,
114 // representing the accessibility information used to initialize
115 // BrowserAccessibilityManager.
116 ui::AXNodeData button;
118 button.SetName("Button");
119 button.role = ui::AX_ROLE_BUTTON;
122 ui::AXNodeData checkbox;
124 checkbox.SetName("Checkbox");
125 checkbox.role = ui::AX_ROLE_CHECK_BOX;
130 root.SetName("Document");
131 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
133 root.child_ids.push_back(2);
134 root.child_ids.push_back(3);
136 // Construct a BrowserAccessibilityManager with this
137 // ui::AXNodeData 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());
147 // Delete the manager and test that all 3 instances are deleted.
149 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
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);
164 base::win::ScopedVariant one(1);
165 hr = root_accessible->get_accChild(one, &child1_iaccessible);
168 // Now delete the manager, and only one of the three nodes in the tree
169 // should be released.
171 ASSERT_EQ(2, CountedBrowserAccessibility::num_instances());
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());
181 TEST_F(BrowserAccessibilityTest, TestChildrenChange) {
182 // Create ui::AXNodeData objects for a simple document tree,
183 // representing the accessibility information used to initialize
184 // BrowserAccessibilityManager.
187 text.role = ui::AX_ROLE_STATIC_TEXT;
188 text.SetName("old text");
193 root.SetName("Document");
194 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
196 root.child_ids.push_back(2);
198 // Construct a BrowserAccessibilityManager with this
199 // ui::AXNodeData 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);
207 // Query for the text IAccessible and verify that it returns "old text" as its
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());
215 base::win::ScopedComPtr<IAccessible> text_accessible;
216 hr = text_dispatch.QueryInterface(text_accessible.Receive());
219 base::win::ScopedVariant childid_self(CHILDID_SELF);
220 base::win::ScopedBstr name;
221 hr = text_accessible->get_accName(childid_self, name.Receive());
223 EXPECT_EQ(L"old text", base::string16(name));
226 text_dispatch.Release();
227 text_accessible.Release();
229 // Notify the BrowserAccessibilityManager that the text child has changed.
230 ui::AXNodeData text2;
232 text2.role = ui::AX_ROLE_STATIC_TEXT;
233 text2.SetName("new text");
234 text2.SetName("old text");
235 AccessibilityHostMsg_EventParams param;
236 param.event_type = ui::AX_EVENT_CHILDREN_CHANGED;
237 param.nodes.push_back(text2);
239 std::vector<AccessibilityHostMsg_EventParams> events;
240 events.push_back(param);
241 manager->OnAccessibilityEvents(events);
243 // Query for the text IAccessible and verify that it now returns "new text"
245 hr = manager->GetRoot()->ToBrowserAccessibilityWin()->get_accChild(
246 one, text_dispatch.Receive());
249 hr = text_dispatch.QueryInterface(text_accessible.Receive());
252 hr = text_accessible->get_accName(childid_self, name.Receive());
254 EXPECT_EQ(L"new text", base::string16(name));
256 text_dispatch.Release();
257 text_accessible.Release();
259 // Delete the manager and test that all BrowserAccessibility instances are
262 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
265 TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) {
266 // Create ui::AXNodeData objects for a simple document tree,
267 // representing the accessibility information used to initialize
268 // BrowserAccessibilityManager.
271 div.role = ui::AX_ROLE_GROUP;
274 ui::AXNodeData text3;
276 text3.role = ui::AX_ROLE_STATIC_TEXT;
279 ui::AXNodeData text4;
281 text4.role = ui::AX_ROLE_STATIC_TEXT;
284 div.child_ids.push_back(3);
285 div.child_ids.push_back(4);
289 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
291 root.child_ids.push_back(2);
293 // Construct a BrowserAccessibilityManager with this
294 // ui::AXNodeData 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());
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 = ui::AX_EVENT_CHILDREN_CHANGED;
309 param.nodes.push_back(root);
311 std::vector<AccessibilityHostMsg_EventParams> events;
312 events.push_back(param);
313 manager->OnAccessibilityEvents(events);
314 ASSERT_EQ(1, CountedBrowserAccessibility::num_instances());
316 // Delete the manager and test that all BrowserAccessibility instances are
319 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
322 TEST_F(BrowserAccessibilityTest, TestTextBoundaries) {
323 std::string text1_value = "One two three.\nFour five six.";
325 ui::AXNodeData text1;
327 text1.role = ui::AX_ROLE_TEXT_FIELD;
329 text1.AddStringAttribute(ui::AX_ATTR_VALUE, text1_value);
330 std::vector<int32> line_breaks;
331 line_breaks.push_back(15);
332 text1.AddIntListAttribute(
333 ui::AX_ATTR_LINE_BREAKS, line_breaks);
337 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
339 root.child_ids.push_back(11);
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());
348 BrowserAccessibilityWin* root_obj =
349 manager->GetRoot()->ToBrowserAccessibilityWin();
350 BrowserAccessibilityWin* text1_obj =
351 root_obj->PlatformGetChild(0)->ToBrowserAccessibilityWin();
354 ASSERT_EQ(S_OK, text1_obj->get_nCharacters(&text1_len));
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)));
361 ASSERT_EQ(S_OK, text1_obj->get_text(0, 4, text.Receive()));
362 ASSERT_STREQ(L"One ", text);
367 ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
368 1, IA2_TEXT_BOUNDARY_CHAR, &start, &end, text.Receive()));
371 ASSERT_STREQ(L"n", text);
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);
380 ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
381 1, IA2_TEXT_BOUNDARY_WORD, &start, &end, text.Receive()));
384 ASSERT_STREQ(L"One", text);
387 ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
388 6, IA2_TEXT_BOUNDARY_WORD, &start, &end, text.Receive()));
391 ASSERT_STREQ(L"two", text);
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);
398 ASSERT_STREQ(L"six.", text);
401 ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
402 1, IA2_TEXT_BOUNDARY_LINE, &start, &end, text.Receive()));
405 ASSERT_STREQ(L"One two three.\n", text);
409 text1_obj->get_text(0, IA2_TEXT_OFFSET_LENGTH, text.Receive()));
410 ASSERT_STREQ(L"One two three.\nFour five six.", text);
412 // Delete the manager and test that all BrowserAccessibility instances are
415 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
418 TEST_F(BrowserAccessibilityTest, TestSimpleHypertext) {
419 const std::string text1_name = "One two three.";
420 const std::string text2_name = " Four five six.";
422 ui::AXNodeData text1;
424 text1.role = ui::AX_ROLE_STATIC_TEXT;
425 text1.state = 1 << ui::AX_STATE_READONLY;
426 text1.SetName(text1_name);
428 ui::AXNodeData text2;
430 text2.role = ui::AX_ROLE_STATIC_TEXT;
431 text2.state = 1 << ui::AX_STATE_READONLY;
432 text2.SetName(text2_name);
436 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
437 root.state = 1 << ui::AX_STATE_READONLY;
438 root.child_ids.push_back(11);
439 root.child_ids.push_back(12);
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());
448 BrowserAccessibilityWin* root_obj =
449 manager->GetRoot()->ToBrowserAccessibilityWin();
452 ASSERT_EQ(S_OK, root_obj->get_nCharacters(&text_len));
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)));
458 long hyperlink_count;
459 ASSERT_EQ(S_OK, root_obj->get_nHyperlinks(&hyperlink_count));
460 EXPECT_EQ(0, hyperlink_count);
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()));
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);
478 // Delete the manager and test that all BrowserAccessibility instances are
481 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
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";
490 ui::AXNodeData text1;
492 text1.role = ui::AX_ROLE_STATIC_TEXT;
493 text1.state = 1 << ui::AX_STATE_READONLY;
494 text1.SetName(text1_name);
496 ui::AXNodeData text2;
498 text2.role = ui::AX_ROLE_STATIC_TEXT;
499 text2.state = 1 << ui::AX_STATE_READONLY;
500 text2.SetName(text2_name);
502 ui::AXNodeData button1, button1_text;
504 button1_text.id = 15;
505 button1_text.SetName(button1_text_name);
506 button1.role = ui::AX_ROLE_BUTTON;
507 button1_text.role = ui::AX_ROLE_STATIC_TEXT;
508 button1.state = 1 << ui::AX_STATE_READONLY;
509 button1_text.state = 1 << ui::AX_STATE_READONLY;
510 button1.child_ids.push_back(15);
512 ui::AXNodeData link1, link1_text;
515 link1_text.SetName(link1_text_name);
516 link1.role = ui::AX_ROLE_LINK;
517 link1_text.role = ui::AX_ROLE_STATIC_TEXT;
518 link1.state = 1 << ui::AX_STATE_READONLY;
519 link1_text.state = 1 << ui::AX_STATE_READONLY;
520 link1.child_ids.push_back(16);
524 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
525 root.state = 1 << ui::AX_STATE_READONLY;
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);
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);
539 ASSERT_EQ(7, CountedBrowserAccessibility::num_instances());
541 BrowserAccessibilityWin* root_obj =
542 manager->GetRoot()->ToBrowserAccessibilityWin();
545 ASSERT_EQ(S_OK, root_obj->get_nCharacters(&text_len));
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 base::UTF16ToUTF8(base::string16(text)));
555 long hyperlink_count;
556 ASSERT_EQ(S_OK, root_obj->get_nHyperlinks(&hyperlink_count));
557 EXPECT_EQ(2, hyperlink_count);
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()));
565 EXPECT_EQ(S_OK, root_obj->get_hyperlink(0, hyperlink.Receive()));
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());
575 EXPECT_EQ(S_OK, root_obj->get_hyperlink(1, hyperlink.Receive()));
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());
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);
595 // Delete the manager and test that all BrowserAccessibility instances are
598 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
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 << ui::AX_STATE_BUSY;
606 const int32 readonly_state = 1 << ui::AX_STATE_READONLY;
607 const int32 enabled_state = 1 << blink::WebAXStateEnabled;
608 scoped_ptr<BrowserAccessibilityManager> manager(
609 new BrowserAccessibilityManagerWin(
612 BrowserAccessibilityManagerWin::GetEmptyDocument(),
614 new CountedBrowserAccessibilityFactory()));
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(ui::AX_ROLE_ROOT_WEB_AREA, root->role());
620 EXPECT_EQ(busy_state | readonly_state | enabled_state, root->state());
622 // Tree with a child textfield.
623 ui::AXNodeData tree1_1;
625 tree1_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
626 tree1_1.child_ids.push_back(2);
628 ui::AXNodeData tree1_2;
630 tree1_2.role = ui::AX_ROLE_TEXT_FIELD;
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 = ui::AX_EVENT_LOAD_COMPLETE;
637 msg->nodes.push_back(tree1_1);
638 msg->nodes.push_back(tree1_2);
639 msg->id = tree1_1.id;
640 manager->OnAccessibilityEvents(params);
642 // Save for later comparison.
643 BrowserAccessibility* acc1_2 = manager->GetFromRendererID(2);
645 // Verify the root has changed.
646 EXPECT_NE(root, manager->GetRoot());
648 // And the proper child remains.
649 EXPECT_EQ(ui::AX_ROLE_TEXT_FIELD, acc1_2->role());
650 EXPECT_EQ(2, acc1_2->renderer_id());
652 // Tree with a child button.
653 ui::AXNodeData tree2_1;
655 tree2_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
656 tree2_1.child_ids.push_back(3);
658 ui::AXNodeData tree2_2;
660 tree2_2.role = ui::AX_ROLE_BUTTON;
663 msg->nodes.push_back(tree2_1);
664 msg->nodes.push_back(tree2_2);
665 msg->id = tree2_1.id;
667 // Fire another load complete.
668 manager->OnAccessibilityEvents(params);
670 BrowserAccessibility* acc2_2 = manager->GetFromRendererID(3);
672 // Verify the root has changed.
673 EXPECT_NE(root, manager->GetRoot());
675 // And the new child exists.
676 EXPECT_EQ(ui::AX_ROLE_BUTTON, acc2_2->role());
677 EXPECT_EQ(3, acc2_2->renderer_id());
679 // Ensure we properly cleaned up.
681 ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
684 TEST(BrowserAccessibilityManagerWinTest, TestAccessibleHWND) {
685 HWND desktop_hwnd = GetDesktopWindow();
686 base::win::ScopedComPtr<IAccessible> desktop_hwnd_iaccessible;
687 ASSERT_EQ(S_OK, AccessibleObjectFromWindow(
688 desktop_hwnd, OBJID_CLIENT,
690 reinterpret_cast<void**>(desktop_hwnd_iaccessible.Receive())));
692 scoped_ptr<BrowserAccessibilityManagerWin> manager(
693 new BrowserAccessibilityManagerWin(
695 desktop_hwnd_iaccessible,
696 BrowserAccessibilityManagerWin::GetEmptyDocument(),
698 ASSERT_EQ(desktop_hwnd, manager->parent_hwnd());
700 // Enabling screen reader support and calling MaybeCallNotifyWinEvent
701 // should trigger creating the AccessibleHWND, and we should now get a
702 // new parent_hwnd with the right window class to fool older screen
704 BrowserAccessibilityStateImpl::GetInstance()->OnScreenReaderDetected();
705 manager->MaybeCallNotifyWinEvent(0, 0);
706 HWND new_parent_hwnd = manager->parent_hwnd();
707 ASSERT_NE(desktop_hwnd, new_parent_hwnd);
708 WCHAR hwnd_class_name[256];
709 ASSERT_NE(0, GetClassName(new_parent_hwnd, hwnd_class_name, 256));
710 ASSERT_STREQ(L"Chrome_RenderWidgetHostHWND", hwnd_class_name);
712 // Destroy the hwnd explicitly; that should trigger clearing parent_hwnd().
713 DestroyWindow(new_parent_hwnd);
714 ASSERT_EQ(NULL, manager->parent_hwnd());
716 // Now create it again.
718 new BrowserAccessibilityManagerWin(
720 desktop_hwnd_iaccessible,
721 BrowserAccessibilityManagerWin::GetEmptyDocument(),
723 manager->MaybeCallNotifyWinEvent(0, 0);
724 new_parent_hwnd = manager->parent_hwnd();
725 ASSERT_FALSE(NULL == new_parent_hwnd);
727 // This time, destroy the manager first, make sure the AccessibleHWND doesn't
728 // crash on destruction (to be caught by SyzyASAN or other tools).
732 } // namespace content