OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <atlbase.h> | 5 #include <atlbase.h> |
6 #include <vector> | 6 #include <vector> |
7 | 7 |
8 #include "base/memory/scoped_ptr.h" | 8 #include "base/memory/scoped_ptr.h" |
| 9 #include "base/utf_string_conversions.h" |
9 #include "base/win/scoped_comptr.h" | 10 #include "base/win/scoped_comptr.h" |
10 #include "chrome/browser/automation/ui_controls.h" | 11 #include "chrome/browser/automation/ui_controls.h" |
11 #include "chrome/browser/renderer_host/render_widget_host_view_win.h" | 12 #include "chrome/browser/renderer_host/render_widget_host_view_win.h" |
12 #include "chrome/browser/ui/browser.h" | 13 #include "chrome/browser/ui/browser.h" |
13 #include "chrome/browser/ui/browser_window.h" | 14 #include "chrome/browser/ui/browser_window.h" |
14 #include "chrome/common/url_constants.h" | 15 #include "chrome/common/url_constants.h" |
15 #include "chrome/test/in_process_browser_test.h" | 16 #include "chrome/test/in_process_browser_test.h" |
16 #include "chrome/test/ui_test_utils.h" | 17 #include "chrome/test/ui_test_utils.h" |
17 #include "content/browser/renderer_host/render_view_host.h" | 18 #include "content/browser/renderer_host/render_view_host.h" |
18 #include "content/browser/tab_contents/tab_contents.h" | 19 #include "content/browser/tab_contents/tab_contents.h" |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
129 // IAccessible fails. | 130 // IAccessible fails. |
130 base::win::ScopedComPtr<IServiceProvider> service_provider; | 131 base::win::ScopedComPtr<IServiceProvider> service_provider; |
131 HRESULT hr = accessible->QueryInterface(service_provider.Receive()); | 132 HRESULT hr = accessible->QueryInterface(service_provider.Receive()); |
132 if (FAILED(hr)) | 133 if (FAILED(hr)) |
133 return hr; | 134 return hr; |
134 | 135 |
135 hr = service_provider->QueryService(IID_IAccessible2, accessible2); | 136 hr = service_provider->QueryService(IID_IAccessible2, accessible2); |
136 return hr; | 137 return hr; |
137 } | 138 } |
138 | 139 |
139 // Sets result to true if the child is located in the parent's tree. An | 140 // Recursively search through all of the descendants reachable from an |
140 // exhustive search is perform here because we determine equality using | 141 // IAccessible node and return true if we find one with the given role |
141 // IAccessible2::get_unique_id which is only supported by the child node. | 142 // and name. |
142 void AccessibleContainsAccessible( | 143 void RecursiveFindNodeInAccessibilityTree( |
143 IAccessible* parent, IAccessible2* child, bool* result) { | 144 IAccessible* node, |
144 vector<base::win::ScopedComPtr<IAccessible>> accessible_list; | 145 int32 expected_role, |
145 accessible_list.push_back(base::win::ScopedComPtr<IAccessible>(parent)); | 146 const wstring& expected_name, |
| 147 int32 depth, |
| 148 bool* found) { |
| 149 CComBSTR name_bstr; |
| 150 node->get_accName(CreateI4Variant(CHILDID_SELF), &name_bstr); |
| 151 wstring name(name_bstr.m_str, SysStringLen(name_bstr)); |
| 152 VARIANT role = {0}; |
| 153 node->get_accRole(CreateI4Variant(CHILDID_SELF), &role); |
146 | 154 |
147 LONG unique_id; | 155 // Print the accessibility tree as we go, because if this test fails |
148 HRESULT hr = child->get_uniqueID(&unique_id); | 156 // on the bots, this is really helpful in figuring out why. |
| 157 for (int i = 0; i < depth; i++) { |
| 158 printf(" "); |
| 159 } |
| 160 printf("role=%d name=%s\n", role.lVal, WideToUTF8(name).c_str()); |
| 161 |
| 162 if (expected_role == role.lVal && expected_name == name) { |
| 163 *found = true; |
| 164 return; |
| 165 } |
| 166 |
| 167 LONG child_count = 0; |
| 168 HRESULT hr = node->get_accChildCount(&child_count); |
149 ASSERT_EQ(S_OK, hr); | 169 ASSERT_EQ(S_OK, hr); |
150 *result = false; | |
151 | 170 |
152 while (accessible_list.size()) { | 171 scoped_array<VARIANT> child_array(new VARIANT[child_count]); |
153 base::win::ScopedComPtr<IAccessible> accessible = accessible_list.back(); | 172 LONG obtained_count = 0; |
154 accessible_list.pop_back(); | 173 hr = AccessibleChildren( |
| 174 node, 0, child_count, child_array.get(), &obtained_count); |
| 175 ASSERT_EQ(S_OK, hr); |
| 176 ASSERT_EQ(child_count, obtained_count); |
155 | 177 |
156 base::win::ScopedComPtr<IAccessible2> accessible2; | 178 for (int index = 0; index < obtained_count; index++) { |
157 hr = QueryIAccessible2(accessible, accessible2.Receive()); | 179 base::win::ScopedComPtr<IAccessible> child_accessible( |
158 if (SUCCEEDED(hr)) { | 180 GetAccessibleFromResultVariant(node, &child_array.get()[index])); |
159 LONG child_id; | 181 if (child_accessible.get()) { |
160 accessible2->get_uniqueID(&child_id); | 182 RecursiveFindNodeInAccessibilityTree( |
161 if (child_id == unique_id) { | 183 child_accessible.get(), expected_role, expected_name, depth + 1, |
162 *result = true; | 184 found); |
163 break; | 185 if (*found) |
164 } | 186 return; |
165 } | |
166 | |
167 LONG child_count; | |
168 hr = accessible->get_accChildCount(&child_count); | |
169 ASSERT_EQ(S_OK, hr); | |
170 if (child_count == 0) | |
171 continue; | |
172 | |
173 scoped_array<VARIANT> child_array(new VARIANT[child_count]); | |
174 LONG obtained_count = 0; | |
175 hr = AccessibleChildren( | |
176 accessible, 0, child_count, child_array.get(), &obtained_count); | |
177 ASSERT_EQ(S_OK, hr); | |
178 ASSERT_EQ(child_count, obtained_count); | |
179 | |
180 for (int index = 0; index < obtained_count; index++) { | |
181 base::win::ScopedComPtr<IAccessible> child_accessible( | |
182 GetAccessibleFromResultVariant(accessible, &child_array.get()[index])); | |
183 if (child_accessible.get()) { | |
184 accessible_list.push_back( | |
185 base::win::ScopedComPtr<IAccessible>(child_accessible)); | |
186 } | |
187 } | 187 } |
188 } | 188 } |
189 } | 189 } |
190 | 190 |
191 // Retrieve the MSAA client accessibility object for the Render Widget Host View | 191 // Retrieve the MSAA client accessibility object for the Render Widget Host View |
192 // of the selected tab. | 192 // of the selected tab. |
193 IAccessible* | 193 IAccessible* |
194 AccessibilityWinBrowserTest::GetRendererAccessible() { | 194 AccessibilityWinBrowserTest::GetRendererAccessible() { |
195 HWND hwnd_render_widget_host_view = | 195 HWND hwnd_render_widget_host_view = |
196 browser()->GetSelectedTabContents()->GetRenderWidgetHostView()-> | 196 browser()->GetSelectedTabContents()->GetRenderWidgetHostView()-> |
(...skipping 385 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
582 // Set the value of the text control | 582 // Set the value of the text control |
583 ExecuteScript(L"document.body.children[0].value='new value'"); | 583 ExecuteScript(L"document.body.children[0].value='new value'"); |
584 ui_test_utils::WaitForNotification( | 584 ui_test_utils::WaitForNotification( |
585 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); | 585 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); |
586 | 586 |
587 // Check that the accessibility tree of the browser has been updated. | 587 // Check that the accessibility tree of the browser has been updated. |
588 text_field_checker.SetExpectedValue(L"new value"); | 588 text_field_checker.SetExpectedValue(L"new value"); |
589 document_checker.CheckAccessible(GetRendererAccessible()); | 589 document_checker.CheckAccessible(GetRendererAccessible()); |
590 } | 590 } |
591 | 591 |
592 // FAILS crbug.com/54220 | 592 // This test verifies that the web content's accessibility tree is a |
593 // This test verifies that browser-side cache of the renderer accessibility | 593 // descendant of the main browser window's accessibility tree, so that |
594 // tree is reachable from the browser's tree. Tools that analyze windows | 594 // tools like AccExplorer32 or AccProbe can be used to examine Chrome's |
595 // accessibility trees like AccExplorer32 should be able to drill into the | 595 // accessibility support. |
596 // cached renderer accessibility tree. | 596 // |
| 597 // If you made a change and this test now fails, check that the NativeViewHost |
| 598 // that wraps the tab contents returns the IAccessible implementation |
| 599 // provided by RenderWidgetHostViewWin in GetNativeViewAccessible(). |
597 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, | 600 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, |
598 DISABLED_ContainsRendererAccessibilityTree) { | 601 ContainsRendererAccessibilityTree) { |
599 GURL tree_url("data:text/html,<body><input type='checkbox' /></body>"); | 602 GURL tree_url("data:text/html,<html><head><title>MyDocument</title></head>" |
| 603 "<body>Content</body></html>"); |
600 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); | 604 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); |
601 GetRendererAccessible(); | 605 GetRendererAccessible(); |
602 ui_test_utils::WaitForNotification( | 606 ui_test_utils::WaitForNotification( |
603 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); | 607 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); |
604 | 608 |
605 // Get the accessibility object for the browser window. | 609 // Get the accessibility object for the browser window. |
606 HWND browser_hwnd = browser()->window()->GetNativeHandle(); | 610 HWND browser_hwnd = browser()->window()->GetNativeHandle(); |
607 base::win::ScopedComPtr<IAccessible> browser_accessible; | 611 base::win::ScopedComPtr<IAccessible> browser_accessible; |
608 HRESULT hr = AccessibleObjectFromWindow( | 612 HRESULT hr = AccessibleObjectFromWindow( |
609 browser_hwnd, | 613 browser_hwnd, |
610 OBJID_WINDOW, | 614 OBJID_WINDOW, |
611 IID_IAccessible, | 615 IID_IAccessible, |
612 reinterpret_cast<void**>(browser_accessible.Receive())); | 616 reinterpret_cast<void**>(browser_accessible.Receive())); |
613 ASSERT_EQ(S_OK, hr); | 617 ASSERT_EQ(S_OK, hr); |
614 | 618 |
615 // Get the accessibility object for the renderer client document. | |
616 base::win::ScopedComPtr<IAccessible> document_accessible( | |
617 GetRendererAccessible()); | |
618 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL)); | |
619 base::win::ScopedComPtr<IAccessible2> document_accessible2; | |
620 hr = QueryIAccessible2(document_accessible, document_accessible2.Receive()); | |
621 ASSERT_EQ(S_OK, hr); | |
622 | |
623 // TODO(ctguil): Pointer comparison of retrieved IAccessible pointers dosen't | |
624 // seem to work for here. Perhaps make IAccessible2 available in views to make | |
625 // unique id comparison available. | |
626 bool found = false; | 619 bool found = false; |
627 base::win::ScopedComPtr<IAccessible> parent = document_accessible; | 620 RecursiveFindNodeInAccessibilityTree( |
628 while (parent.get()) { | 621 browser_accessible.get(), ROLE_SYSTEM_DOCUMENT, L"MyDocument", 0, &found); |
629 base::win::ScopedComPtr<IDispatch> parent_dispatch; | |
630 hr = parent->get_accParent(parent_dispatch.Receive()); | |
631 ASSERT_TRUE(SUCCEEDED(hr)); | |
632 if (!parent_dispatch.get()) { | |
633 ASSERT_EQ(hr, S_FALSE); | |
634 break; | |
635 } | |
636 | |
637 parent.Release(); | |
638 hr = parent_dispatch.QueryInterface(parent.Receive()); | |
639 ASSERT_EQ(S_OK, hr); | |
640 | |
641 if (parent.get() == browser_accessible.get()) { | |
642 found = true; | |
643 break; | |
644 } | |
645 } | |
646 | |
647 // If pointer comparison fails resort to the exhuasive search that can use | |
648 // IAccessible2::get_unique_id for equality comparison. | |
649 if (!found) { | |
650 AccessibleContainsAccessible( | |
651 browser_accessible, document_accessible2, &found); | |
652 } | |
653 | |
654 ASSERT_EQ(found, true); | 622 ASSERT_EQ(found, true); |
655 } | 623 } |
656 | 624 |
657 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, | 625 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, |
658 SupportsISimpleDOM) { | 626 SupportsISimpleDOM) { |
659 GURL tree_url("data:text/html,<body><input type='checkbox' /></body>"); | 627 GURL tree_url("data:text/html,<body><input type='checkbox' /></body>"); |
660 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); | 628 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); |
661 GetRendererAccessible(); | 629 GetRendererAccessible(); |
662 ui_test_utils::WaitForNotification( | 630 ui_test_utils::WaitForNotification( |
663 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); | 631 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
711 ASSERT_EQ(S_OK, hr); | 679 ASSERT_EQ(S_OK, hr); |
712 hr = checkbox_isimpledomnode->get_nodeInfo( | 680 hr = checkbox_isimpledomnode->get_nodeInfo( |
713 &node_name, &name_space_id, &node_value, &num_children, &unique_id, | 681 &node_name, &name_space_id, &node_value, &num_children, &unique_id, |
714 &node_type); | 682 &node_type); |
715 ASSERT_EQ(S_OK, hr); | 683 ASSERT_EQ(S_OK, hr); |
716 EXPECT_STREQ(L"input", wstring(node_name, SysStringLen(node_name)).c_str()); | 684 EXPECT_STREQ(L"input", wstring(node_name, SysStringLen(node_name)).c_str()); |
717 EXPECT_EQ(NODETYPE_ELEMENT, node_type); | 685 EXPECT_EQ(NODETYPE_ELEMENT, node_type); |
718 EXPECT_EQ(0, num_children); | 686 EXPECT_EQ(0, num_children); |
719 } | 687 } |
720 } // namespace. | 688 } // namespace. |
OLD | NEW |