| // 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. |