// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <atlbase.h> #include <vector> #include "base/win/scoped_comptr.h" #include "chrome/browser/automation/ui_controls.h" #include "chrome/browser/renderer_host/render_widget_host_view_win.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/test/in_process_browser_test.h" #include "chrome/test/ui_test_utils.h" #include "content/browser/renderer_host/render_view_host.h" #include "content/browser/tab_contents/tab_contents.h" #include "content/common/notification_type.h" #include "ia2_api_all.h" // Generated NOLINT #include "ISimpleDOMNode.h" // Generated NOLINT using std::auto_ptr; using std::vector; using std::wstring; namespace { class AccessibilityWinBrowserTest : public InProcessBrowserTest { public: AccessibilityWinBrowserTest() {} // InProcessBrowserTest void SetUpInProcessBrowserTestFixture(); protected: IAccessible* GetRendererAccessible(); void ExecuteScript(wstring script); }; void AccessibilityWinBrowserTest::SetUpInProcessBrowserTestFixture() { // If the mouse happens to be on the document then it will have the unexpected // STATE_SYSTEM_HOTTRACKED state. Move it to a non-document location. ui_controls::SendMouseMove(0, 0); } class AccessibleChecker { public: AccessibleChecker( wstring expected_name, int32 expected_role, wstring expected_value); AccessibleChecker( wstring expected_name, wstring expected_role, wstring expected_value); // Append an AccessibleChecker that verifies accessibility information for // a child IAccessible. Order is important. void AppendExpectedChild(AccessibleChecker* expected_child); // Check that the name and role of the given IAccessible instance and its // descendants match the expected names and roles that this object was // initialized with. void CheckAccessible(IAccessible* accessible); // Set the expected value for this AccessibleChecker. void SetExpectedValue(wstring expected_value); // Set the expected state for this AccessibleChecker. void SetExpectedState(LONG expected_state); private: void CheckAccessibleName(IAccessible* accessible); void CheckAccessibleRole(IAccessible* accessible); void CheckAccessibleValue(IAccessible* accessible); void CheckAccessibleState(IAccessible* accessible); void CheckAccessibleChildren(IAccessible* accessible); private: typedef vector<AccessibleChecker*> AccessibleCheckerVector; // Expected accessible name. Checked against IAccessible::get_accName. wstring name_; // Expected accessible role. Checked against IAccessible::get_accRole. CComVariant role_; // Expected accessible value. Checked against IAccessible::get_accValue. wstring value_; // Expected accessible state. Checked against IAccessible::get_accState. LONG state_; // Expected accessible children. Checked using IAccessible::get_accChildCount // and ::AccessibleChildren. AccessibleCheckerVector children_; }; VARIANT CreateI4Variant(LONG value) { VARIANT variant = {0}; V_VT(&variant) = VT_I4; V_I4(&variant) = value; return variant; } IAccessible* GetAccessibleFromResultVariant(IAccessible* parent, VARIANT *var) { switch (V_VT(var)) { case VT_DISPATCH: return CComQIPtr<IAccessible>(V_DISPATCH(var)).Detach(); break; case VT_I4: { CComPtr<IDispatch> dispatch; HRESULT hr = parent->get_accChild(CreateI4Variant(V_I4(var)), &dispatch); EXPECT_TRUE(SUCCEEDED(hr)); return CComQIPtr<IAccessible>(dispatch).Detach(); break; } } return NULL; } HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) { // TODO(ctguil): For some reason querying the IAccessible2 interface from // IAccessible fails. base::win::ScopedComPtr<IServiceProvider> service_provider; HRESULT hr = accessible->QueryInterface(service_provider.Receive()); if (FAILED(hr)) return hr; hr = service_provider->QueryService(IID_IAccessible2, accessible2); return hr; } // Sets result to true if the child is located in the parent's tree. An // exhustive search is perform here because we determine equality using // IAccessible2::get_unique_id which is only supported by the child node. void AccessibleContainsAccessible( IAccessible* parent, IAccessible2* child, bool* result) { vector<base::win::ScopedComPtr<IAccessible>> accessible_list; accessible_list.push_back(base::win::ScopedComPtr<IAccessible>(parent)); LONG unique_id; HRESULT hr = child->get_uniqueID(&unique_id); ASSERT_EQ(S_OK, hr); *result = false; while (accessible_list.size()) { base::win::ScopedComPtr<IAccessible> accessible = accessible_list.back(); accessible_list.pop_back(); base::win::ScopedComPtr<IAccessible2> accessible2; hr = QueryIAccessible2(accessible, accessible2.Receive()); if (SUCCEEDED(hr)) { LONG child_id; accessible2->get_uniqueID(&child_id); if (child_id == unique_id) { *result = true; break; } } LONG child_count; hr = accessible->get_accChildCount(&child_count); ASSERT_EQ(S_OK, hr); if (child_count == 0) continue; auto_ptr<VARIANT> child_array(new VARIANT[child_count]); LONG obtained_count = 0; hr = AccessibleChildren( accessible, 0, child_count, child_array.get(), &obtained_count); ASSERT_EQ(S_OK, hr); ASSERT_EQ(child_count, obtained_count); for (int index = 0; index < obtained_count; index++) { base::win::ScopedComPtr<IAccessible> child_accessible( GetAccessibleFromResultVariant(accessible, &child_array.get()[index])); if (child_accessible.get()) { accessible_list.push_back( base::win::ScopedComPtr<IAccessible>(child_accessible)); } } } } // Retrieve the MSAA client accessibility object for the Render Widget Host View // of the selected tab. IAccessible* AccessibilityWinBrowserTest::GetRendererAccessible() { HWND hwnd_render_widget_host_view = browser()->GetSelectedTabContents()->GetRenderWidgetHostView()-> GetNativeView(); // Invoke windows screen reader detection by sending the WM_GETOBJECT message // with kIdCustom as the LPARAM. const int32 kIdCustom = 1; SendMessage( hwnd_render_widget_host_view, WM_GETOBJECT, OBJID_CLIENT, kIdCustom); IAccessible* accessible; HRESULT hr = AccessibleObjectFromWindow( hwnd_render_widget_host_view, OBJID_CLIENT, IID_IAccessible, reinterpret_cast<void**>(&accessible)); EXPECT_EQ(S_OK, hr); EXPECT_NE(accessible, reinterpret_cast<IAccessible*>(NULL)); return accessible; } void AccessibilityWinBrowserTest::ExecuteScript(wstring script) { browser()->GetSelectedTabContents()->render_view_host()-> ExecuteJavascriptInWebFrame(L"", script); } AccessibleChecker::AccessibleChecker( wstring expected_name, int32 expected_role, wstring expected_value) : name_(expected_name), role_(expected_role), value_(expected_value), state_(-1) { } AccessibleChecker::AccessibleChecker( wstring expected_name, wstring expected_role, wstring expected_value) : name_(expected_name), role_(expected_role.c_str()), value_(expected_value), state_(-1) { } void AccessibleChecker::AppendExpectedChild( AccessibleChecker* expected_child) { children_.push_back(expected_child); } void AccessibleChecker::CheckAccessible(IAccessible* accessible) { CheckAccessibleName(accessible); CheckAccessibleRole(accessible); CheckAccessibleValue(accessible); CheckAccessibleState(accessible); CheckAccessibleChildren(accessible); } void AccessibleChecker::SetExpectedValue(wstring expected_value) { value_ = expected_value; } void AccessibleChecker::SetExpectedState(LONG expected_state) { state_ = expected_state; } void AccessibleChecker::CheckAccessibleName(IAccessible* accessible) { CComBSTR name; HRESULT hr = accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name); if (name_.empty()) { // If the object doesn't have name S_FALSE should be returned. EXPECT_EQ(hr, S_FALSE); } else { // Test that the correct string was returned. EXPECT_EQ(S_OK, hr); EXPECT_STREQ(name_.c_str(), wstring(name.m_str, SysStringLen(name)).c_str()); } } void AccessibleChecker::CheckAccessibleRole(IAccessible* accessible) { VARIANT var_role = {0}; HRESULT hr = accessible->get_accRole(CreateI4Variant(CHILDID_SELF), &var_role); ASSERT_EQ(S_OK, hr); EXPECT_TRUE(role_ == var_role); } void AccessibleChecker::CheckAccessibleValue(IAccessible* accessible) { CComBSTR value; HRESULT hr = accessible->get_accValue(CreateI4Variant(CHILDID_SELF), &value); EXPECT_EQ(S_OK, hr); // Test that the correct string was returned. EXPECT_STREQ(value_.c_str(), wstring(value.m_str, SysStringLen(value)).c_str()); } void AccessibleChecker::CheckAccessibleState(IAccessible* accessible) { if (state_ < 0) return; VARIANT var_state = {0}; HRESULT hr = accessible->get_accState(CreateI4Variant(CHILDID_SELF), &var_state); EXPECT_EQ(S_OK, hr); ASSERT_EQ(VT_I4, V_VT(&var_state)); EXPECT_EQ(state_, V_I4(&var_state)); } void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) { LONG child_count = 0; HRESULT hr = parent->get_accChildCount(&child_count); EXPECT_EQ(S_OK, hr); ASSERT_EQ(child_count, children_.size()); auto_ptr<VARIANT> child_array(new VARIANT[child_count]); LONG obtained_count = 0; hr = AccessibleChildren(parent, 0, child_count, child_array.get(), &obtained_count); ASSERT_EQ(S_OK, hr); ASSERT_EQ(child_count, obtained_count); VARIANT* child = child_array.get(); for (AccessibleCheckerVector::iterator child_checker = children_.begin(); child_checker != children_.end(); ++child_checker, ++child) { base::win::ScopedComPtr<IAccessible> child_accessible; child_accessible.Attach(GetAccessibleFromResultVariant(parent, child)); ASSERT_TRUE(child_accessible.get()); (*child_checker)->CheckAccessible(child_accessible); } } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestRendererAccessibilityTree) { // The initial accessible returned should have state STATE_SYSTEM_BUSY while // the accessibility tree is being requested from the renderer. AccessibleChecker document1_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); document1_checker.SetExpectedState( STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED | STATE_SYSTEM_BUSY); document1_checker.CheckAccessible(GetRendererAccessible()); // Wait for the initial accessibility tree to load. Busy state should clear. ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); document1_checker.SetExpectedState( STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED); document1_checker.CheckAccessible(GetRendererAccessible()); GURL tree_url( "data:text/html,<html><head><title>Accessibility Win Test</title></head>" "<body><input type='button' value='push' /><input type='checkbox' />" "</body></html>"); browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check the browser's copy of the renderer accessibility tree. AccessibleChecker button_checker(L"push", ROLE_SYSTEM_PUSHBUTTON, L"push"); AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON, L""); AccessibleChecker body_checker(L"", L"body", L""); AccessibleChecker document2_checker( L"Accessibility Win Test", ROLE_SYSTEM_DOCUMENT, L""); body_checker.AppendExpectedChild(&button_checker); body_checker.AppendExpectedChild(&checkbox_checker); document2_checker.AppendExpectedChild(&body_checker); document2_checker.CheckAccessible(GetRendererAccessible()); // Check that document accessible has a parent accessible. base::win::ScopedComPtr<IAccessible> document_accessible( GetRendererAccessible()); ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL)); base::win::ScopedComPtr<IDispatch> parent_dispatch; HRESULT hr = document_accessible->get_accParent(parent_dispatch.Receive()); EXPECT_EQ(S_OK, hr); EXPECT_NE(parent_dispatch, reinterpret_cast<IDispatch*>(NULL)); // Navigate to another page. GURL about_url("about:"); ui_test_utils::NavigateToURL(browser(), about_url); // Verify that the IAccessible reference still points to a valid object and // that calls to its methods fail since the tree is no longer valid after // the page navagation. CComBSTR name; hr = document_accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name); ASSERT_EQ(E_FAIL, hr); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestNotificationActiveDescendantChanged) { GURL tree_url("data:text/html,<ul tabindex='-1' role='radiogroup'><li id='li'" ">li</li></ul>"); browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); GetRendererAccessible(); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check the browser's copy of the renderer accessibility tree. AccessibleChecker list_marker_checker(L"", ROLE_SYSTEM_LISTITEM, L"\x2022"); AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT, L""); AccessibleChecker list_item_checker(L"", ROLE_SYSTEM_LISTITEM, L""); list_item_checker.SetExpectedState( STATE_SYSTEM_READONLY); AccessibleChecker radio_group_checker(L"", ROLE_SYSTEM_GROUPING, L""); radio_group_checker.SetExpectedState( STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY); AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); list_item_checker.AppendExpectedChild(&list_marker_checker); list_item_checker.AppendExpectedChild(&static_text_checker); radio_group_checker.AppendExpectedChild(&list_item_checker); document_checker.AppendExpectedChild(&radio_group_checker); document_checker.CheckAccessible(GetRendererAccessible()); // Set focus to the radio group. ExecuteScript(L"document.body.children[0].focus()"); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check that the accessibility tree of the browser has been updated. radio_group_checker.SetExpectedState( STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED); document_checker.CheckAccessible(GetRendererAccessible()); // Set the active descendant of the radio group ExecuteScript( L"document.body.children[0].setAttribute('aria-activedescendant', 'li')"); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check that the accessibility tree of the browser has been updated. list_item_checker.SetExpectedState( STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED); radio_group_checker.SetExpectedState( STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY); document_checker.CheckAccessible(GetRendererAccessible()); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestNotificationCheckedStateChanged) { GURL tree_url("data:text/html,<body><input type='checkbox' /></body>"); browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); GetRendererAccessible(); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check the browser's copy of the renderer accessibility tree. AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON, L""); checkbox_checker.SetExpectedState( STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY); AccessibleChecker body_checker(L"", L"body", L""); AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); body_checker.AppendExpectedChild(&checkbox_checker); document_checker.AppendExpectedChild(&body_checker); document_checker.CheckAccessible(GetRendererAccessible()); // Check the checkbox. ExecuteScript(L"document.body.children[0].checked=true"); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check that the accessibility tree of the browser has been updated. checkbox_checker.SetExpectedState( STATE_SYSTEM_CHECKED | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY); document_checker.CheckAccessible(GetRendererAccessible()); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestNotificationChildrenChanged) { // The role attribute causes the node to be in the accessibility tree. GURL tree_url( "data:text/html,<body role=group></body>"); browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); GetRendererAccessible(); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check the browser's copy of the renderer accessibility tree. AccessibleChecker body_checker(L"", L"body", L""); AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); document_checker.AppendExpectedChild(&body_checker); document_checker.CheckAccessible(GetRendererAccessible()); // Change the children of the document body. ExecuteScript(L"document.body.innerHTML='<b>new text</b>'"); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check that the accessibility tree of the browser has been updated. AccessibleChecker text_checker(L"new text", ROLE_SYSTEM_TEXT, L""); body_checker.AppendExpectedChild(&text_checker); document_checker.CheckAccessible(GetRendererAccessible()); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestNotificationChildrenChanged2) { // The role attribute causes the node to be in the accessibility tree. GURL tree_url( "data:text/html,<div role=group style='visibility: hidden'>text" "</div>"); browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); GetRendererAccessible(); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check the accessible tree of the browser. AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); document_checker.CheckAccessible(GetRendererAccessible()); // Change the children of the document body. ExecuteScript(L"document.body.children[0].style.visibility='visible'"); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check that the accessibility tree of the browser has been updated. AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_TEXT, L""); AccessibleChecker div_checker(L"", L"div", L""); document_checker.AppendExpectedChild(&div_checker); div_checker.AppendExpectedChild(&static_text_checker); document_checker.CheckAccessible(GetRendererAccessible()); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestNotificationFocusChanged) { // The role attribute causes the node to be in the accessibility tree. GURL tree_url( "data:text/html,<div role=group tabindex='-1'></div>"); browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); GetRendererAccessible(); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check the browser's copy of the renderer accessibility tree. AccessibleChecker div_checker(L"", L"div", L""); div_checker.SetExpectedState( STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_READONLY); AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); document_checker.AppendExpectedChild(&div_checker); document_checker.CheckAccessible(GetRendererAccessible()); // Focus the div in the document ExecuteScript(L"document.body.children[0].focus()"); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check that the accessibility tree of the browser has been updated. div_checker.SetExpectedState( STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED); document_checker.CheckAccessible(GetRendererAccessible()); // Focus the document accessible. This will un-focus the current node. base::win::ScopedComPtr<IAccessible> document_accessible( GetRendererAccessible()); ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL)); HRESULT hr = document_accessible->accSelect( SELFLAG_TAKEFOCUS, CreateI4Variant(CHILDID_SELF)); ASSERT_EQ(S_OK, hr); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check that the accessibility tree of the browser has been updated. div_checker.SetExpectedState( STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY); document_checker.CheckAccessible(GetRendererAccessible()); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestNotificationValueChanged) { GURL tree_url("data:text/html,<body><input type='text' value='old value'/>" "</body>"); browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); GetRendererAccessible(); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check the browser's copy of the renderer accessibility tree. AccessibleChecker text_field_div_checker(L"", L"div", L""); AccessibleChecker text_field_checker(L"", ROLE_SYSTEM_TEXT, L"old value"); text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); AccessibleChecker body_checker(L"", L"body", L""); AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L""); text_field_checker.AppendExpectedChild(&text_field_div_checker); body_checker.AppendExpectedChild(&text_field_checker); document_checker.AppendExpectedChild(&body_checker); document_checker.CheckAccessible(GetRendererAccessible()); // Set the value of the text control ExecuteScript(L"document.body.children[0].value='new value'"); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Check that the accessibility tree of the browser has been updated. text_field_checker.SetExpectedValue(L"new value"); document_checker.CheckAccessible(GetRendererAccessible()); } // FAILS crbug.com/54220 // This test verifies that browser-side cache of the renderer accessibility // tree is reachable from the browser's tree. Tools that analyze windows // accessibility trees like AccExplorer32 should be able to drill into the // cached renderer accessibility tree. IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, DISABLED_ContainsRendererAccessibilityTree) { GURL tree_url("data:text/html,<body><input type='checkbox' /></body>"); browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); GetRendererAccessible(); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Get the accessibility object for the browser window. HWND browser_hwnd = browser()->window()->GetNativeHandle(); base::win::ScopedComPtr<IAccessible> browser_accessible; HRESULT hr = AccessibleObjectFromWindow( browser_hwnd, OBJID_WINDOW, IID_IAccessible, reinterpret_cast<void**>(browser_accessible.Receive())); ASSERT_EQ(S_OK, hr); // Get the accessibility object for the renderer client document. base::win::ScopedComPtr<IAccessible> document_accessible( GetRendererAccessible()); ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL)); base::win::ScopedComPtr<IAccessible2> document_accessible2; hr = QueryIAccessible2(document_accessible, document_accessible2.Receive()); ASSERT_EQ(S_OK, hr); // TODO(ctguil): Pointer comparison of retrieved IAccessible pointers dosen't // seem to work for here. Perhaps make IAccessible2 available in views to make // unique id comparison available. bool found = false; base::win::ScopedComPtr<IAccessible> parent = document_accessible; while (parent.get()) { base::win::ScopedComPtr<IDispatch> parent_dispatch; hr = parent->get_accParent(parent_dispatch.Receive()); ASSERT_TRUE(SUCCEEDED(hr)); if (!parent_dispatch.get()) { ASSERT_EQ(hr, S_FALSE); break; } parent.Release(); hr = parent_dispatch.QueryInterface(parent.Receive()); ASSERT_EQ(S_OK, hr); if (parent.get() == browser_accessible.get()) { found = true; break; } } // If pointer comparison fails resort to the exhuasive search that can use // IAccessible2::get_unique_id for equality comparison. if (!found) { AccessibleContainsAccessible( browser_accessible, document_accessible2, &found); } ASSERT_EQ(found, true); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, SupportsISimpleDOM) { GURL tree_url("data:text/html,<body><input type='checkbox' /></body>"); browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED); GetRendererAccessible(); ui_test_utils::WaitForNotification( NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED); // Get the IAccessible object for the document. base::win::ScopedComPtr<IAccessible> document_accessible( GetRendererAccessible()); ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL)); // Get the ISimpleDOM object for the document. base::win::ScopedComPtr<IServiceProvider> service_provider; HRESULT hr = static_cast<IAccessible*>(document_accessible)->QueryInterface( service_provider.Receive()); ASSERT_EQ(S_OK, hr); const GUID refguid = {0x0c539790, 0x12e4, 0x11cf, 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8}; base::win::ScopedComPtr<ISimpleDOMNode> document_isimpledomnode; hr = static_cast<IServiceProvider *>(service_provider)->QueryService( refguid, IID_ISimpleDOMNode, reinterpret_cast<void**>(document_isimpledomnode.Receive())); ASSERT_EQ(S_OK, hr); BSTR node_name; short name_space_id; // NOLINT BSTR node_value; unsigned int num_children; unsigned int unique_id; unsigned short node_type; // NOLINT hr = document_isimpledomnode->get_nodeInfo( &node_name, &name_space_id, &node_value, &num_children, &unique_id, &node_type); ASSERT_EQ(S_OK, hr); EXPECT_EQ(NODETYPE_DOCUMENT, node_type); EXPECT_EQ(1, num_children); base::win::ScopedComPtr<ISimpleDOMNode> body_isimpledomnode; hr = document_isimpledomnode->get_firstChild( body_isimpledomnode.Receive()); ASSERT_EQ(S_OK, hr); hr = body_isimpledomnode->get_nodeInfo( &node_name, &name_space_id, &node_value, &num_children, &unique_id, &node_type); ASSERT_EQ(S_OK, hr); EXPECT_STREQ(L"body", wstring(node_name, SysStringLen(node_name)).c_str()); EXPECT_EQ(NODETYPE_ELEMENT, node_type); EXPECT_EQ(1, num_children); base::win::ScopedComPtr<ISimpleDOMNode> checkbox_isimpledomnode; hr = body_isimpledomnode->get_firstChild( checkbox_isimpledomnode.Receive()); ASSERT_EQ(S_OK, hr); hr = checkbox_isimpledomnode->get_nodeInfo( &node_name, &name_space_id, &node_value, &num_children, &unique_id, &node_type); ASSERT_EQ(S_OK, hr); EXPECT_STREQ(L"input", wstring(node_name, SysStringLen(node_name)).c_str()); EXPECT_EQ(NODETYPE_ELEMENT, node_type); EXPECT_EQ(0, num_children); } } // namespace.