// Copyright (c) 2012 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 #include "base/memory/scoped_ptr.h" #include "base/strings/string_number_conversions.h" #include "base/strings/sys_string_conversions.h" #include "base/win/scoped_bstr.h" #include "base/win/scoped_comptr.h" #include "base/win/scoped_variant.h" #include "content/browser/accessibility/accessibility_mode_helper.h" #include "content/browser/accessibility/accessibility_tree_formatter.h" #include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" #include "content/public/common/url_constants.h" #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" #include "content/shell/browser/shell.h" #include "content/test/accessibility_browser_test_utils.h" #include "net/base/escape.h" #include "third_party/iaccessible2/ia2_api_all.h" #include "third_party/isimpledom/ISimpleDOMNode.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" namespace content { namespace { const char INPUT_CONTENTS[] = "Moz/5.0 (ST 6.x; WWW33) " "WebKit \"KHTML, like\"."; const char TEXTAREA_CONTENTS[] = "Moz/5.0 (ST 6.x; WWW33)\n" "WebKit \n\"KHTML, like\"."; const LONG CONTENTS_LENGTH = static_cast( (sizeof(INPUT_CONTENTS) - 1) / sizeof(char)); // AccessibilityWinBrowserTest ------------------------------------------------ class AccessibilityWinBrowserTest : public ContentBrowserTest { public: AccessibilityWinBrowserTest(); ~AccessibilityWinBrowserTest() override; protected: class AccessibleChecker; void LoadInitialAccessibilityTreeFromHtml(const std::string& html); IAccessible* GetRendererAccessible(); void ExecuteScript(const std::wstring& script); void SetUpInputField( base::win::ScopedComPtr* input_text); void SetUpTextareaField( base::win::ScopedComPtr* textarea_text); static base::win::ScopedComPtr AccessibilityWinBrowserTest::GetAccessibleFromVariant( IAccessible* parent, VARIANT* var); static HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2); static void FindNodeInAccessibilityTree(IAccessible* node, int32 expected_role, const std::wstring& expected_name, int32 depth, bool* found); static void CheckTextAtOffset( base::win::ScopedComPtr& element, LONG offset, IA2TextBoundaryType boundary_type, LONG expected_start_offset, LONG expected_end_offset, const std::wstring& expected_text); static std::vector GetAllAccessibleChildren( IAccessible* element); private: DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest); }; AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() { } AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() { } void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml( const std::string& html) { AccessibilityNotificationWaiter waiter( shell(), AccessibilityModeComplete, ui::AX_EVENT_LOAD_COMPLETE); GURL html_data_url("data:text/html," + html); NavigateToURL(shell(), html_data_url); waiter.WaitForNotification(); } // Retrieve the MSAA client accessibility object for the Render Widget Host View // of the selected tab. IAccessible* AccessibilityWinBrowserTest::GetRendererAccessible() { content::WebContents* web_contents = shell()->web_contents(); return web_contents->GetRenderWidgetHostView()->GetNativeViewAccessible(); } void AccessibilityWinBrowserTest::ExecuteScript(const std::wstring& script) { shell()->web_contents()->GetMainFrame()->ExecuteJavaScript(script); } // Loads a page with an input text field and places sample text in it. Also, // places the caret on the last character. void AccessibilityWinBrowserTest::SetUpInputField( base::win::ScopedComPtr* input_text) { ASSERT_NE(nullptr, input_text); LoadInitialAccessibilityTreeFromHtml(std::string("" "
" "
")); // Retrieve the IAccessible interface for the web page. base::win::ScopedComPtr document(GetRendererAccessible()); std::vector document_children = GetAllAccessibleChildren(document.get()); ASSERT_EQ(1, document_children.size()); base::win::ScopedComPtr form; HRESULT hr = QueryIAccessible2(GetAccessibleFromVariant( document.get(), document_children[0].AsInput()).get(), form.Receive()); ASSERT_EQ(S_OK, hr); std::vector form_children = GetAllAccessibleChildren(form.get()); ASSERT_EQ(2, form_children.size()); // Find the input text field. base::win::ScopedComPtr input; hr = QueryIAccessible2(GetAccessibleFromVariant( form.get(), form_children[1].AsInput()).get(), input.Receive()); ASSERT_EQ(S_OK, hr); LONG input_role = 0; hr = input->role(&input_role); ASSERT_EQ(S_OK, hr); ASSERT_EQ(ROLE_SYSTEM_TEXT, input_role); // Retrieve the IAccessibleText interface for the field. hr = input.QueryInterface(input_text->Receive()); ASSERT_EQ(S_OK, hr); // Set the caret on the last character. AccessibilityNotificationWaiter waiter( shell(), AccessibilityModeComplete, ui::AX_EVENT_TEXT_SELECTION_CHANGED); std::wstring caret_offset = base::UTF16ToWide(base::IntToString16( static_cast(CONTENTS_LENGTH - 1))); ExecuteScript(std::wstring( L"var textField = document.getElementById('textField');" L"textField.focus();" L"textField.setSelectionRange(") + caret_offset + L"," + caret_offset + L");"); waiter.WaitForNotification(); } // Loads a page with a textarea text field and places sample text in it. Also, // places the caret on the last character. void AccessibilityWinBrowserTest::SetUpTextareaField( base::win::ScopedComPtr* textarea_text) { ASSERT_NE(nullptr, textarea_text); LoadInitialAccessibilityTreeFromHtml(std::string("" "")); // Retrieve the IAccessible interface for the web page. base::win::ScopedComPtr document(GetRendererAccessible()); std::vector document_children = GetAllAccessibleChildren(document.get()); ASSERT_EQ(1, document_children.size()); base::win::ScopedComPtr section; HRESULT hr = QueryIAccessible2(GetAccessibleFromVariant( document.get(), document_children[0].AsInput()).get(), section.Receive()); ASSERT_EQ(S_OK, hr); std::vector section_children = GetAllAccessibleChildren(section.get()); ASSERT_EQ(1, section_children.size()); // Find the textarea text field. base::win::ScopedComPtr textarea; hr = QueryIAccessible2(GetAccessibleFromVariant( section.get(), section_children[0].AsInput()).get(), textarea.Receive()); ASSERT_EQ(S_OK, hr); LONG textarea_role = 0; hr = textarea->role(&textarea_role); ASSERT_EQ(S_OK, hr); ASSERT_EQ(ROLE_SYSTEM_TEXT, textarea_role); // Retrieve the IAccessibleText interface for the field. hr = textarea.QueryInterface(textarea_text->Receive()); ASSERT_EQ(S_OK, hr); // Set the caret on the last character. AccessibilityNotificationWaiter waiter( shell(), AccessibilityModeComplete, ui::AX_EVENT_TEXT_SELECTION_CHANGED); std::wstring caret_offset = base::UTF16ToWide(base::IntToString16( static_cast(CONTENTS_LENGTH - 1))); ExecuteScript(std::wstring( L"var textField = document.getElementById('textField');" L"textField.focus();" L"textField.setSelectionRange(") + caret_offset + L"," + caret_offset + L");"); waiter.WaitForNotification(); } // Static helpers ------------------------------------------------ base::win::ScopedComPtr AccessibilityWinBrowserTest::GetAccessibleFromVariant( IAccessible* parent, VARIANT* var) { base::win::ScopedComPtr ptr; switch (V_VT(var)) { case VT_DISPATCH: { IDispatch* dispatch = V_DISPATCH(var); if (dispatch) dispatch->QueryInterface(ptr.Receive()); break; } case VT_I4: { base::win::ScopedComPtr dispatch; HRESULT hr = parent->get_accChild(*var, dispatch.Receive()); EXPECT_TRUE(SUCCEEDED(hr)); if (dispatch.get()) dispatch.QueryInterface(ptr.Receive()); break; } } return ptr; } HRESULT AccessibilityWinBrowserTest::QueryIAccessible2( IAccessible* accessible, IAccessible2** accessible2) { // IA2 Spec dictates that IServiceProvider should be used instead of // QueryInterface when retrieving IAccessible2. base::win::ScopedComPtr service_provider; HRESULT hr = accessible->QueryInterface(service_provider.Receive()); return SUCCEEDED(hr) ? service_provider->QueryService(IID_IAccessible2, accessible2) : hr; } // Recursively search through all of the descendants reachable from an // IAccessible node and return true if we find one with the given role // and name. void AccessibilityWinBrowserTest::FindNodeInAccessibilityTree( IAccessible* node, int32 expected_role, const std::wstring& expected_name, int32 depth, bool* found) { base::win::ScopedBstr name_bstr; base::win::ScopedVariant childid_self(CHILDID_SELF); node->get_accName(childid_self, name_bstr.Receive()); std::wstring name(name_bstr, name_bstr.Length()); base::win::ScopedVariant role; node->get_accRole(childid_self, role.Receive()); ASSERT_EQ(VT_I4, role.type()); // Print the accessibility tree as we go, because if this test fails // on the bots, this is really helpful in figuring out why. for (int i = 0; i < depth; i++) printf(" "); printf("role=%s name=%s\n", base::WideToUTF8(IAccessibleRoleToString(V_I4(role.ptr()))).c_str(), base::WideToUTF8(name).c_str()); if (expected_role == V_I4(role.ptr()) && expected_name == name) { *found = true; return; } std::vector children = GetAllAccessibleChildren( node); for (size_t i = 0; i < children.size(); ++i) { base::win::ScopedComPtr child_accessible( GetAccessibleFromVariant(node, children[i].AsInput())); if (child_accessible) { FindNodeInAccessibilityTree( child_accessible.get(), expected_role, expected_name, depth + 1, found); if (*found) return; } } } // Ensures that the text and the start and end offsets retrieved using // get_textAtOffset match the expected values. void AccessibilityWinBrowserTest::CheckTextAtOffset( base::win::ScopedComPtr& element, LONG offset, IA2TextBoundaryType boundary_type, LONG expected_start_offset, LONG expected_end_offset, const std::wstring& expected_text) { testing::Message message; message << "While checking for \'" << expected_text << "\' at " << expected_start_offset << '-' << expected_end_offset << '.'; SCOPED_TRACE(message); LONG start_offset = 0; LONG end_offset = 0; base::win::ScopedBstr text; HRESULT hr = element->get_textAtOffset( offset, boundary_type, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_OK, hr); EXPECT_EQ(expected_start_offset, start_offset); EXPECT_EQ(expected_end_offset, end_offset); EXPECT_EQ(expected_text, std::wstring(text, text.Length())); } std::vector AccessibilityWinBrowserTest::GetAllAccessibleChildren( IAccessible* element) { LONG child_count = 0; HRESULT hr = element->get_accChildCount(&child_count); EXPECT_EQ(S_OK, hr); if (child_count <= 0) return std::vector(); scoped_ptr children_array(new VARIANT[child_count]); LONG obtained_count = 0; hr = AccessibleChildren( element, 0, child_count, children_array.get(), &obtained_count); EXPECT_EQ(S_OK, hr); EXPECT_EQ(child_count, obtained_count); std::vector children( static_cast(child_count)); for (size_t i = 0; i < children.size(); i++) { children[i].Reset(children_array[i]); } return children; } // AccessibleChecker ---------------------------------------------------------- class AccessibilityWinBrowserTest::AccessibleChecker { public: // This constructor can be used if the IA2 role will be the same as the MSAA // role. AccessibleChecker(const std::wstring& expected_name, int32 expected_role, const std::wstring& expected_value); AccessibleChecker(const std::wstring& expected_name, int32 expected_role, int32 expected_ia2_role, const std::wstring& expected_value); AccessibleChecker(const std::wstring& expected_name, const std::wstring& expected_role, int32 expected_ia2_role, const std::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(const std::wstring& expected_value); // Set the expected state for this AccessibleChecker. void SetExpectedState(LONG expected_state); private: typedef std::vector AccessibleCheckerVector; void CheckAccessibleName(IAccessible* accessible); void CheckAccessibleRole(IAccessible* accessible); void CheckIA2Role(IAccessible* accessible); void CheckAccessibleValue(IAccessible* accessible); void CheckAccessibleState(IAccessible* accessible); void CheckAccessibleChildren(IAccessible* accessible); base::string16 RoleVariantToString(const base::win::ScopedVariant& role); // Expected accessible name. Checked against IAccessible::get_accName. std::wstring name_; // Expected accessible role. Checked against IAccessible::get_accRole. base::win::ScopedVariant role_; // Expected IAccessible2 role. Checked against IAccessible2::role. int32 ia2_role_; // Expected accessible value. Checked against IAccessible::get_accValue. std::wstring value_; // Expected accessible state. Checked against IAccessible::get_accState. LONG state_; // Expected accessible children. Checked using IAccessible::get_accChildCount // and ::AccessibleChildren. AccessibleCheckerVector children_; }; AccessibilityWinBrowserTest::AccessibleChecker::AccessibleChecker( const std::wstring& expected_name, int32 expected_role, const std::wstring& expected_value) : name_(expected_name), role_(expected_role), ia2_role_(expected_role), value_(expected_value), state_(-1) { } AccessibilityWinBrowserTest::AccessibleChecker::AccessibleChecker( const std::wstring& expected_name, int32 expected_role, int32 expected_ia2_role, const std::wstring& expected_value) : name_(expected_name), role_(expected_role), ia2_role_(expected_ia2_role), value_(expected_value), state_(-1) { } AccessibilityWinBrowserTest::AccessibleChecker::AccessibleChecker( const std::wstring& expected_name, const std::wstring& expected_role, int32 expected_ia2_role, const std::wstring& expected_value) : name_(expected_name), role_(expected_role.c_str()), ia2_role_(expected_ia2_role), value_(expected_value), state_(-1) { } void AccessibilityWinBrowserTest::AccessibleChecker::AppendExpectedChild( AccessibleChecker* expected_child) { children_.push_back(expected_child); } void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessible( IAccessible* accessible) { SCOPED_TRACE("While checking " + base::UTF16ToUTF8(RoleVariantToString(role_))); CheckAccessibleName(accessible); CheckAccessibleRole(accessible); CheckIA2Role(accessible); CheckAccessibleValue(accessible); CheckAccessibleState(accessible); CheckAccessibleChildren(accessible); } void AccessibilityWinBrowserTest::AccessibleChecker::SetExpectedValue( const std::wstring& expected_value) { value_ = expected_value; } void AccessibilityWinBrowserTest::AccessibleChecker::SetExpectedState( LONG expected_state) { state_ = expected_state; } void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleName( IAccessible* accessible) { base::win::ScopedBstr name; base::win::ScopedVariant childid_self(CHILDID_SELF); HRESULT hr = accessible->get_accName(childid_self, name.Receive()); if (name_.empty()) { // If the object doesn't have name S_FALSE should be returned. EXPECT_EQ(S_FALSE, hr); } else { // Test that the correct string was returned. EXPECT_EQ(S_OK, hr); EXPECT_EQ(name_, std::wstring(name, name.Length())); } } void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleRole( IAccessible* accessible) { base::win::ScopedVariant role; base::win::ScopedVariant childid_self(CHILDID_SELF); HRESULT hr = accessible->get_accRole(childid_self, role.Receive()); ASSERT_EQ(S_OK, hr); EXPECT_EQ(0, role_.Compare(role)) << "Expected role: " << RoleVariantToString(role_) << "\nGot role: " << RoleVariantToString(role); } void AccessibilityWinBrowserTest::AccessibleChecker::CheckIA2Role( IAccessible* accessible) { base::win::ScopedComPtr accessible2; HRESULT hr = QueryIAccessible2(accessible, accessible2.Receive()); ASSERT_EQ(S_OK, hr); long ia2_role = 0; hr = accessible2->role(&ia2_role); ASSERT_EQ(S_OK, hr); EXPECT_EQ(ia2_role_, ia2_role) << "Expected ia2 role: " << IAccessible2RoleToString(ia2_role_) << "\nGot ia2 role: " << IAccessible2RoleToString(ia2_role); } void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleValue( IAccessible* accessible) { // Don't check the value if if's a DOCUMENT role, because the value // is supposed to be the url (and we don't keep track of that in the // test expectations). base::win::ScopedVariant role; base::win::ScopedVariant childid_self(CHILDID_SELF); HRESULT hr = accessible->get_accRole(childid_self, role.Receive()); ASSERT_EQ(S_OK, hr); if (role.type() == VT_I4 && V_I4(role.ptr()) == ROLE_SYSTEM_DOCUMENT) return; // Get the value. base::win::ScopedBstr value; hr = accessible->get_accValue(childid_self, value.Receive()); EXPECT_EQ(S_OK, hr); // Test that the correct string was returned. EXPECT_EQ(value_, std::wstring(value, value.Length())); } void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleState( IAccessible* accessible) { if (state_ < 0) return; base::win::ScopedVariant state; base::win::ScopedVariant childid_self(CHILDID_SELF); HRESULT hr = accessible->get_accState(childid_self, state.Receive()); EXPECT_EQ(S_OK, hr); ASSERT_EQ(VT_I4, state.type()); LONG obj_state = V_I4(state.ptr()); // Avoid flakiness. The "offscreen" state depends on whether the browser // window is frontmost or not, and "hottracked" depends on whether the // mouse cursor happens to be over the element. obj_state &= ~(STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_HOTTRACKED); EXPECT_EQ(state_, obj_state) << "Expected state: " << IAccessibleStateToString(state_) << "\nGot state: " << IAccessibleStateToString(obj_state); } void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleChildren( IAccessible* parent) { std::vector obtained_children = GetAllAccessibleChildren(parent); size_t child_count = obtained_children.size(); ASSERT_EQ(child_count, children_.size()); AccessibleCheckerVector::iterator child_checker; std::vector::iterator child; for (child_checker = children_.begin(), child = obtained_children.begin(); child_checker != children_.end() && child != obtained_children.end(); ++child_checker, ++child) { base::win::ScopedComPtr child_accessible( GetAccessibleFromVariant(parent, child->AsInput())); ASSERT_TRUE(child_accessible.get()); (*child_checker)->CheckAccessible(child_accessible.get()); } } base::string16 AccessibilityWinBrowserTest::AccessibleChecker::RoleVariantToString( const base::win::ScopedVariant& role) { if (role.type() == VT_I4) return IAccessibleRoleToString(V_I4(role.ptr())); if (role.type() == VT_BSTR) return base::string16(V_BSTR(role.ptr()), SysStringLen(V_BSTR(role.ptr()))); return base::string16(); } } // namespace // Tests ---------------------------------------------------------------------- IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestBusyAccessibilityTree) { if (GetBaseAccessibilityMode() != AccessibilityModeOff) return; NavigateToURL(shell(), GURL(url::kAboutBlankURL)); // The initial accessible returned should have state STATE_SYSTEM_BUSY while // the accessibility tree is being requested from the renderer. AccessibleChecker document1_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, std::wstring()); document1_checker.SetExpectedState( STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED | STATE_SYSTEM_BUSY); document1_checker.CheckAccessible(GetRendererAccessible()); } // Periodically failing. See crbug.com/145537 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, DISABLED_TestNotificationActiveDescendantChanged) { LoadInitialAccessibilityTreeFromHtml( "
    " "
  • li
"); // Check the browser's copy of the renderer accessibility tree. AccessibleChecker list_marker_checker(L"\x2022", ROLE_SYSTEM_TEXT, std::wstring()); AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT, std::wstring()); AccessibleChecker list_item_checker(std::wstring(), ROLE_SYSTEM_LISTITEM, std::wstring()); list_item_checker.SetExpectedState(STATE_SYSTEM_READONLY); AccessibleChecker radio_group_checker(L"ul", ROLE_SYSTEM_GROUPING, IA2_ROLE_SECTION, std::wstring()); radio_group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, std::wstring()); 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. scoped_ptr waiter( new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, ui::AX_EVENT_FOCUS)); ExecuteScript(L"document.body.children[0].focus()"); waiter->WaitForNotification(); // Check that the accessibility tree of the browser has been updated. radio_group_checker.SetExpectedState( STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED); document_checker.CheckAccessible(GetRendererAccessible()); // Set the active descendant of the radio group waiter.reset(new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, ui::AX_EVENT_FOCUS)); ExecuteScript( L"document.body.children[0].setAttribute('aria-activedescendant', 'li')"); waiter->WaitForNotification(); // 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); document_checker.CheckAccessible(GetRendererAccessible()); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestNotificationCheckedStateChanged) { LoadInitialAccessibilityTreeFromHtml( ""); // Check the browser's copy of the renderer accessibility tree. AccessibleChecker checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON, std::wstring()); checkbox_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION, std::wstring()); AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, std::wstring()); body_checker.AppendExpectedChild(&checkbox_checker); document_checker.AppendExpectedChild(&body_checker); document_checker.CheckAccessible(GetRendererAccessible()); // Check the checkbox. scoped_ptr waiter( new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, ui::AX_EVENT_CHECKED_STATE_CHANGED)); ExecuteScript(L"document.body.children[0].checked=true"); waiter->WaitForNotification(); // Check that the accessibility tree of the browser has been updated. checkbox_checker.SetExpectedState( STATE_SYSTEM_CHECKED | STATE_SYSTEM_FOCUSABLE); document_checker.CheckAccessible(GetRendererAccessible()); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestNotificationChildrenChanged) { // The role attribute causes the node to be in the accessibility tree. LoadInitialAccessibilityTreeFromHtml(""); // Check the browser's copy of the renderer accessibility tree. AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING, std::wstring()); AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, std::wstring()); document_checker.AppendExpectedChild(&group_checker); document_checker.CheckAccessible(GetRendererAccessible()); // Change the children of the document body. scoped_ptr waiter( new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, ui::AX_EVENT_CHILDREN_CHANGED)); ExecuteScript(L"document.body.innerHTML='new text'"); waiter->WaitForNotification(); // Check that the accessibility tree of the browser has been updated. AccessibleChecker text_checker( L"new text", ROLE_SYSTEM_STATICTEXT, std::wstring()); group_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. LoadInitialAccessibilityTreeFromHtml( "
text
"); // Check the accessible tree of the browser. AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, std::wstring()); document_checker.CheckAccessible(GetRendererAccessible()); // Change the children of the document body. scoped_ptr waiter( new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, ui::AX_EVENT_CHILDREN_CHANGED)); ExecuteScript(L"document.body.children[0].style.visibility='visible'"); waiter->WaitForNotification(); // Check that the accessibility tree of the browser has been updated. AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_STATICTEXT, std::wstring()); AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING, std::wstring()); document_checker.AppendExpectedChild(&group_checker); group_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. LoadInitialAccessibilityTreeFromHtml("
"); // Check the browser's copy of the renderer accessibility tree. SCOPED_TRACE("Check initial tree"); AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING, std::wstring()); group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, std::wstring()); document_checker.AppendExpectedChild(&group_checker); document_checker.CheckAccessible(GetRendererAccessible()); // Focus the div in the document scoped_ptr waiter( new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, ui::AX_EVENT_FOCUS)); ExecuteScript(L"document.body.children[0].focus()"); waiter->WaitForNotification(); // Check that the accessibility tree of the browser has been updated. SCOPED_TRACE("Check updated tree after focusing div"); group_checker.SetExpectedState( STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED); document_checker.CheckAccessible(GetRendererAccessible()); // Focus the document accessible. This will un-focus the current node. waiter.reset( new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, ui::AX_EVENT_BLUR)); base::win::ScopedComPtr document_accessible( GetRendererAccessible()); ASSERT_NE(document_accessible.get(), reinterpret_cast(NULL)); base::win::ScopedVariant childid_self(CHILDID_SELF); HRESULT hr = document_accessible->accSelect(SELFLAG_TAKEFOCUS, childid_self); ASSERT_EQ(S_OK, hr); waiter->WaitForNotification(); // Check that the accessibility tree of the browser has been updated. SCOPED_TRACE("Check updated tree after focusing document again"); group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); document_checker.CheckAccessible(GetRendererAccessible()); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestNotificationValueChanged) { LoadInitialAccessibilityTreeFromHtml( ""); // Check the browser's copy of the renderer accessibility tree. AccessibleChecker text_field_checker(std::wstring(), ROLE_SYSTEM_TEXT, L"old value"); text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE); AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION, std::wstring()); AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, std::wstring()); body_checker.AppendExpectedChild(&text_field_checker); document_checker.AppendExpectedChild(&body_checker); document_checker.CheckAccessible(GetRendererAccessible()); // Set the value of the text control scoped_ptr waiter( new AccessibilityNotificationWaiter( shell(), AccessibilityModeComplete, ui::AX_EVENT_VALUE_CHANGED)); ExecuteScript(L"document.body.children[0].value='new value'"); waiter->WaitForNotification(); // Check that the accessibility tree of the browser has been updated. text_field_checker.SetExpectedValue(L"new value"); document_checker.CheckAccessible(GetRendererAccessible()); } // This test verifies that the web content's accessibility tree is a // descendant of the main browser window's accessibility tree, so that // tools like AccExplorer32 or AccProbe can be used to examine Chrome's // accessibility support. // // If you made a change and this test now fails, check that the NativeViewHost // that wraps the tab contents returns the IAccessible implementation // provided by RenderWidgetHostViewWin in GetNativeViewAccessible(). // flaky: http://crbug.com/402190 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, DISABLED_ContainsRendererAccessibilityTree) { LoadInitialAccessibilityTreeFromHtml( "MyDocument" "Content"); // Get the accessibility object for the window tree host. aura::Window* window = shell()->window(); CHECK(window); aura::WindowTreeHost* window_tree_host = window->GetHost(); CHECK(window_tree_host); HWND hwnd = window_tree_host->GetAcceleratedWidget(); CHECK(hwnd); base::win::ScopedComPtr browser_accessible; HRESULT hr = AccessibleObjectFromWindow( hwnd, OBJID_WINDOW, IID_IAccessible, reinterpret_cast(browser_accessible.Receive())); ASSERT_EQ(S_OK, hr); bool found = false; FindNodeInAccessibilityTree( browser_accessible.get(), ROLE_SYSTEM_DOCUMENT, L"MyDocument", 0, &found); ASSERT_EQ(found, true); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, SupportsISimpleDOM) { LoadInitialAccessibilityTreeFromHtml( ""); // Get the IAccessible object for the document. base::win::ScopedComPtr document_accessible( GetRendererAccessible()); ASSERT_NE(document_accessible.get(), reinterpret_cast(NULL)); // Get the ISimpleDOM object for the document. base::win::ScopedComPtr service_provider; HRESULT hr = static_cast(document_accessible.get()) ->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 document_isimpledomnode; hr = static_cast(service_provider.get()) ->QueryService( refguid, IID_ISimpleDOMNode, reinterpret_cast(document_isimpledomnode.Receive())); ASSERT_EQ(S_OK, hr); base::win::ScopedBstr node_name; short name_space_id; // NOLINT base::win::ScopedBstr node_value; unsigned int num_children; unsigned int unique_id; unsigned short node_type; // NOLINT hr = document_isimpledomnode->get_nodeInfo( node_name.Receive(), &name_space_id, node_value.Receive(), &num_children, &unique_id, &node_type); ASSERT_EQ(S_OK, hr); EXPECT_EQ(NODETYPE_DOCUMENT, node_type); EXPECT_EQ(1, num_children); node_name.Reset(); node_value.Reset(); base::win::ScopedComPtr body_isimpledomnode; hr = document_isimpledomnode->get_firstChild( body_isimpledomnode.Receive()); ASSERT_EQ(S_OK, hr); hr = body_isimpledomnode->get_nodeInfo( node_name.Receive(), &name_space_id, node_value.Receive(), &num_children, &unique_id, &node_type); ASSERT_EQ(S_OK, hr); EXPECT_EQ(L"body", std::wstring(node_name, node_name.Length())); EXPECT_EQ(NODETYPE_ELEMENT, node_type); EXPECT_EQ(1, num_children); node_name.Reset(); node_value.Reset(); base::win::ScopedComPtr checkbox_isimpledomnode; hr = body_isimpledomnode->get_firstChild( checkbox_isimpledomnode.Receive()); ASSERT_EQ(S_OK, hr); hr = checkbox_isimpledomnode->get_nodeInfo( node_name.Receive(), &name_space_id, node_value.Receive(), &num_children, &unique_id, &node_type); ASSERT_EQ(S_OK, hr); EXPECT_EQ(L"input", std::wstring(node_name, node_name.Length())); EXPECT_EQ(NODETYPE_ELEMENT, node_type); EXPECT_EQ(0, num_children); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestRoleGroup) { LoadInitialAccessibilityTreeFromHtml( "
"); // Check the browser's copy of the renderer accessibility tree. AccessibleChecker grouping1_checker(std::wstring(), ROLE_SYSTEM_GROUPING, std::wstring()); AccessibleChecker grouping2_checker(std::wstring(), ROLE_SYSTEM_GROUPING, std::wstring()); AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, std::wstring()); document_checker.AppendExpectedChild(&grouping1_checker); document_checker.AppendExpectedChild(&grouping2_checker); document_checker.CheckAccessible(GetRendererAccessible()); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestSetCaretOffset) { base::win::ScopedComPtr input_text; SetUpInputField(&input_text); LONG caret_offset = 0; HRESULT hr = input_text->get_caretOffset(&caret_offset); EXPECT_EQ(S_OK, hr); EXPECT_EQ(CONTENTS_LENGTH - 1, caret_offset); AccessibilityNotificationWaiter waiter( shell(), AccessibilityModeComplete, ui::AX_EVENT_TEXT_SELECTION_CHANGED); caret_offset = 0; hr = input_text->setCaretOffset(caret_offset); EXPECT_EQ(S_OK, hr); waiter.WaitForNotification(); hr = input_text->get_caretOffset(&caret_offset); EXPECT_EQ(S_OK, hr); EXPECT_EQ(0, caret_offset); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestMultiLineSetCaretOffset) { base::win::ScopedComPtr textarea_text; SetUpTextareaField(&textarea_text); LONG caret_offset = 0; HRESULT hr = textarea_text->get_caretOffset(&caret_offset); EXPECT_EQ(S_OK, hr); EXPECT_EQ(CONTENTS_LENGTH - 1, caret_offset); AccessibilityNotificationWaiter waiter( shell(), AccessibilityModeComplete, ui::AX_EVENT_TEXT_SELECTION_CHANGED); caret_offset = 0; hr = textarea_text->setCaretOffset(caret_offset); EXPECT_EQ(S_OK, hr); waiter.WaitForNotification(); hr = textarea_text->get_caretOffset(&caret_offset); EXPECT_EQ(S_OK, hr); EXPECT_EQ(0, caret_offset); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestTextAtOffsetWithInvalidArgs) { base::win::ScopedComPtr input_text; SetUpInputField(&input_text); HRESULT hr = input_text->get_textAtOffset( 0, IA2_TEXT_BOUNDARY_CHAR, NULL, NULL, NULL); EXPECT_EQ(E_INVALIDARG, hr); // Test invalid offset. LONG start_offset = 0; LONG end_offset = 0; base::win::ScopedBstr text; LONG invalid_offset = -5; hr = input_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_CHAR, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(E_INVALIDARG, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); invalid_offset = CONTENTS_LENGTH + 1; hr = input_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_WORD, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(E_INVALIDARG, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); // According to the IA2 Spec, only line boundaries should succeed when // the offset is one past the end of the text. invalid_offset = CONTENTS_LENGTH; hr = input_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_CHAR, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); hr = input_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_WORD, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); hr = input_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); hr = input_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_ALL, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); // The same behavior should be observed when the special offset // IA2_TEXT_OFFSET_LENGTH is used. hr = input_text->get_textAtOffset( IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_CHAR, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); hr = input_text->get_textAtOffset( IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_WORD, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); hr = input_text->get_textAtOffset( IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); hr = input_text->get_textAtOffset( IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_ALL, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestMultiLineTextAtOffsetWithInvalidArgs) { base::win::ScopedComPtr textarea_text; SetUpTextareaField(&textarea_text); HRESULT hr = textarea_text->get_textAtOffset( 0, IA2_TEXT_BOUNDARY_CHAR, NULL, NULL, NULL); EXPECT_EQ(E_INVALIDARG, hr); // Test invalid offset. LONG start_offset = 0; LONG end_offset = 0; base::win::ScopedBstr text; LONG invalid_offset = -5; hr = textarea_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_CHAR, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(E_INVALIDARG, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); invalid_offset = CONTENTS_LENGTH + 1; hr = textarea_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_WORD, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(E_INVALIDARG, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); // According to the IA2 Spec, only line boundaries should succeed when // the offset is one past the end of the text. invalid_offset = CONTENTS_LENGTH; hr = textarea_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_CHAR, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); hr = textarea_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_WORD, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); hr = textarea_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); hr = textarea_text->get_textAtOffset( invalid_offset, IA2_TEXT_BOUNDARY_ALL, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); // The same behavior should be observed when the special offset // IA2_TEXT_OFFSET_LENGTH is used. hr = textarea_text->get_textAtOffset( IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_CHAR, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); hr = textarea_text->get_textAtOffset( IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_WORD, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); hr = textarea_text->get_textAtOffset( IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); hr = textarea_text->get_textAtOffset( IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_ALL, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); EXPECT_EQ(0, start_offset); EXPECT_EQ(0, end_offset); EXPECT_EQ(nullptr, static_cast(text)); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestTextAtOffsetWithBoundaryCharacter) { base::win::ScopedComPtr input_text; SetUpInputField(&input_text); for (LONG offset = 0; offset < CONTENTS_LENGTH; ++offset) { std::wstring expected_text(1, INPUT_CONTENTS[offset]); LONG expected_start_offset = offset; LONG expected_end_offset = offset + 1; CheckTextAtOffset(input_text, offset, IA2_TEXT_BOUNDARY_CHAR, expected_start_offset, expected_end_offset, expected_text); } for (LONG offset = CONTENTS_LENGTH - 1; offset >= 0; --offset) { std::wstring expected_text(1, INPUT_CONTENTS[offset]); LONG expected_start_offset = offset; LONG expected_end_offset = offset + 1; CheckTextAtOffset(input_text, offset, IA2_TEXT_BOUNDARY_CHAR, expected_start_offset, expected_end_offset, expected_text); } CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, IA2_TEXT_BOUNDARY_CHAR, CONTENTS_LENGTH - 1, CONTENTS_LENGTH, L"."); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestMultiLineTextAtOffsetWithBoundaryCharacter) { base::win::ScopedComPtr textarea_text; SetUpTextareaField(&textarea_text); for (LONG offset = 0; offset < CONTENTS_LENGTH; ++offset) { std::wstring expected_text(1, TEXTAREA_CONTENTS[offset]); LONG expected_start_offset = offset; LONG expected_end_offset = offset + 1; CheckTextAtOffset(textarea_text, offset, IA2_TEXT_BOUNDARY_CHAR, expected_start_offset, expected_end_offset, expected_text); } for (LONG offset = CONTENTS_LENGTH - 1; offset >= 0; --offset) { std::wstring expected_text(1, TEXTAREA_CONTENTS[offset]); LONG expected_start_offset = offset; LONG expected_end_offset = offset + 1; CheckTextAtOffset(textarea_text, offset, IA2_TEXT_BOUNDARY_CHAR, expected_start_offset, expected_end_offset, expected_text); } CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET, IA2_TEXT_BOUNDARY_CHAR, CONTENTS_LENGTH - 1, CONTENTS_LENGTH, L"."); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestTextAtOffsetWithBoundaryWord) { base::win::ScopedComPtr input_text; SetUpInputField(&input_text); // Trailing punctuation should be included as part of the previous word. CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_WORD, 0, 4, L"Moz/"); CheckTextAtOffset(input_text, 2, IA2_TEXT_BOUNDARY_WORD, 0, 4, L"Moz/"); // If the offset is at the punctuation, it should return // the previous word. CheckTextAtOffset(input_text, 3, IA2_TEXT_BOUNDARY_WORD, 0, 4, L"Moz/"); // Numbers with a decimal point ("." for U.S), should be treated as one word. // Also, trailing punctuation that occurs after empty space should be part of // the word. ("5.0 (" and not "5.0 ".) CheckTextAtOffset(input_text, 4, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); CheckTextAtOffset(input_text, 5, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); CheckTextAtOffset(input_text, 6, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); CheckTextAtOffset(input_text, 7, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); // Leading punctuation should not be included with the word after it. CheckTextAtOffset(input_text, 8, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); CheckTextAtOffset(input_text, 11, IA2_TEXT_BOUNDARY_WORD, 9, 12, L"ST "); // Numbers separated from letters with trailing punctuation should // be split into two words. Same for abreviations like "i.e.". CheckTextAtOffset(input_text, 12, IA2_TEXT_BOUNDARY_WORD, 12, 14, L"6."); CheckTextAtOffset(input_text, 15, IA2_TEXT_BOUNDARY_WORD, 14, 17, L"x; "); // Words with numbers should be treated like ordinary words. CheckTextAtOffset(input_text, 17, IA2_TEXT_BOUNDARY_WORD, 17, 24, L"WWW33) "); CheckTextAtOffset(input_text, 23, IA2_TEXT_BOUNDARY_WORD, 17, 24, L"WWW33) "); // Multiple trailing empty spaces should be part of the word preceding it. CheckTextAtOffset(input_text, 28, IA2_TEXT_BOUNDARY_WORD, 24, 33, L"WebKit \""); CheckTextAtOffset(input_text, 31, IA2_TEXT_BOUNDARY_WORD, 24, 33, L"WebKit \""); CheckTextAtOffset(input_text, 32, IA2_TEXT_BOUNDARY_WORD, 24, 33, L"WebKit \""); // Leading punctuation such as quotation marks should not be part of the word. CheckTextAtOffset(input_text, 33, IA2_TEXT_BOUNDARY_WORD, 33, 40, L"KHTML, "); CheckTextAtOffset(input_text, 38, IA2_TEXT_BOUNDARY_WORD, 33, 40, L"KHTML, "); // Trailing final punctuation should be part of the last word. CheckTextAtOffset(input_text, 41, IA2_TEXT_BOUNDARY_WORD, 40, CONTENTS_LENGTH, L"like\"."); CheckTextAtOffset(input_text, 45, IA2_TEXT_BOUNDARY_WORD, 40, CONTENTS_LENGTH, L"like\"."); // Test special offsets. CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, IA2_TEXT_BOUNDARY_WORD, 40, CONTENTS_LENGTH, L"like\"."); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestMultiLineTextAtOffsetWithBoundaryWord) { base::win::ScopedComPtr textarea_text; SetUpTextareaField(&textarea_text); // Trailing punctuation should be included as part of the previous word. CheckTextAtOffset(textarea_text, 0, IA2_TEXT_BOUNDARY_WORD, 0, 4, L"Moz/"); CheckTextAtOffset(textarea_text, 2, IA2_TEXT_BOUNDARY_WORD, 0, 4, L"Moz/"); // If the offset is at the punctuation, it should return // the previous word. CheckTextAtOffset(textarea_text, 3, IA2_TEXT_BOUNDARY_WORD, 0, 4, L"Moz/"); // Numbers with a decimal point ("." for U.S), should be treated as one word. // Also, trailing punctuation that occurs after empty space should be part of // the word. ("5.0 (" and not "5.0 ".) CheckTextAtOffset(textarea_text, 4, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); CheckTextAtOffset(textarea_text, 5, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); CheckTextAtOffset(textarea_text, 6, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); CheckTextAtOffset(textarea_text, 7, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); // Leading punctuation should not be included with the word after it. CheckTextAtOffset(textarea_text, 8, IA2_TEXT_BOUNDARY_WORD, 4, 9, L"5.0 ("); CheckTextAtOffset(textarea_text, 11, IA2_TEXT_BOUNDARY_WORD, 9, 12, L"ST "); // Numbers separated from letters with trailing punctuation should // be split into two words. Same for abreviations like "i.e.". CheckTextAtOffset(textarea_text, 12, IA2_TEXT_BOUNDARY_WORD, 12, 14, L"6."); CheckTextAtOffset(textarea_text, 15, IA2_TEXT_BOUNDARY_WORD, 14, 17, L"x; "); // Words with numbers should be treated like ordinary words. CheckTextAtOffset(textarea_text, 17, IA2_TEXT_BOUNDARY_WORD, 17, 24, L"WWW33)\n"); CheckTextAtOffset(textarea_text, 23, IA2_TEXT_BOUNDARY_WORD, 17, 24, L"WWW33)\n"); // Multiple trailing empty spaces should be part of the word preceding it. CheckTextAtOffset(textarea_text, 28, IA2_TEXT_BOUNDARY_WORD, 24, 33, L"WebKit \n\""); CheckTextAtOffset(textarea_text, 31, IA2_TEXT_BOUNDARY_WORD, 24, 33, L"WebKit \n\""); CheckTextAtOffset(textarea_text, 32, IA2_TEXT_BOUNDARY_WORD, 24, 33, L"WebKit \n\""); // Leading punctuation such as quotation marks should not be part of the word. CheckTextAtOffset(textarea_text, 33, IA2_TEXT_BOUNDARY_WORD, 33, 40, L"KHTML, "); CheckTextAtOffset(textarea_text, 38, IA2_TEXT_BOUNDARY_WORD, 33, 40, L"KHTML, "); // Trailing final punctuation should be part of the last word. CheckTextAtOffset(textarea_text, 41, IA2_TEXT_BOUNDARY_WORD, 40, CONTENTS_LENGTH, L"like\"."); CheckTextAtOffset(textarea_text, 45, IA2_TEXT_BOUNDARY_WORD, 40, CONTENTS_LENGTH, L"like\"."); // Test special offsets. CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET, IA2_TEXT_BOUNDARY_WORD, 40, CONTENTS_LENGTH, L"like\"."); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestTextAtOffsetWithBoundarySentence) { base::win::ScopedComPtr input_text; SetUpInputField(&input_text); // Sentence navigation is not currently implemented. LONG start_offset = 0; LONG end_offset = 0; base::win::ScopedBstr text; HRESULT hr = input_text->get_textAtOffset( 5, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestMultiLineTextAtOffsetWithBoundarySentence) { base::win::ScopedComPtr textarea_text; SetUpTextareaField(&textarea_text); // Sentence navigation is not currently implemented. LONG start_offset = 0; LONG end_offset = 0; base::win::ScopedBstr text; HRESULT hr = textarea_text->get_textAtOffset( 25, IA2_TEXT_BOUNDARY_SENTENCE, &start_offset, &end_offset, text.Receive()); EXPECT_EQ(S_FALSE, hr); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestTextAtOffsetWithBoundaryLine) { base::win::ScopedComPtr input_text; SetUpInputField(&input_text); // Single line text fields should return the whole text. CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_LINE, 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS)); // Test special offsets. CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_LINE, 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS)); CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, IA2_TEXT_BOUNDARY_LINE, 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS)); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestMultiLineTextAtOffsetWithBoundaryLine) { base::win::ScopedComPtr textarea_text; SetUpTextareaField(&textarea_text); CheckTextAtOffset(textarea_text, 0, IA2_TEXT_BOUNDARY_LINE, 0, 24, L"Moz/5.0 (ST 6.x; WWW33)\n"); // If the offset is at the newline, return the line preceding it. CheckTextAtOffset(textarea_text, 31, IA2_TEXT_BOUNDARY_LINE, 24, 32, L"WebKit \n"); // Last line does not have a trailing newline. CheckTextAtOffset(textarea_text, 32, IA2_TEXT_BOUNDARY_LINE, 32, CONTENTS_LENGTH, L"\"KHTML, like\"."); // An offset one past the last character should return the last line. CheckTextAtOffset(textarea_text, CONTENTS_LENGTH, IA2_TEXT_BOUNDARY_LINE, 32, CONTENTS_LENGTH, L"\"KHTML, like\"."); // Test special offsets. CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_LINE, 32, CONTENTS_LENGTH, L"\"KHTML, like\"."); CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET, IA2_TEXT_BOUNDARY_LINE, 32, CONTENTS_LENGTH, L"\"KHTML, like\"."); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestTextAtOffsetWithBoundaryAll) { base::win::ScopedComPtr input_text; SetUpInputField(&input_text); CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_ALL, 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS)); } IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestMultiLineTextAtOffsetWithBoundaryAll) { base::win::ScopedComPtr textarea_text; SetUpTextareaField(&textarea_text); CheckTextAtOffset(textarea_text, CONTENTS_LENGTH - 1, IA2_TEXT_BOUNDARY_ALL, 0, CONTENTS_LENGTH, base::SysUTF8ToWide(TEXTAREA_CONTENTS)); } } // namespace content