OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <vector> | 5 #include <vector> |
6 | 6 |
7 #include "base/memory/scoped_ptr.h" | 7 #include "base/memory/scoped_ptr.h" |
8 #include "base/strings/utf_string_conversions.h" | 8 #include "base/strings/utf_string_conversions.h" |
9 #include "base/win/scoped_bstr.h" | 9 #include "base/win/scoped_bstr.h" |
10 #include "base/win/scoped_comptr.h" | 10 #include "base/win/scoped_comptr.h" |
(...skipping 13 matching lines...) Expand all Loading... | |
24 #include "content/test/accessibility_browser_test_utils.h" | 24 #include "content/test/accessibility_browser_test_utils.h" |
25 #include "third_party/iaccessible2/ia2_api_all.h" | 25 #include "third_party/iaccessible2/ia2_api_all.h" |
26 #include "third_party/isimpledom/ISimpleDOMNode.h" | 26 #include "third_party/isimpledom/ISimpleDOMNode.h" |
27 #include "ui/aura/window.h" | 27 #include "ui/aura/window.h" |
28 #include "ui/aura/window_tree_host.h" | 28 #include "ui/aura/window_tree_host.h" |
29 | 29 |
30 namespace content { | 30 namespace content { |
31 | 31 |
32 namespace { | 32 namespace { |
33 | 33 |
34 // Helpers -------------------------------------------------------------------- | 34 class AccessibleChecker; |
35 | 35 |
36 base::win::ScopedComPtr<IAccessible> GetAccessibleFromResultVariant( | 36 |
37 IAccessible* parent, | 37 // AccessibilityWinBrowserTest ------------------------------------------------ |
38 VARIANT* var) { | 38 |
39 class AccessibilityWinBrowserTest : public ContentBrowserTest { | |
40 public: | |
41 AccessibilityWinBrowserTest(); | |
42 virtual ~AccessibilityWinBrowserTest(); | |
43 | |
44 protected: | |
45 void LoadInitialAccessibilityTreeFromHtml(const std::string& html); | |
46 IAccessible* GetRendererAccessible(); | |
47 void ExecuteScript(const std::wstring& script); | |
48 | |
49 static base::win::ScopedComPtr<IAccessible> | |
50 AccessibilityWinBrowserTest::GetAccessibleFromVariant( | |
51 IAccessible* parent, VARIANT* var); | |
52 static HRESULT QueryIAccessible2(IAccessible* accessible, | |
53 IAccessible2** accessible2); | |
dmazzoni
2014/11/06 06:53:30
nit: check indentation
| |
54 static void RecursiveFindNodeInAccessibilityTree(IAccessible* node, | |
55 int32 expected_role, const std::wstring& expected_name, | |
56 int32 depth, bool* found); | |
57 static void CheckTextAtOffset(IAccessibleText* element, | |
58 LONG offset, IA2TextBoundaryType boundary_type, | |
59 LONG expected_start_offset, LONG expected_end_offset, | |
60 const std::wstring& expected_text); | |
61 static std::vector<base::win::ScopedVariant> GetAllAccessibleChildren( | |
62 IAccessible* element); | |
63 | |
64 private: | |
65 friend class AccessibleChecker; | |
66 DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest); | |
67 }; | |
68 | |
69 AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() { | |
70 } | |
71 | |
72 AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() { | |
73 } | |
74 | |
75 void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml( | |
76 const std::string& html) { | |
77 AccessibilityNotificationWaiter waiter( | |
78 shell(), AccessibilityModeComplete, | |
79 ui::AX_EVENT_LOAD_COMPLETE); | |
80 GURL html_data_url("data:text/html," + html); | |
81 NavigateToURL(shell(), html_data_url); | |
82 waiter.WaitForNotification(); | |
83 } | |
84 | |
85 // Retrieve the MSAA client accessibility object for the Render Widget Host View | |
86 // of the selected tab. | |
87 IAccessible* AccessibilityWinBrowserTest::GetRendererAccessible() { | |
88 content::WebContents* web_contents = shell()->web_contents(); | |
89 return web_contents->GetRenderWidgetHostView()->GetNativeViewAccessible(); | |
90 } | |
91 | |
92 void AccessibilityWinBrowserTest::ExecuteScript(const std::wstring& script) { | |
93 shell()->web_contents()->GetMainFrame()->ExecuteJavaScript(script); | |
94 } | |
95 | |
96 // Static helpers ------------------------------------------------ | |
97 | |
98 base::win::ScopedComPtr<IAccessible> | |
99 AccessibilityWinBrowserTest::GetAccessibleFromVariant( | |
100 IAccessible* parent, VARIANT* var) { | |
39 base::win::ScopedComPtr<IAccessible> ptr; | 101 base::win::ScopedComPtr<IAccessible> ptr; |
40 switch (V_VT(var)) { | 102 switch (V_VT(var)) { |
41 case VT_DISPATCH: { | 103 case VT_DISPATCH: { |
42 IDispatch* dispatch = V_DISPATCH(var); | 104 IDispatch* dispatch = V_DISPATCH(var); |
43 if (dispatch) | 105 if (dispatch) |
44 ptr.QueryFrom(dispatch); | 106 ptr.QueryFrom(dispatch); |
45 break; | 107 break; |
46 } | 108 } |
47 | 109 |
48 case VT_I4: { | 110 case VT_I4: { |
49 base::win::ScopedComPtr<IDispatch> dispatch; | 111 base::win::ScopedComPtr<IDispatch> dispatch; |
50 HRESULT hr = parent->get_accChild(*var, dispatch.Receive()); | 112 HRESULT hr = parent->get_accChild(*var, dispatch.Receive()); |
51 EXPECT_TRUE(SUCCEEDED(hr)); | 113 EXPECT_TRUE(SUCCEEDED(hr)); |
52 if (dispatch) | 114 if (dispatch) |
53 dispatch.QueryInterface(ptr.Receive()); | 115 dispatch.QueryInterface(ptr.Receive()); |
54 break; | 116 break; |
55 } | 117 } |
56 } | 118 } |
57 return ptr; | 119 return ptr; |
58 } | 120 } |
59 | 121 |
60 HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) { | 122 HRESULT AccessibilityWinBrowserTest::QueryIAccessible2( |
61 // TODO(ctguil): For some reason querying the IAccessible2 interface from | 123 IAccessible* accessible, IAccessible2** accessible2) { |
dmazzoni
2014/11/06 06:53:30
I like naming output parameters with an "out_" pre
| |
62 // IAccessible fails. | 124 // IA2 Spec dictates that IServiceProvider should be used instead of |
125 // QueryInterface when retrieving IAccessible2. | |
63 base::win::ScopedComPtr<IServiceProvider> service_provider; | 126 base::win::ScopedComPtr<IServiceProvider> service_provider; |
64 HRESULT hr = accessible->QueryInterface(service_provider.Receive()); | 127 HRESULT hr = accessible->QueryInterface(service_provider.Receive()); |
65 return SUCCEEDED(hr) ? | 128 return SUCCEEDED(hr) ? |
66 service_provider->QueryService(IID_IAccessible2, accessible2) : hr; | 129 service_provider->QueryService(IID_IAccessible2, accessible2) : hr; |
67 } | 130 } |
68 | 131 |
69 // Recursively search through all of the descendants reachable from an | 132 // Recursively search through all of the descendants reachable from an |
70 // IAccessible node and return true if we find one with the given role | 133 // IAccessible node and return true if we find one with the given role |
71 // and name. | 134 // and name. |
72 void RecursiveFindNodeInAccessibilityTree(IAccessible* node, | 135 void AccessibilityWinBrowserTest::RecursiveFindNodeInAccessibilityTree( |
73 int32 expected_role, | 136 IAccessible* node, int32 expected_role, const std::wstring& expected_name, |
74 const std::wstring& expected_name, | 137 int32 depth, bool* found) { |
75 int32 depth, | |
76 bool* found) { | |
77 base::win::ScopedBstr name_bstr; | 138 base::win::ScopedBstr name_bstr; |
78 base::win::ScopedVariant childid_self(CHILDID_SELF); | 139 base::win::ScopedVariant childid_self(CHILDID_SELF); |
79 node->get_accName(childid_self, name_bstr.Receive()); | 140 node->get_accName(childid_self, name_bstr.Receive()); |
80 std::wstring name(name_bstr, name_bstr.Length()); | 141 std::wstring name(name_bstr, name_bstr.Length()); |
81 base::win::ScopedVariant role; | 142 base::win::ScopedVariant role; |
82 node->get_accRole(childid_self, role.Receive()); | 143 node->get_accRole(childid_self, role.Receive()); |
83 ASSERT_EQ(VT_I4, role.type()); | 144 ASSERT_EQ(VT_I4, role.type()); |
84 | 145 |
85 // Print the accessibility tree as we go, because if this test fails | 146 // Print the accessibility tree as we go, because if this test fails |
86 // on the bots, this is really helpful in figuring out why. | 147 // on the bots, this is really helpful in figuring out why. |
87 for (int i = 0; i < depth; i++) | 148 for (int i = 0; i < depth; i++) |
88 printf(" "); | 149 printf(" "); |
89 printf("role=%s name=%s\n", | 150 printf("role=%s name=%s\n", |
90 base::WideToUTF8(IAccessibleRoleToString(V_I4(&role))).c_str(), | 151 base::WideToUTF8(IAccessibleRoleToString(V_I4(&role))).c_str(), |
91 base::WideToUTF8(name).c_str()); | 152 base::WideToUTF8(name).c_str()); |
92 | 153 |
93 if (expected_role == V_I4(&role) && expected_name == name) { | 154 if (expected_role == V_I4(&role) && expected_name == name) { |
94 *found = true; | 155 *found = true; |
95 return; | 156 return; |
96 } | 157 } |
97 | 158 |
98 LONG child_count = 0; | 159 std::vector<base::win::ScopedVariant> children = GetAllAccessibleChildren( |
99 HRESULT hr = node->get_accChildCount(&child_count); | 160 node); |
100 ASSERT_EQ(S_OK, hr); | 161 for (size_t i = 0; i < children.size(); ++i) { |
101 | |
102 scoped_ptr<VARIANT[]> child_array(new VARIANT[child_count]); | |
103 LONG obtained_count = 0; | |
104 hr = AccessibleChildren( | |
105 node, 0, child_count, child_array.get(), &obtained_count); | |
106 ASSERT_EQ(S_OK, hr); | |
107 ASSERT_EQ(child_count, obtained_count); | |
108 | |
109 for (int index = 0; index < obtained_count; index++) { | |
110 base::win::ScopedComPtr<IAccessible> child_accessible( | 162 base::win::ScopedComPtr<IAccessible> child_accessible( |
111 GetAccessibleFromResultVariant(node, &child_array.get()[index])); | 163 GetAccessibleFromVariant(node, children[i].AsInput())); |
112 if (child_accessible) { | 164 if (child_accessible) { |
113 RecursiveFindNodeInAccessibilityTree( | 165 RecursiveFindNodeInAccessibilityTree( |
114 child_accessible.get(), expected_role, expected_name, depth + 1, | 166 child_accessible.get(), expected_role, expected_name, depth + 1, |
115 found); | 167 found); |
116 if (*found) | 168 if (*found) |
117 return; | 169 return; |
118 } | 170 } |
119 } | 171 } |
120 } | 172 } |
121 | 173 |
174 // Ensures that the text and the start and end offsets retrieved using | |
175 // get_textAtOffset match the expected values. | |
176 void AccessibilityWinBrowserTest::CheckTextAtOffset(IAccessibleText* element, | |
177 LONG offset, IA2TextBoundaryType boundary_type, | |
178 LONG expected_start_offset, LONG expected_end_offset, | |
179 const std::wstring& expected_text) { | |
180 testing::Message message; | |
181 message << "while checking for " << expected_text << " at " << | |
182 expected_start_offset << '-' << expected_end_offset; | |
183 SCOPED_TRACE(message); | |
122 | 184 |
123 // AccessibilityWinBrowserTest ------------------------------------------------ | 185 LONG start_offset = 0; |
124 | 186 LONG end_offset = 0; |
125 class AccessibilityWinBrowserTest : public ContentBrowserTest { | 187 base::win::ScopedBstr text; |
126 public: | 188 HRESULT hr = element->get_textAtOffset( |
127 AccessibilityWinBrowserTest(); | 189 offset, boundary_type, |
128 virtual ~AccessibilityWinBrowserTest(); | 190 &start_offset, &end_offset, text.Receive()); |
129 | 191 EXPECT_EQ(S_OK, hr); |
130 protected: | 192 EXPECT_EQ(expected_start_offset, start_offset); |
131 void LoadInitialAccessibilityTreeFromHtml(const std::string& html); | 193 EXPECT_EQ(expected_end_offset, end_offset); |
132 IAccessible* GetRendererAccessible(); | 194 EXPECT_EQ(expected_text, std::wstring(text, text.Length())); |
133 void ExecuteScript(const std::wstring& script); | |
134 | |
135 private: | |
136 DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest); | |
137 }; | |
138 | |
139 AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() { | |
140 } | 195 } |
141 | 196 |
142 AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() { | 197 std::vector<base::win::ScopedVariant> |
143 } | 198 AccessibilityWinBrowserTest::GetAllAccessibleChildren( |
199 IAccessible* element) { | |
200 LONG child_count = 0; | |
201 HRESULT hr = element->get_accChildCount(&child_count); | |
202 EXPECT_EQ(S_OK, hr); | |
203 if (child_count <= 0) | |
204 return std::vector<base::win::ScopedVariant>(); | |
144 | 205 |
145 void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml( | 206 scoped_ptr<VARIANT[]> children_array(new VARIANT[child_count]); |
146 const std::string& html) { | 207 LONG obtained_count = 0; |
147 AccessibilityNotificationWaiter waiter( | 208 hr = AccessibleChildren( |
148 shell(), AccessibilityModeComplete, | 209 element, 0, child_count, children_array.get(), &obtained_count); |
149 ui::AX_EVENT_LOAD_COMPLETE); | 210 EXPECT_EQ(S_OK, hr); |
150 GURL html_data_url("data:text/html," + html); | 211 EXPECT_EQ(child_count, obtained_count); |
151 NavigateToURL(shell(), html_data_url); | |
152 waiter.WaitForNotification(); | |
153 } | |
154 | 212 |
155 // Retrieve the MSAA client accessibility object for the Render Widget Host View | 213 std::vector<base::win::ScopedVariant> children( |
156 // of the selected tab. | 214 static_cast<size_t>(child_count)); |
157 IAccessible* AccessibilityWinBrowserTest::GetRendererAccessible() { | 215 for (size_t i = 0; i < children.size(); i++) { |
158 content::WebContents* web_contents = shell()->web_contents(); | 216 children[i].Reset(children_array[i]); |
159 return web_contents->GetRenderWidgetHostView()->GetNativeViewAccessible(); | 217 } |
160 } | 218 return children; |
161 | |
162 void AccessibilityWinBrowserTest::ExecuteScript(const std::wstring& script) { | |
163 shell()->web_contents()->GetMainFrame()->ExecuteJavaScript(script); | |
164 } | 219 } |
165 | 220 |
166 | 221 |
167 // AccessibleChecker ---------------------------------------------------------- | 222 // AccessibleChecker ---------------------------------------------------------- |
168 | 223 |
169 class AccessibleChecker { | 224 class AccessibleChecker { |
170 public: | 225 public: |
171 // This constructor can be used if the IA2 role will be the same as the MSAA | 226 // This constructor can be used if the IA2 role will be the same as the MSAA |
172 // role. | 227 // role. |
173 AccessibleChecker(const std::wstring& expected_name, | 228 AccessibleChecker(const std::wstring& expected_name, |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
304 base::win::ScopedVariant childid_self(CHILDID_SELF); | 359 base::win::ScopedVariant childid_self(CHILDID_SELF); |
305 HRESULT hr = accessible->get_accRole(childid_self, role.Receive()); | 360 HRESULT hr = accessible->get_accRole(childid_self, role.Receive()); |
306 ASSERT_EQ(S_OK, hr); | 361 ASSERT_EQ(S_OK, hr); |
307 EXPECT_EQ(0, role_.Compare(role)) | 362 EXPECT_EQ(0, role_.Compare(role)) |
308 << "Expected role: " << RoleVariantToString(role_) | 363 << "Expected role: " << RoleVariantToString(role_) |
309 << "\nGot role: " << RoleVariantToString(role); | 364 << "\nGot role: " << RoleVariantToString(role); |
310 } | 365 } |
311 | 366 |
312 void AccessibleChecker::CheckIA2Role(IAccessible* accessible) { | 367 void AccessibleChecker::CheckIA2Role(IAccessible* accessible) { |
313 base::win::ScopedComPtr<IAccessible2> accessible2; | 368 base::win::ScopedComPtr<IAccessible2> accessible2; |
314 HRESULT hr = QueryIAccessible2(accessible, accessible2.Receive()); | 369 HRESULT hr = AccessibilityWinBrowserTest::QueryIAccessible2( |
370 accessible, accessible2.Receive()); | |
315 ASSERT_EQ(S_OK, hr); | 371 ASSERT_EQ(S_OK, hr); |
316 long ia2_role = 0; | 372 long ia2_role = 0; |
317 hr = accessible2->role(&ia2_role); | 373 hr = accessible2->role(&ia2_role); |
318 ASSERT_EQ(S_OK, hr); | 374 ASSERT_EQ(S_OK, hr); |
319 EXPECT_EQ(ia2_role_, ia2_role) | 375 EXPECT_EQ(ia2_role_, ia2_role) |
320 << "Expected ia2 role: " << IAccessible2RoleToString(ia2_role_) | 376 << "Expected ia2 role: " << IAccessible2RoleToString(ia2_role_) |
321 << "\nGot ia2 role: " << IAccessible2RoleToString(ia2_role); | 377 << "\nGot ia2 role: " << IAccessible2RoleToString(ia2_role); |
322 } | 378 } |
323 | 379 |
324 void AccessibleChecker::CheckAccessibleValue(IAccessible* accessible) { | 380 void AccessibleChecker::CheckAccessibleValue(IAccessible* accessible) { |
(...skipping 29 matching lines...) Expand all Loading... | |
354 // Avoid flakiness. The "offscreen" state depends on whether the browser | 410 // Avoid flakiness. The "offscreen" state depends on whether the browser |
355 // window is frontmost or not, and "hottracked" depends on whether the | 411 // window is frontmost or not, and "hottracked" depends on whether the |
356 // mouse cursor happens to be over the element. | 412 // mouse cursor happens to be over the element. |
357 obj_state &= ~(STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_HOTTRACKED); | 413 obj_state &= ~(STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_HOTTRACKED); |
358 EXPECT_EQ(state_, obj_state) | 414 EXPECT_EQ(state_, obj_state) |
359 << "Expected state: " << IAccessibleStateToString(state_) | 415 << "Expected state: " << IAccessibleStateToString(state_) |
360 << "\nGot state: " << IAccessibleStateToString(obj_state); | 416 << "\nGot state: " << IAccessibleStateToString(obj_state); |
361 } | 417 } |
362 | 418 |
363 void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) { | 419 void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) { |
364 LONG child_count = 0; | 420 std::vector<base::win::ScopedVariant> obtained_children = |
365 HRESULT hr = parent->get_accChildCount(&child_count); | 421 AccessibilityWinBrowserTest::GetAllAccessibleChildren(parent); |
366 EXPECT_EQ(S_OK, hr); | 422 size_t child_count = obtained_children.size(); |
367 ASSERT_EQ(child_count, children_.size()); | 423 ASSERT_EQ(child_count, children_.size()); |
368 | 424 |
369 scoped_ptr<VARIANT[]> child_array(new VARIANT[child_count]); | 425 AccessibleCheckerVector::iterator child_checker; |
370 LONG obtained_count = 0; | 426 std::vector<base::win::ScopedVariant>::iterator child; |
371 hr = AccessibleChildren(parent, 0, child_count, | 427 for (child_checker = children_.begin(), |
372 child_array.get(), &obtained_count); | 428 child = obtained_children.begin(); |
dmazzoni
2014/11/06 06:53:30
I'd indent this 4 more spaces as a continuation of
| |
373 ASSERT_EQ(S_OK, hr); | 429 child_checker != children_.end() |
374 ASSERT_EQ(child_count, obtained_count); | 430 && child != obtained_children.end(); |
dmazzoni
2014/11/06 06:53:30
same here - indent 4 more.
| |
375 | |
376 VARIANT* child = child_array.get(); | |
377 for (AccessibleCheckerVector::iterator child_checker = children_.begin(); | |
378 child_checker != children_.end(); | |
379 ++child_checker, ++child) { | 431 ++child_checker, ++child) { |
380 base::win::ScopedComPtr<IAccessible> child_accessible( | 432 base::win::ScopedComPtr<IAccessible> child_accessible( |
381 GetAccessibleFromResultVariant(parent, child)); | 433 AccessibilityWinBrowserTest::GetAccessibleFromVariant(parent, |
dmazzoni
2014/11/06 06:53:30
It'd be more readable if you didn't need the Acces
| |
434 child->AsInput())); | |
dmazzoni
2014/11/06 06:53:30
indent 4 more
| |
382 ASSERT_TRUE(child_accessible.get()); | 435 ASSERT_TRUE(child_accessible.get()); |
383 (*child_checker)->CheckAccessible(child_accessible); | 436 (*child_checker)->CheckAccessible(child_accessible); |
384 } | 437 } |
385 } | 438 } |
386 | 439 |
387 base::string16 AccessibleChecker::RoleVariantToString( | 440 base::string16 AccessibleChecker::RoleVariantToString( |
388 const base::win::ScopedVariant& role) { | 441 const base::win::ScopedVariant& role) { |
389 if (role.type() == VT_I4) | 442 if (role.type() == VT_I4) |
390 return IAccessibleRoleToString(V_I4(&role)); | 443 return IAccessibleRoleToString(V_I4(&role)); |
391 if (role.type() == VT_BSTR) | 444 if (role.type() == VT_BSTR) |
(...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
746 std::wstring()); | 799 std::wstring()); |
747 AccessibleChecker grouping2_checker(std::wstring(), ROLE_SYSTEM_GROUPING, | 800 AccessibleChecker grouping2_checker(std::wstring(), ROLE_SYSTEM_GROUPING, |
748 std::wstring()); | 801 std::wstring()); |
749 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, | 802 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT, |
750 std::wstring()); | 803 std::wstring()); |
751 document_checker.AppendExpectedChild(&grouping1_checker); | 804 document_checker.AppendExpectedChild(&grouping1_checker); |
752 document_checker.AppendExpectedChild(&grouping2_checker); | 805 document_checker.AppendExpectedChild(&grouping2_checker); |
753 document_checker.CheckAccessible(GetRendererAccessible()); | 806 document_checker.CheckAccessible(GetRendererAccessible()); |
754 } | 807 } |
755 | 808 |
809 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestTextAtOffset) { | |
dmazzoni
2014/11/06 06:53:30
Consider splitting this into several smaller tests
| |
810 const std::wstring input_contents(L"Mozilla/5.0 (NT 6.x; WOW64) " | |
811 L"WebKit (KHTML, like)."); | |
812 const std::wstring textarea_contents(L"Mozilla/5.0 (NT 6.x; WOW64)\n" | |
813 L"WebKit \n(KHTML, like)."); | |
814 ASSERT_EQ(input_contents.size(), textarea_contents.size()); | |
815 LONG contents_length = static_cast<LONG>(input_contents.size()); | |
816 LoadInitialAccessibilityTreeFromHtml("<!DOCTYPE html>" | |
817 "<html><body><form>" | |
818 "<label for='name'>Browser name:</label>" | |
819 "<input type='text' id='name' name='name' value='" | |
820 "Mozilla/5.0 (NT 6.x; WOW64) WebKit " | |
821 "(KHTML, like).'>" | |
822 "</form><textarea rows='3' cols='60'>" | |
823 "<b>Moz</b>illa/<i>5.0 (NT</i> 6.x; WOW64)<br>" | |
824 "WebKit <u><br>(KHTML,</u> like)." | |
825 "</textarea></body></html>"); | |
826 | |
827 // Retrieve the IAccessible interface for the web page. | |
828 base::win::ScopedComPtr<IAccessible> document(GetRendererAccessible()); | |
829 std::vector<base::win::ScopedVariant> document_children = | |
830 GetAllAccessibleChildren(document); | |
831 ASSERT_EQ(2, document_children.size()); | |
832 | |
833 base::win::ScopedComPtr<IAccessible2> form; | |
834 HRESULT hr = QueryIAccessible2(GetAccessibleFromVariant( | |
835 document, document_children[0].AsInput()), form.Receive()); | |
836 ASSERT_EQ(S_OK, hr); | |
837 std::vector<base::win::ScopedVariant> form_children = | |
838 GetAllAccessibleChildren(form); | |
839 ASSERT_EQ(2, form_children.size()); | |
840 | |
841 // Find the two edit fields (simple and text area). | |
842 base::win::ScopedComPtr<IAccessible2> input; | |
843 hr = QueryIAccessible2(GetAccessibleFromVariant( | |
844 form, form_children[1].AsInput()), input.Receive()); | |
845 ASSERT_EQ(S_OK, hr); | |
846 base::win::ScopedVariant input_role; | |
847 hr = input->get_accRole( | |
848 base::win::ScopedVariant(CHILDID_SELF), input_role.Receive()); | |
849 ASSERT_EQ(S_OK, hr); | |
850 ASSERT_EQ(VT_I4, input_role.type()); | |
851 | |
852 base::win::ScopedComPtr<IAccessible2> textarea; | |
853 hr = QueryIAccessible2(GetAccessibleFromVariant( | |
854 document, document_children[1].AsInput()), textarea.Receive()); | |
855 ASSERT_EQ(S_OK, hr); | |
856 base::win::ScopedVariant textarea_role; | |
857 hr = textarea->get_accRole( | |
858 base::win::ScopedVariant(CHILDID_SELF), textarea_role.Receive()); | |
859 ASSERT_EQ(S_OK, hr); | |
860 ASSERT_EQ(VT_BSTR, textarea_role.type()); | |
861 | |
862 // Retrieve the IAccessibleText interface for both of the fields. | |
863 base::win::ScopedComPtr<IAccessibleText> input_text; | |
864 hr = input_text.QueryFrom(input); | |
865 ASSERT_EQ(S_OK, hr); | |
866 base::win::ScopedComPtr<IAccessibleText> textarea_text; | |
867 hr = textarea_text.QueryFrom(textarea); | |
868 ASSERT_EQ(S_OK, hr); | |
869 // Set the caret on the last character. | |
870 LONG caret_offset = 49; | |
871 hr = input_text->setCaretOffset(49); | |
872 ASSERT_EQ(S_OK, hr); | |
873 hr = textarea_text->setCaretOffset(49); | |
874 ASSERT_EQ(S_OK, hr); | |
875 | |
876 // Test invalid arguments. | |
877 hr = input_text->get_textAtOffset( | |
878 0, IA2_TEXT_BOUNDARY_CHAR, NULL, NULL, NULL); | |
879 EXPECT_EQ(E_INVALIDARG, hr); | |
880 hr = textarea_text->get_textAtOffset( | |
881 0, IA2_TEXT_BOUNDARY_CHAR, NULL, NULL, NULL); | |
882 EXPECT_EQ(E_INVALIDARG, hr); | |
883 | |
884 // Test invalid offset. | |
885 LONG invalid_offset = -5; | |
886 LONG start_offset = 0; | |
887 LONG end_offset = 0; | |
888 base::win::ScopedBstr text; | |
889 hr = input_text->get_textAtOffset( | |
890 invalid_offset, IA2_TEXT_BOUNDARY_WORD, | |
891 &start_offset, &end_offset, text.Receive()); | |
892 EXPECT_EQ(E_FAIL, hr); | |
893 hr = textarea_text->get_textAtOffset( | |
894 invalid_offset, IA2_TEXT_BOUNDARY_WORD, | |
895 &start_offset, &end_offset, text.Receive()); | |
896 EXPECT_EQ(E_FAIL, hr); | |
897 invalid_offset = contents_length; | |
898 hr = input_text->get_textAtOffset( | |
899 invalid_offset, IA2_TEXT_BOUNDARY_WORD, | |
900 &start_offset, &end_offset, text.Receive()); | |
901 EXPECT_EQ(E_FAIL, hr); | |
902 hr = textarea_text->get_textAtOffset( | |
903 invalid_offset, IA2_TEXT_BOUNDARY_WORD, | |
904 &start_offset, &end_offset, text.Receive()); | |
905 EXPECT_EQ(E_FAIL, hr); | |
906 | |
907 // Test character navigation. | |
908 for (LONG offset = 0; offset < contents_length; ++offset) { | |
909 std::wstring expected_text(textarea_contents[offset], 1); | |
910 LONG expected_start_offset = offset; | |
911 LONG expected_end_offset = offset + 1; | |
912 CheckTextAtOffset(input_text, offset, IA2_TEXT_BOUNDARY_CHAR, | |
913 expected_start_offset, expected_end_offset, expected_text); | |
914 } | |
915 for (LONG offset = contents_length - 1; offset >= 0; --offset) { | |
916 std::wstring expected_text(textarea_contents[offset], 1); | |
917 LONG expected_start_offset = offset; | |
918 LONG expected_end_offset = offset + 1; | |
919 CheckTextAtOffset(textarea_text, offset, IA2_TEXT_BOUNDARY_CHAR, | |
920 expected_start_offset, expected_end_offset, expected_text); | |
921 } | |
922 // Test special offsets. | |
923 CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_LENGTH, | |
924 IA2_TEXT_BOUNDARY_CHAR, 49, contents_length, L"."); | |
925 CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, | |
926 IA2_TEXT_BOUNDARY_CHAR, 49, contents_length, L"."); | |
927 CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_LENGTH, | |
928 IA2_TEXT_BOUNDARY_CHAR, 49, contents_length, L"."); | |
929 CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET, | |
930 IA2_TEXT_BOUNDARY_CHAR, 49, contents_length, L"."); | |
931 | |
932 // Test word navigation. | |
933 // (Imitate Firefox behavior.) | |
934 // Trailing punctuation should be included as part of the previous word. | |
935 CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_WORD, | |
936 0, 8, L"Mozilla/"); | |
937 CheckTextAtOffset(textarea_text, 6, IA2_TEXT_BOUNDARY_WORD, | |
938 0, 8, L"Mozilla/"); | |
939 // If the offset is at the punctuation, it should return | |
dmazzoni
2014/11/06 06:53:30
Nit: no indentatino for this comment.
| |
940 // the previous word. | |
941 CheckTextAtOffset(textarea_text, 7, IA2_TEXT_BOUNDARY_WORD, | |
942 0, 8, L"Mozilla/"); | |
943 // Numbers with a decimal point (U.S), should be treated as two words. | |
944 CheckTextAtOffset(input_text, 8, IA2_TEXT_BOUNDARY_WORD, | |
945 8, 10, L"5."); | |
946 CheckTextAtOffset(textarea_text, 9, IA2_TEXT_BOUNDARY_WORD, | |
947 8, 10, L"5."); | |
948 // Also, trailing punctuation that occurs after empty space should not be | |
949 // part of the word. ("0 " and not "0 (".) | |
950 CheckTextAtOffset(input_text, 10, IA2_TEXT_BOUNDARY_WORD, | |
951 10, 12, L"0 "); | |
952 CheckTextAtOffset(textarea_text, 11, IA2_TEXT_BOUNDARY_WORD, | |
953 10, 12, L"0 "); | |
954 // Leading punctuation should be included with the word after it. | |
955 CheckTextAtOffset(input_text, 12, IA2_TEXT_BOUNDARY_WORD, | |
956 12, 16, L"(NT "); | |
957 CheckTextAtOffset(textarea_text, 15, IA2_TEXT_BOUNDARY_WORD, | |
958 12, 16, L"(NT "); | |
959 // Numbers separated from letters with trailing punctuation should | |
960 // behave like decimal numbers above. | |
961 CheckTextAtOffset(input_text, 16, IA2_TEXT_BOUNDARY_WORD, | |
962 16, 18, L"6."); | |
963 CheckTextAtOffset(textarea_text, 19, IA2_TEXT_BOUNDARY_WORD, | |
964 18, 21, L"x; "); | |
965 // Words with numbers should be treated like ordinary words. | |
966 CheckTextAtOffset(input_text, 24, IA2_TEXT_BOUNDARY_WORD, | |
967 21, 28, L"WOW64) "); | |
968 CheckTextAtOffset(textarea_text, 26, IA2_TEXT_BOUNDARY_WORD, | |
969 21, 28, L"WOW64)\n"); | |
970 // Multiple trailing empty spaces should be part of the word preceding it. | |
971 CheckTextAtOffset(input_text, 30, IA2_TEXT_BOUNDARY_WORD, | |
972 28, 36, L"WebKit "); | |
973 CheckTextAtOffset(textarea_text, 35, IA2_TEXT_BOUNDARY_WORD, | |
974 28, 36, L"WebKit \n"); | |
975 // Test special offsets. | |
976 CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_LENGTH, | |
977 IA2_TEXT_BOUNDARY_WORD, 44, contents_length, L"like)."); | |
978 CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, | |
979 IA2_TEXT_BOUNDARY_WORD, 44, contents_length, L"like)."); | |
980 CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_LENGTH, | |
981 IA2_TEXT_BOUNDARY_WORD, 44, contents_length, L"like)."); | |
982 CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET, | |
983 IA2_TEXT_BOUNDARY_WORD, 44, contents_length, L"like)."); | |
984 | |
985 // Test sentence navigation (not currently implemented). | |
986 hr = input_text->get_textAtOffset( | |
987 5, IA2_TEXT_BOUNDARY_SENTENCE, | |
988 &start_offset, &end_offset, text.Receive()); | |
989 EXPECT_EQ(S_FALSE, hr); | |
990 hr = textarea_text->get_textAtOffset( | |
991 25, IA2_TEXT_BOUNDARY_CHAR, | |
992 &start_offset, &end_offset, text.Receive()); | |
993 EXPECT_EQ(S_FALSE, hr); | |
994 | |
995 // Test line navigation. | |
996 CheckTextAtOffset(textarea_text, 0, IA2_TEXT_BOUNDARY_LINE, | |
997 0, 28, L"Mozilla/5.0 (NT 6.x; WOW64)\n"); | |
998 // If the offset is at the newline, return the line preceding it. | |
999 CheckTextAtOffset(textarea_text, 35, IA2_TEXT_BOUNDARY_LINE, | |
1000 28, 36, L"WebKit \n"); | |
1001 // Last line does not have a trailing newline. | |
1002 CheckTextAtOffset(textarea_text, 36, IA2_TEXT_BOUNDARY_LINE, | |
1003 36, contents_length, L"(KHTML, like)."); | |
1004 // Test special offsets. | |
1005 CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_LENGTH, | |
1006 IA2_TEXT_BOUNDARY_LINE, 36, contents_length, L"(KHTML, like)."); | |
1007 CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, | |
1008 IA2_TEXT_BOUNDARY_LINE, 36, contents_length, L"(KHTML, like)."); | |
1009 CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_LENGTH, | |
1010 IA2_TEXT_BOUNDARY_LINE, 36, contents_length, L"(KHTML, like)."); | |
1011 CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET, | |
1012 IA2_TEXT_BOUNDARY_LINE, 36, contents_length, L"(KHTML, like)."); | |
1013 | |
1014 // Return the whole of the text. | |
1015 CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_ALL, | |
1016 0, contents_length, input_contents); | |
1017 CheckTextAtOffset(textarea_text, contents_length - 1, IA2_TEXT_BOUNDARY_ALL, | |
1018 0, contents_length, textarea_contents); | |
1019 } | |
1020 | |
756 } // namespace content | 1021 } // namespace content |
OLD | NEW |