Index: content/browser/accessibility/accessibility_win_browsertest.cc |
diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc |
index 058e033ae4e4fb959d92411fde0d05f5f6440969..c96a930b9480fe91555f85d467a6adf6d6d01dc7 100644 |
--- a/content/browser/accessibility/accessibility_win_browsertest.cc |
+++ b/content/browser/accessibility/accessibility_win_browsertest.cc |
@@ -31,11 +31,66 @@ namespace content { |
namespace { |
-// Helpers -------------------------------------------------------------------- |
+// AccessibilityWinBrowserTest ------------------------------------------------ |
+ |
+class AccessibilityWinBrowserTest : public ContentBrowserTest { |
+ public: |
+ AccessibilityWinBrowserTest(); |
+ virtual ~AccessibilityWinBrowserTest(); |
+ |
+ protected: |
+ void LoadInitialAccessibilityTreeFromHtml(const std::string& html); |
+ IAccessible* GetRendererAccessible(); |
+ void ExecuteScript(const std::wstring& script); |
-base::win::ScopedComPtr<IAccessible> GetAccessibleFromResultVariant( |
- IAccessible* parent, |
- VARIANT* var) { |
+ static base::win::ScopedComPtr<IAccessible> |
+ AccessibilityWinBrowserTest::GetAccessibleFromResultVariant( |
+ IAccessible* parent, VARIANT* var); |
+ static HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2); |
dmazzoni
2014/11/03 16:26:49
nit: wrap to 80 chars
|
+ static void RecursiveFindNodeInAccessibilityTree(IAccessible* node, |
+ int32 expected_role, const std::wstring& expected_name, |
+ int32 depth, bool* found); |
+ static void CheckTextAtOffset(IAccessible2 element, |
+ LONG offset, IA2TextBoundaryType boundary_type, |
+ LONG expected_start_offset, LONG expected_end_offset, |
+ std::wstring& expected_text); |
+ |
+ 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); |
+} |
+ |
+// Static helpers ------------------------------------------------ |
+ |
+base::win::ScopedComPtr<IAccessible> |
+ AccessibilityWinBrowserTest::GetAccessibleFromResultVariant( |
+ IAccessible* parent, VARIANT* var) { |
base::win::ScopedComPtr<IAccessible> ptr; |
switch (V_VT(var)) { |
case VT_DISPATCH: { |
@@ -57,9 +112,9 @@ base::win::ScopedComPtr<IAccessible> GetAccessibleFromResultVariant( |
return ptr; |
} |
-HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) { |
- // TODO(ctguil): For some reason querying the IAccessible2 interface from |
- // IAccessible fails. |
+HRESULT AccessibilityWinBrowserTest::QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) { |
+ // IA2 Spec dictates that IServiceProvider should be used instead of |
+ // QueryInterface when retrieving IAccessible2. |
base::win::ScopedComPtr<IServiceProvider> service_provider; |
HRESULT hr = accessible->QueryInterface(service_provider.Receive()); |
return SUCCEEDED(hr) ? |
@@ -69,11 +124,9 @@ HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) { |
// 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 RecursiveFindNodeInAccessibilityTree(IAccessible* node, |
- int32 expected_role, |
- const std::wstring& expected_name, |
- int32 depth, |
- bool* found) { |
+void AccessibilityWinBrowserTest::RecursiveFindNodeInAccessibilityTree( |
+ 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()); |
@@ -119,48 +172,26 @@ void RecursiveFindNodeInAccessibilityTree(IAccessible* node, |
} |
} |
- |
-// AccessibilityWinBrowserTest ------------------------------------------------ |
- |
-class AccessibilityWinBrowserTest : public ContentBrowserTest { |
- public: |
- AccessibilityWinBrowserTest(); |
- virtual ~AccessibilityWinBrowserTest(); |
- |
- protected: |
- void LoadInitialAccessibilityTreeFromHtml(const std::string& html); |
- IAccessible* GetRendererAccessible(); |
- void ExecuteScript(const std::wstring& script); |
- |
- 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); |
+// Ensures that the text, the start and end offsets retrieved using |
+// get_textAtOffset match the expected values. |
+void AccessibilityWinBrowserTest::CheckTextAtOffset(IAccessible2 element, |
+ LONG offset, IA2TextBoundaryType boundary_type, |
+ LONG expected_start_offset, LONG expected_end_offset, |
+ std::wstring& expected_text) { |
+ const std::wstring message = L"while checking for " + expected_text + |
+ L" at " + expected_start_offset + L'-' + expected_end_offset; |
dmazzoni
2014/11/03 16:26:49
I don't think you can append integers to strings i
|
+ 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())); |
} |
@@ -753,4 +784,171 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestRoleGroup) { |
document_checker.CheckAccessible(GetRendererAccessible()); |
} |
+IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestTextAtOffset) { |
+ const char input_contents[] = "Mozilla/5.0 (NT 6.x; WOW64) " |
+ "WebKit (KHTML, like)."; |
+ const char textarea_contents[] = "Mozilla/5.0 (NT 6.x; WOW64)\n" |
+ "WebKit \n(KHTML, like)."; |
+ LONG contents_length = sizeof(input_contents) / sizeof(char); |
+ LoadInitialAccessibilityTreeFromHtml("<!DOCTYPE html>" |
+ "<html><body><form>" |
+ "<label for='name'>Browser name:</label>" |
+ "<input type='text' id='name' name='name' value='" |
+ "Mozilla/5.0 (NT 6.x; WOW64) WebKit " |
+ "(KHTML, like).'>" |
+ "</form><textarea rows='3' cols='60'>" |
+ "Mozilla/5.0 (NT 6.x; WOW64)<br>" |
+ "WebKit <br>(KHTML, like)." |
+ "</textarea></body></html>"); |
+ |
+ // Retrieve the IAccessible interface for the web page. |
+ base::win::ScopedComPtr<IAccessible> document = GetRendererAccessible(); |
+ LONG child_count = 0; |
+ HRESULT hr = document->get_accChildCount(&child_count); |
+ ASSERT_EQ(S_OK, hr); |
+ ASSERT_EQ(2, child_count); |
+ // Get all the document's children. |
+ scoped_ptr<VARIANT[]> children(new VARIANT[child_count]); |
+ LONG obtained_count = 0; |
+ hr = AccessibleChildren( |
+ document, 0, child_count, children.get(), &obtained_count); |
+ ASSERT_EQ(S_OK, hr); |
+ ASSERT_EQ(child_count, obtained_count); |
+ |
+ // Find the two edit fields (simple and text area). |
+ base::win::ScopedComPtr<IAccessible2> input; |
+ hr = QueryIAccessible2(GetAccessibleFromResultVariant( |
+ document, children.get()[0]), input.Receive()); |
+ ASSERT_EQ(S_OK, hr); |
+ base::win::ScopedComPtr<IAccessible2> textarea; |
+ hr = QueryIAccessible2(GetAccessibleFromResultVariant( |
+ document, children.get()[1]), textarea.Receive()); |
+ ASSERT_EQ(S_OK, hr); |
+ |
+ // Retrieve the IAccessibleText interface for both of the fields. |
+ base::win::ScopedComPtr<IAccessibleText> input_text; |
+ hr = input->QueryInterface(input_text.Receive()); |
+ ASSERT_EQ(S_OK, hr); |
+ base::win::ScopedComPtr<IAccessibleText> textarea_text; |
+ hr = textarea->QueryInterface(textarea_text.Receive()); |
+ ASSERT_EQ(S_OK, hr); |
+ |
+ // Test invalid arguments. |
+ hr = input_text->get_textAtOffset( |
+ 0, IA2_TEXT_BOUNDARY_CHAR, NULL, NULL, NULL); |
+ EXPECT_EQ(E_INVALIDARG, hr); |
+ hr = textarea_text->get_textAtOffset( |
+ 0, IA2_TEXT_BOUNDARY_CHAR, NULL, NULL, NULL); |
+ EXPECT_EQ(E_INVALIDARG, hr); |
+ |
+ // Test invalid offset. |
+ LONG invalid_offset = -5; |
+ LONG start_offset = 0; |
+ LONG end_offset = 0; |
+ base::win::ScopedBstr text; |
+ hr = input_text->get_textAtOffset( |
+ invalid_offset, IA2_TEXT_BOUNDARY_CHAR, |
+ &start_offset, &end_offset, text.Receive()); |
+ EXPECT_EQ(E_FAIL, hr); |
+ hr = textarea_text->get_textAtOffset( |
+ invalid_offset, IA2_TEXT_BOUNDARY_CHAR, |
+ &start_offset, &end_offset, text.Receive()); |
+ EXPECT_EQ(E_FAIL, hr); |
+ invalid_offset = contents_length; |
+ hr = input_text->get_textAtOffset( |
+ invalid_offset, IA2_TEXT_BOUNDARY_CHAR, |
+ &start_offset, &end_offset, text.Receive()); |
+ EXPECT_EQ(E_FAIL, hr); |
+ hr = textarea_text->get_textAtOffset( |
+ invalid_offset, IA2_TEXT_BOUNDARY_CHAR, |
+ &start_offset, &end_offset, text.Receive()); |
+ EXPECT_EQ(E_FAIL, hr); |
+ |
+ // Test character navigation. |
+ for (LONG offset = 0; offset < contents_length; ++offset) { |
+ std::wstring expected_text(input_contents[offset], 1); |
+ 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(textarea_contents[offset], 1); |
+ 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); |
+ } |
+ |
+ // Test word navigation. |
dmazzoni
2014/11/03 16:26:49
Please add tests for passing IA2_TEXT_OFFSET_LENGT
|
+ // (Imitate Firefox behavior.) |
+ // Trailing punctuation should be included as part of the previous word. |
+ CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_WORD, |
+ 0, 8, L"Mozilla/"); |
+ CheckTextAtOffset(textarea_text, 6, IA2_TEXT_BOUNDARY_WORD, |
+ 0, 8, L"Mozilla/"); |
+ // If the offset is at the punctuation, it should return |
+ // the previous word. |
+ CheckTextAtOffset(textarea_text, 7, IA2_TEXT_BOUNDARY_WORD, |
+ 0, 8, L"Mozilla/"); |
+ // Numbers with a decimal point (U.S), should be treated as two words. |
+ CheckTextAtOffset(input_text, 8, IA2_TEXT_BOUNDARY_WORD, |
+ 8, 10, L"5."); |
+ CheckTextAtOffset(textarea_text, 9, IA2_TEXT_BOUNDARY_WORD, |
+ 8, 10, L"5."); |
+ // Also, trailing punctuation that occurs after empty space should not be |
+ // part of the word. ("0 " and not "0 (".) |
+ CheckTextAtOffset(input_text, 10, IA2_TEXT_BOUNDARY_WORD, |
+ 10, 12, L"0 "); |
+ CheckTextAtOffset(textarea_text, 11, IA2_TEXT_BOUNDARY_WORD, |
+ 10, 12, L"0 "); |
+ // Leading punctuation should be included with the word after it. |
+ CheckTextAtOffset(input_text, 12, IA2_TEXT_BOUNDARY_WORD, |
+ 12, 16, L"(NT "); |
+ CheckTextAtOffset(textarea_text, 15, IA2_TEXT_BOUNDARY_WORD, |
+ 12, 16, L"(NT "); |
+ // Numbers separated from letters with trailing punctuation should |
+ // behave like decimal numbers above. |
+ CheckTextAtOffset(input_text, 16, IA2_TEXT_BOUNDARY_WORD, |
+ 16, 18, L"6."); |
+ CheckTextAtOffset(textarea_text, 19, IA2_TEXT_BOUNDARY_WORD, |
+ 18, 21, L"x; "); |
+ // Words with numbers should be treated like ordinary words. |
+ CheckTextAtOffset(input_text, 24, IA2_TEXT_BOUNDARY_WORD, |
+ 21, 28, L"WOW64) "); |
+ CheckTextAtOffset(textarea_text, 26, IA2_TEXT_BOUNDARY_WORD, |
+ 21, 28, L"WOW64)\n"); |
+ // Multiple trailing empty spaces should be part of the word preceding it. |
+ CheckTextAtOffset(input_text, 30, IA2_TEXT_BOUNDARY_WORD, |
+ 28, 36, L"WebKit "); |
+ CheckTextAtOffset(textarea_text, 35, IA2_TEXT_BOUNDARY_WORD, |
+ 28, 36, L"WebKit \n"); |
+ |
+ // Test sentence navigation (not currently implemented). |
+ hr = input_text->get_textAtOffset( |
+ 5, IA2_TEXT_BOUNDARY_SENTENCE, |
+ &start_offset, &end_offset, text.Receive()); |
+ EXPECT_EQ(S_FALSE, hr); |
+ hr = textarea_text->get_textAtOffset( |
+ 25, IA2_TEXT_BOUNDARY_CHAR, |
+ &start_offset, &end_offset, text.Receive()); |
+ EXPECT_EQ(S_FALSE, hr); |
+ |
+ // Test line navigation. |
+ CheckTextAtOffset(textarea_text, 0, IA2_TEXT_BOUNDARY_LINE, |
+ 0, 28, L"Mozilla/5.0 (NT 6.x; WOW64)\n"); |
+ // If the offset is at the newline, return the line preceding it. |
+ CheckTextAtOffset(textarea_text, 35, IA2_TEXT_BOUNDARY_LINE, |
+ 28, 36, L"WebKit \n"); |
+ // Last line does not have a trailing newline. |
+ CheckTextAtOffset(textarea_text, 36, IA2_TEXT_BOUNDARY_LINE, |
+ 36, contents_length, L"(KHTML, like)."); |
+ |
+ // Return the whole of the text. |
+ CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_ALL, |
+ 0, contents_length, std::string(input_contents, contents_length)); |
+ CheckTextAtOffset(textarea_text, contents_length - 1, IA2_TEXT_BOUNDARY_ALL, |
+ 0, contents_length, std::string(textarea_contents, contents_length)); |
+} |
+ |
} // namespace content |