Fixed IAccessibleText::TextAtOffset with IA2_TEXT_BOUNDARY_WORD to return text that...
[chromium-blink-merge.git] / content / browser / accessibility / accessibility_win_browsertest.cc
blobb3f553d024ecdab4c173e27ca5e7c1b065bfc58b
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
3 // found in the LICENSE file.
5 #include <vector>
7 #include "base/memory/scoped_ptr.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "base/win/scoped_bstr.h"
11 #include "base/win/scoped_comptr.h"
12 #include "base/win/scoped_variant.h"
13 #include "content/browser/accessibility/accessibility_mode_helper.h"
14 #include "content/browser/accessibility/accessibility_tree_formatter.h"
15 #include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
16 #include "content/browser/renderer_host/render_view_host_impl.h"
17 #include "content/public/browser/notification_service.h"
18 #include "content/public/browser/notification_types.h"
19 #include "content/public/browser/render_frame_host.h"
20 #include "content/public/browser/render_widget_host_view.h"
21 #include "content/public/browser/web_contents.h"
22 #include "content/public/common/url_constants.h"
23 #include "content/public/test/content_browser_test.h"
24 #include "content/public/test/content_browser_test_utils.h"
25 #include "content/shell/browser/shell.h"
26 #include "content/test/accessibility_browser_test_utils.h"
27 #include "net/base/escape.h"
28 #include "third_party/iaccessible2/ia2_api_all.h"
29 #include "third_party/isimpledom/ISimpleDOMNode.h"
30 #include "ui/aura/window.h"
31 #include "ui/aura/window_tree_host.h"
33 namespace content {
35 namespace {
38 const char INPUT_CONTENTS[] = "Moz/5.0 (ST 6.x; WWW33) "
39 "WebKit \"KHTML, like\".";
40 const char TEXTAREA_CONTENTS[] = "Moz/5.0 (ST 6.x; WWW33)\n"
41 "WebKit \n\"KHTML, like\".";
42 const LONG CONTENTS_LENGTH = static_cast<LONG>(
43 (sizeof(INPUT_CONTENTS) - 1) / sizeof(char));
46 // AccessibilityWinBrowserTest ------------------------------------------------
48 class AccessibilityWinBrowserTest : public ContentBrowserTest {
49 public:
50 AccessibilityWinBrowserTest();
51 virtual ~AccessibilityWinBrowserTest();
53 protected:
54 class AccessibleChecker;
55 void LoadInitialAccessibilityTreeFromHtml(const std::string& html);
56 IAccessible* GetRendererAccessible();
57 void ExecuteScript(const std::wstring& script);
58 void SetUpInputField(
59 base::win::ScopedComPtr<IAccessibleText>* input_text);
60 void SetUpTextareaField(
61 base::win::ScopedComPtr<IAccessibleText>* textarea_text);
64 static base::win::ScopedComPtr<IAccessible>
65 AccessibilityWinBrowserTest::GetAccessibleFromVariant(
66 IAccessible* parent,
67 VARIANT* var);
68 static HRESULT QueryIAccessible2(IAccessible* accessible,
69 IAccessible2** accessible2);
70 static void FindNodeInAccessibilityTree(IAccessible* node,
71 int32 expected_role,
72 const std::wstring& expected_name,
73 int32 depth,
74 bool* found);
75 static void CheckTextAtOffset(
76 base::win::ScopedComPtr<IAccessibleText>& element,
77 LONG offset,
78 IA2TextBoundaryType boundary_type,
79 LONG expected_start_offset,
80 LONG expected_end_offset,
81 const std::wstring& expected_text);
82 static std::vector<base::win::ScopedVariant> GetAllAccessibleChildren(
83 IAccessible* element);
85 private:
86 DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest);
89 AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() {
92 AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() {
95 void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml(
96 const std::string& html) {
97 AccessibilityNotificationWaiter waiter(
98 shell(), AccessibilityModeComplete,
99 ui::AX_EVENT_LOAD_COMPLETE);
100 GURL html_data_url("data:text/html," + html);
101 NavigateToURL(shell(), html_data_url);
102 waiter.WaitForNotification();
105 // Retrieve the MSAA client accessibility object for the Render Widget Host View
106 // of the selected tab.
107 IAccessible* AccessibilityWinBrowserTest::GetRendererAccessible() {
108 content::WebContents* web_contents = shell()->web_contents();
109 return web_contents->GetRenderWidgetHostView()->GetNativeViewAccessible();
112 void AccessibilityWinBrowserTest::ExecuteScript(const std::wstring& script) {
113 shell()->web_contents()->GetMainFrame()->ExecuteJavaScript(script);
116 // Loads a page with an input text field and places sample text in it. Also,
117 // places the caret on the last character.
118 void AccessibilityWinBrowserTest::SetUpInputField(
119 base::win::ScopedComPtr<IAccessibleText>* input_text) {
120 ASSERT_NE(nullptr, input_text);
121 LoadInitialAccessibilityTreeFromHtml(std::string("<!DOCTYPE html><html><body>"
122 "<form><label for='textField'>Browser name:</label>"
123 "<input type='text' id='textField' name='name' value='") +
124 net::EscapeQueryParamValue(INPUT_CONTENTS, false) + std::string(
125 "'></form></body></html>"));
127 // Retrieve the IAccessible interface for the web page.
128 base::win::ScopedComPtr<IAccessible> document(GetRendererAccessible());
129 std::vector<base::win::ScopedVariant> document_children =
130 GetAllAccessibleChildren(document.get());
131 ASSERT_EQ(1, document_children.size());
133 base::win::ScopedComPtr<IAccessible2> form;
134 HRESULT hr = QueryIAccessible2(GetAccessibleFromVariant(
135 document.get(), document_children[0].AsInput()).get(), form.Receive());
136 ASSERT_EQ(S_OK, hr);
137 std::vector<base::win::ScopedVariant> form_children =
138 GetAllAccessibleChildren(form.get());
139 ASSERT_EQ(2, form_children.size());
141 // Find the input text field.
142 base::win::ScopedComPtr<IAccessible2> input;
143 hr = QueryIAccessible2(GetAccessibleFromVariant(
144 form.get(), form_children[1].AsInput()).get(), input.Receive());
145 ASSERT_EQ(S_OK, hr);
146 LONG input_role = 0;
147 hr = input->role(&input_role);
148 ASSERT_EQ(S_OK, hr);
149 ASSERT_EQ(ROLE_SYSTEM_TEXT, input_role);
151 // Retrieve the IAccessibleText interface for the field.
152 hr = input.QueryInterface(input_text->Receive());
153 ASSERT_EQ(S_OK, hr);
155 // Set the caret on the last character.
156 AccessibilityNotificationWaiter waiter(
157 shell(), AccessibilityModeComplete,
158 ui::AX_EVENT_TEXT_SELECTION_CHANGED);
159 std::wstring caret_offset = base::UTF16ToWide(base::IntToString16(
160 static_cast<int>(CONTENTS_LENGTH - 1)));
161 ExecuteScript(std::wstring(
162 L"var textField = document.getElementById('textField');"
163 L"textField.focus();"
164 L"textField.setSelectionRange(") +
165 caret_offset + L"," + caret_offset + L");");
166 waiter.WaitForNotification();
169 // Loads a page with a textarea text field and places sample text in it. Also,
170 // places the caret on the last character.
171 void AccessibilityWinBrowserTest::SetUpTextareaField(
172 base::win::ScopedComPtr<IAccessibleText>* textarea_text) {
173 ASSERT_NE(nullptr, textarea_text);
174 LoadInitialAccessibilityTreeFromHtml(std::string("<!DOCTYPE html><html><body>"
175 "<textarea id='textField' rows='3' cols='60'>") +
176 net::EscapeQueryParamValue(TEXTAREA_CONTENTS, false) + std::string(
177 "</textarea></body></html>"));
179 // Retrieve the IAccessible interface for the web page.
180 base::win::ScopedComPtr<IAccessible> document(GetRendererAccessible());
181 std::vector<base::win::ScopedVariant> document_children =
182 GetAllAccessibleChildren(document.get());
183 ASSERT_EQ(1, document_children.size());
185 base::win::ScopedComPtr<IAccessible2> section;
186 HRESULT hr = QueryIAccessible2(GetAccessibleFromVariant(
187 document.get(), document_children[0].AsInput()).get(), section.Receive());
188 ASSERT_EQ(S_OK, hr);
189 std::vector<base::win::ScopedVariant> section_children =
190 GetAllAccessibleChildren(section.get());
191 ASSERT_EQ(1, section_children.size());
193 // Find the textarea text field.
194 base::win::ScopedComPtr<IAccessible2> textarea;
195 hr = QueryIAccessible2(GetAccessibleFromVariant(
196 section.get(), section_children[0].AsInput()).get(), textarea.Receive());
197 ASSERT_EQ(S_OK, hr);
198 LONG textarea_role = 0;
199 hr = textarea->role(&textarea_role);
200 ASSERT_EQ(S_OK, hr);
201 ASSERT_EQ(ROLE_SYSTEM_TEXT, textarea_role);
203 // Retrieve the IAccessibleText interface for the field.
204 hr = textarea.QueryInterface(textarea_text->Receive());
205 ASSERT_EQ(S_OK, hr);
207 // Set the caret on the last character.
208 AccessibilityNotificationWaiter waiter(
209 shell(), AccessibilityModeComplete,
210 ui::AX_EVENT_TEXT_SELECTION_CHANGED);
211 std::wstring caret_offset = base::UTF16ToWide(base::IntToString16(
212 static_cast<int>(CONTENTS_LENGTH - 1)));
213 ExecuteScript(std::wstring(
214 L"var textField = document.getElementById('textField');"
215 L"textField.focus();"
216 L"textField.setSelectionRange(") +
217 caret_offset + L"," + caret_offset + L");");
218 waiter.WaitForNotification();
222 // Static helpers ------------------------------------------------
224 base::win::ScopedComPtr<IAccessible>
225 AccessibilityWinBrowserTest::GetAccessibleFromVariant(
226 IAccessible* parent,
227 VARIANT* var) {
228 base::win::ScopedComPtr<IAccessible> ptr;
229 switch (V_VT(var)) {
230 case VT_DISPATCH: {
231 IDispatch* dispatch = V_DISPATCH(var);
232 if (dispatch)
233 dispatch->QueryInterface(ptr.Receive());
234 break;
237 case VT_I4: {
238 base::win::ScopedComPtr<IDispatch> dispatch;
239 HRESULT hr = parent->get_accChild(*var, dispatch.Receive());
240 EXPECT_TRUE(SUCCEEDED(hr));
241 if (dispatch.get())
242 dispatch.QueryInterface(ptr.Receive());
243 break;
246 return ptr;
249 HRESULT AccessibilityWinBrowserTest::QueryIAccessible2(
250 IAccessible* accessible,
251 IAccessible2** accessible2) {
252 // IA2 Spec dictates that IServiceProvider should be used instead of
253 // QueryInterface when retrieving IAccessible2.
254 base::win::ScopedComPtr<IServiceProvider> service_provider;
255 HRESULT hr = accessible->QueryInterface(service_provider.Receive());
256 return SUCCEEDED(hr) ?
257 service_provider->QueryService(IID_IAccessible2, accessible2) : hr;
260 // Recursively search through all of the descendants reachable from an
261 // IAccessible node and return true if we find one with the given role
262 // and name.
263 void AccessibilityWinBrowserTest::FindNodeInAccessibilityTree(
264 IAccessible* node,
265 int32 expected_role,
266 const std::wstring& expected_name,
267 int32 depth,
268 bool* found) {
269 base::win::ScopedBstr name_bstr;
270 base::win::ScopedVariant childid_self(CHILDID_SELF);
271 node->get_accName(childid_self, name_bstr.Receive());
272 std::wstring name(name_bstr, name_bstr.Length());
273 base::win::ScopedVariant role;
274 node->get_accRole(childid_self, role.Receive());
275 ASSERT_EQ(VT_I4, role.type());
277 // Print the accessibility tree as we go, because if this test fails
278 // on the bots, this is really helpful in figuring out why.
279 for (int i = 0; i < depth; i++)
280 printf(" ");
281 printf("role=%s name=%s\n",
282 base::WideToUTF8(IAccessibleRoleToString(V_I4(&role))).c_str(),
283 base::WideToUTF8(name).c_str());
285 if (expected_role == V_I4(&role) && expected_name == name) {
286 *found = true;
287 return;
290 std::vector<base::win::ScopedVariant> children = GetAllAccessibleChildren(
291 node);
292 for (size_t i = 0; i < children.size(); ++i) {
293 base::win::ScopedComPtr<IAccessible> child_accessible(
294 GetAccessibleFromVariant(node, children[i].AsInput()));
295 if (child_accessible) {
296 FindNodeInAccessibilityTree(
297 child_accessible.get(), expected_role, expected_name, depth + 1,
298 found);
299 if (*found)
300 return;
305 // Ensures that the text and the start and end offsets retrieved using
306 // get_textAtOffset match the expected values.
307 void AccessibilityWinBrowserTest::CheckTextAtOffset(
308 base::win::ScopedComPtr<IAccessibleText>& element,
309 LONG offset,
310 IA2TextBoundaryType boundary_type,
311 LONG expected_start_offset,
312 LONG expected_end_offset,
313 const std::wstring& expected_text) {
314 testing::Message message;
315 message << "While checking for \'" << expected_text << "\' at " <<
316 expected_start_offset << '-' << expected_end_offset << '.';
317 SCOPED_TRACE(message);
319 LONG start_offset = 0;
320 LONG end_offset = 0;
321 base::win::ScopedBstr text;
322 HRESULT hr = element->get_textAtOffset(
323 offset, boundary_type,
324 &start_offset, &end_offset, text.Receive());
325 EXPECT_EQ(S_OK, hr);
326 EXPECT_EQ(expected_start_offset, start_offset);
327 EXPECT_EQ(expected_end_offset, end_offset);
328 EXPECT_EQ(expected_text, std::wstring(text, text.Length()));
331 std::vector<base::win::ScopedVariant>
332 AccessibilityWinBrowserTest::GetAllAccessibleChildren(
333 IAccessible* element) {
334 LONG child_count = 0;
335 HRESULT hr = element->get_accChildCount(&child_count);
336 EXPECT_EQ(S_OK, hr);
337 if (child_count <= 0)
338 return std::vector<base::win::ScopedVariant>();
340 scoped_ptr<VARIANT[]> children_array(new VARIANT[child_count]);
341 LONG obtained_count = 0;
342 hr = AccessibleChildren(
343 element, 0, child_count, children_array.get(), &obtained_count);
344 EXPECT_EQ(S_OK, hr);
345 EXPECT_EQ(child_count, obtained_count);
347 std::vector<base::win::ScopedVariant> children(
348 static_cast<size_t>(child_count));
349 for (size_t i = 0; i < children.size(); i++) {
350 children[i].Reset(children_array[i]);
352 return children;
356 // AccessibleChecker ----------------------------------------------------------
358 class AccessibilityWinBrowserTest::AccessibleChecker {
359 public:
360 // This constructor can be used if the IA2 role will be the same as the MSAA
361 // role.
362 AccessibleChecker(const std::wstring& expected_name,
363 int32 expected_role,
364 const std::wstring& expected_value);
365 AccessibleChecker(const std::wstring& expected_name,
366 int32 expected_role,
367 int32 expected_ia2_role,
368 const std::wstring& expected_value);
369 AccessibleChecker(const std::wstring& expected_name,
370 const std::wstring& expected_role,
371 int32 expected_ia2_role,
372 const std::wstring& expected_value);
374 // Append an AccessibleChecker that verifies accessibility information for
375 // a child IAccessible. Order is important.
376 void AppendExpectedChild(AccessibleChecker* expected_child);
378 // Check that the name and role of the given IAccessible instance and its
379 // descendants match the expected names and roles that this object was
380 // initialized with.
381 void CheckAccessible(IAccessible* accessible);
383 // Set the expected value for this AccessibleChecker.
384 void SetExpectedValue(const std::wstring& expected_value);
386 // Set the expected state for this AccessibleChecker.
387 void SetExpectedState(LONG expected_state);
389 private:
390 typedef std::vector<AccessibleChecker*> AccessibleCheckerVector;
392 void CheckAccessibleName(IAccessible* accessible);
393 void CheckAccessibleRole(IAccessible* accessible);
394 void CheckIA2Role(IAccessible* accessible);
395 void CheckAccessibleValue(IAccessible* accessible);
396 void CheckAccessibleState(IAccessible* accessible);
397 void CheckAccessibleChildren(IAccessible* accessible);
398 base::string16 RoleVariantToString(const base::win::ScopedVariant& role);
400 // Expected accessible name. Checked against IAccessible::get_accName.
401 std::wstring name_;
403 // Expected accessible role. Checked against IAccessible::get_accRole.
404 base::win::ScopedVariant role_;
406 // Expected IAccessible2 role. Checked against IAccessible2::role.
407 int32 ia2_role_;
409 // Expected accessible value. Checked against IAccessible::get_accValue.
410 std::wstring value_;
412 // Expected accessible state. Checked against IAccessible::get_accState.
413 LONG state_;
415 // Expected accessible children. Checked using IAccessible::get_accChildCount
416 // and ::AccessibleChildren.
417 AccessibleCheckerVector children_;
420 AccessibilityWinBrowserTest::AccessibleChecker::AccessibleChecker(
421 const std::wstring& expected_name,
422 int32 expected_role,
423 const std::wstring& expected_value)
424 : name_(expected_name),
425 role_(expected_role),
426 ia2_role_(expected_role),
427 value_(expected_value),
428 state_(-1) {
431 AccessibilityWinBrowserTest::AccessibleChecker::AccessibleChecker(
432 const std::wstring& expected_name,
433 int32 expected_role,
434 int32 expected_ia2_role,
435 const std::wstring& expected_value)
436 : name_(expected_name),
437 role_(expected_role),
438 ia2_role_(expected_ia2_role),
439 value_(expected_value),
440 state_(-1) {
443 AccessibilityWinBrowserTest::AccessibleChecker::AccessibleChecker(
444 const std::wstring& expected_name,
445 const std::wstring& expected_role,
446 int32 expected_ia2_role,
447 const std::wstring& expected_value)
448 : name_(expected_name),
449 role_(expected_role.c_str()),
450 ia2_role_(expected_ia2_role),
451 value_(expected_value),
452 state_(-1) {
455 void AccessibilityWinBrowserTest::AccessibleChecker::AppendExpectedChild(
456 AccessibleChecker* expected_child) {
457 children_.push_back(expected_child);
460 void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessible(
461 IAccessible* accessible) {
462 SCOPED_TRACE("While checking "
463 + base::UTF16ToUTF8(RoleVariantToString(role_)));
464 CheckAccessibleName(accessible);
465 CheckAccessibleRole(accessible);
466 CheckIA2Role(accessible);
467 CheckAccessibleValue(accessible);
468 CheckAccessibleState(accessible);
469 CheckAccessibleChildren(accessible);
472 void AccessibilityWinBrowserTest::AccessibleChecker::SetExpectedValue(
473 const std::wstring& expected_value) {
474 value_ = expected_value;
477 void AccessibilityWinBrowserTest::AccessibleChecker::SetExpectedState(
478 LONG expected_state) {
479 state_ = expected_state;
482 void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleName(
483 IAccessible* accessible) {
484 base::win::ScopedBstr name;
485 base::win::ScopedVariant childid_self(CHILDID_SELF);
486 HRESULT hr = accessible->get_accName(childid_self, name.Receive());
488 if (name_.empty()) {
489 // If the object doesn't have name S_FALSE should be returned.
490 EXPECT_EQ(S_FALSE, hr);
491 } else {
492 // Test that the correct string was returned.
493 EXPECT_EQ(S_OK, hr);
494 EXPECT_EQ(name_, std::wstring(name, name.Length()));
498 void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleRole(
499 IAccessible* accessible) {
500 base::win::ScopedVariant role;
501 base::win::ScopedVariant childid_self(CHILDID_SELF);
502 HRESULT hr = accessible->get_accRole(childid_self, role.Receive());
503 ASSERT_EQ(S_OK, hr);
504 EXPECT_EQ(0, role_.Compare(role))
505 << "Expected role: " << RoleVariantToString(role_)
506 << "\nGot role: " << RoleVariantToString(role);
509 void AccessibilityWinBrowserTest::AccessibleChecker::CheckIA2Role(
510 IAccessible* accessible) {
511 base::win::ScopedComPtr<IAccessible2> accessible2;
512 HRESULT hr = QueryIAccessible2(accessible, accessible2.Receive());
513 ASSERT_EQ(S_OK, hr);
514 long ia2_role = 0;
515 hr = accessible2->role(&ia2_role);
516 ASSERT_EQ(S_OK, hr);
517 EXPECT_EQ(ia2_role_, ia2_role)
518 << "Expected ia2 role: " << IAccessible2RoleToString(ia2_role_)
519 << "\nGot ia2 role: " << IAccessible2RoleToString(ia2_role);
522 void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleValue(
523 IAccessible* accessible) {
524 // Don't check the value if if's a DOCUMENT role, because the value
525 // is supposed to be the url (and we don't keep track of that in the
526 // test expectations).
527 base::win::ScopedVariant role;
528 base::win::ScopedVariant childid_self(CHILDID_SELF);
529 HRESULT hr = accessible->get_accRole(childid_self, role.Receive());
530 ASSERT_EQ(S_OK, hr);
531 if (role.type() == VT_I4 && V_I4(&role) == ROLE_SYSTEM_DOCUMENT)
532 return;
534 // Get the value.
535 base::win::ScopedBstr value;
536 hr = accessible->get_accValue(childid_self, value.Receive());
537 EXPECT_EQ(S_OK, hr);
539 // Test that the correct string was returned.
540 EXPECT_EQ(value_, std::wstring(value, value.Length()));
543 void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleState(
544 IAccessible* accessible) {
545 if (state_ < 0)
546 return;
548 base::win::ScopedVariant state;
549 base::win::ScopedVariant childid_self(CHILDID_SELF);
550 HRESULT hr = accessible->get_accState(childid_self, state.Receive());
551 EXPECT_EQ(S_OK, hr);
552 ASSERT_EQ(VT_I4, state.type());
553 LONG obj_state = V_I4(&state);
554 // Avoid flakiness. The "offscreen" state depends on whether the browser
555 // window is frontmost or not, and "hottracked" depends on whether the
556 // mouse cursor happens to be over the element.
557 obj_state &= ~(STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_HOTTRACKED);
558 EXPECT_EQ(state_, obj_state)
559 << "Expected state: " << IAccessibleStateToString(state_)
560 << "\nGot state: " << IAccessibleStateToString(obj_state);
563 void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleChildren(
564 IAccessible* parent) {
565 std::vector<base::win::ScopedVariant> obtained_children =
566 GetAllAccessibleChildren(parent);
567 size_t child_count = obtained_children.size();
568 ASSERT_EQ(child_count, children_.size());
570 AccessibleCheckerVector::iterator child_checker;
571 std::vector<base::win::ScopedVariant>::iterator child;
572 for (child_checker = children_.begin(),
573 child = obtained_children.begin();
574 child_checker != children_.end()
575 && child != obtained_children.end();
576 ++child_checker, ++child) {
577 base::win::ScopedComPtr<IAccessible> child_accessible(
578 GetAccessibleFromVariant(parent, child->AsInput()));
579 ASSERT_TRUE(child_accessible.get());
580 (*child_checker)->CheckAccessible(child_accessible.get());
584 base::string16
585 AccessibilityWinBrowserTest::AccessibleChecker::RoleVariantToString(
586 const base::win::ScopedVariant& role) {
587 if (role.type() == VT_I4)
588 return IAccessibleRoleToString(V_I4(&role));
589 if (role.type() == VT_BSTR)
590 return base::string16(V_BSTR(&role), SysStringLen(V_BSTR(&role)));
591 return base::string16();
594 } // namespace
597 // Tests ----------------------------------------------------------------------
599 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
600 TestBusyAccessibilityTree) {
601 if (GetBaseAccessibilityMode() != AccessibilityModeOff)
602 return;
604 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
606 // The initial accessible returned should have state STATE_SYSTEM_BUSY while
607 // the accessibility tree is being requested from the renderer.
608 AccessibleChecker document1_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
609 std::wstring());
610 document1_checker.SetExpectedState(
611 STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED |
612 STATE_SYSTEM_BUSY);
613 document1_checker.CheckAccessible(GetRendererAccessible());
616 // Periodically failing. See crbug.com/145537
617 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
618 DISABLED_TestNotificationActiveDescendantChanged) {
619 LoadInitialAccessibilityTreeFromHtml(
620 "<ul tabindex='-1' role='radiogroup' aria-label='ul'>"
621 "<li id='li'>li</li></ul>");
623 // Check the browser's copy of the renderer accessibility tree.
624 AccessibleChecker list_marker_checker(L"\x2022", ROLE_SYSTEM_TEXT,
625 std::wstring());
626 AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT,
627 std::wstring());
628 AccessibleChecker list_item_checker(std::wstring(), ROLE_SYSTEM_LISTITEM,
629 std::wstring());
630 list_item_checker.SetExpectedState(STATE_SYSTEM_READONLY);
631 AccessibleChecker radio_group_checker(L"ul", ROLE_SYSTEM_GROUPING,
632 IA2_ROLE_SECTION, std::wstring());
633 radio_group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
634 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
635 std::wstring());
636 list_item_checker.AppendExpectedChild(&list_marker_checker);
637 list_item_checker.AppendExpectedChild(&static_text_checker);
638 radio_group_checker.AppendExpectedChild(&list_item_checker);
639 document_checker.AppendExpectedChild(&radio_group_checker);
640 document_checker.CheckAccessible(GetRendererAccessible());
642 // Set focus to the radio group.
643 scoped_ptr<AccessibilityNotificationWaiter> waiter(
644 new AccessibilityNotificationWaiter(
645 shell(), AccessibilityModeComplete,
646 ui::AX_EVENT_FOCUS));
647 ExecuteScript(L"document.body.children[0].focus()");
648 waiter->WaitForNotification();
650 // Check that the accessibility tree of the browser has been updated.
651 radio_group_checker.SetExpectedState(
652 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED);
653 document_checker.CheckAccessible(GetRendererAccessible());
655 // Set the active descendant of the radio group
656 waiter.reset(new AccessibilityNotificationWaiter(
657 shell(), AccessibilityModeComplete,
658 ui::AX_EVENT_FOCUS));
659 ExecuteScript(
660 L"document.body.children[0].setAttribute('aria-activedescendant', 'li')");
661 waiter->WaitForNotification();
663 // Check that the accessibility tree of the browser has been updated.
664 list_item_checker.SetExpectedState(
665 STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED);
666 radio_group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
667 document_checker.CheckAccessible(GetRendererAccessible());
670 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
671 TestNotificationCheckedStateChanged) {
672 LoadInitialAccessibilityTreeFromHtml(
673 "<body><input type='checkbox' /></body>");
675 // Check the browser's copy of the renderer accessibility tree.
676 AccessibleChecker checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON,
677 std::wstring());
678 checkbox_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
679 AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
680 std::wstring());
681 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
682 std::wstring());
683 body_checker.AppendExpectedChild(&checkbox_checker);
684 document_checker.AppendExpectedChild(&body_checker);
685 document_checker.CheckAccessible(GetRendererAccessible());
687 // Check the checkbox.
688 scoped_ptr<AccessibilityNotificationWaiter> waiter(
689 new AccessibilityNotificationWaiter(
690 shell(), AccessibilityModeComplete,
691 ui::AX_EVENT_CHECKED_STATE_CHANGED));
692 ExecuteScript(L"document.body.children[0].checked=true");
693 waiter->WaitForNotification();
695 // Check that the accessibility tree of the browser has been updated.
696 checkbox_checker.SetExpectedState(
697 STATE_SYSTEM_CHECKED | STATE_SYSTEM_FOCUSABLE);
698 document_checker.CheckAccessible(GetRendererAccessible());
701 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
702 TestNotificationChildrenChanged) {
703 // The role attribute causes the node to be in the accessibility tree.
704 LoadInitialAccessibilityTreeFromHtml("<body role=group></body>");
706 // Check the browser's copy of the renderer accessibility tree.
707 AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
708 std::wstring());
709 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
710 std::wstring());
711 document_checker.AppendExpectedChild(&group_checker);
712 document_checker.CheckAccessible(GetRendererAccessible());
714 // Change the children of the document body.
715 scoped_ptr<AccessibilityNotificationWaiter> waiter(
716 new AccessibilityNotificationWaiter(
717 shell(),
718 AccessibilityModeComplete,
719 ui::AX_EVENT_CHILDREN_CHANGED));
720 ExecuteScript(L"document.body.innerHTML='<b>new text</b>'");
721 waiter->WaitForNotification();
723 // Check that the accessibility tree of the browser has been updated.
724 AccessibleChecker text_checker(
725 L"new text", ROLE_SYSTEM_STATICTEXT, std::wstring());
726 group_checker.AppendExpectedChild(&text_checker);
727 document_checker.CheckAccessible(GetRendererAccessible());
730 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
731 TestNotificationChildrenChanged2) {
732 // The role attribute causes the node to be in the accessibility tree.
733 LoadInitialAccessibilityTreeFromHtml(
734 "<div role=group style='visibility: hidden'>text</div>");
736 // Check the accessible tree of the browser.
737 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
738 std::wstring());
739 document_checker.CheckAccessible(GetRendererAccessible());
741 // Change the children of the document body.
742 scoped_ptr<AccessibilityNotificationWaiter> waiter(
743 new AccessibilityNotificationWaiter(
744 shell(), AccessibilityModeComplete,
745 ui::AX_EVENT_CHILDREN_CHANGED));
746 ExecuteScript(L"document.body.children[0].style.visibility='visible'");
747 waiter->WaitForNotification();
749 // Check that the accessibility tree of the browser has been updated.
750 AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_STATICTEXT,
751 std::wstring());
752 AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
753 std::wstring());
754 document_checker.AppendExpectedChild(&group_checker);
755 group_checker.AppendExpectedChild(&static_text_checker);
756 document_checker.CheckAccessible(GetRendererAccessible());
759 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
760 TestNotificationFocusChanged) {
761 // The role attribute causes the node to be in the accessibility tree.
762 LoadInitialAccessibilityTreeFromHtml("<div role=group tabindex='-1'></div>");
764 // Check the browser's copy of the renderer accessibility tree.
765 SCOPED_TRACE("Check initial tree");
766 AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
767 std::wstring());
768 group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
769 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
770 std::wstring());
771 document_checker.AppendExpectedChild(&group_checker);
772 document_checker.CheckAccessible(GetRendererAccessible());
774 // Focus the div in the document
775 scoped_ptr<AccessibilityNotificationWaiter> waiter(
776 new AccessibilityNotificationWaiter(
777 shell(), AccessibilityModeComplete,
778 ui::AX_EVENT_FOCUS));
779 ExecuteScript(L"document.body.children[0].focus()");
780 waiter->WaitForNotification();
782 // Check that the accessibility tree of the browser has been updated.
783 SCOPED_TRACE("Check updated tree after focusing div");
784 group_checker.SetExpectedState(
785 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED);
786 document_checker.CheckAccessible(GetRendererAccessible());
788 // Focus the document accessible. This will un-focus the current node.
789 waiter.reset(
790 new AccessibilityNotificationWaiter(
791 shell(), AccessibilityModeComplete,
792 ui::AX_EVENT_BLUR));
793 base::win::ScopedComPtr<IAccessible> document_accessible(
794 GetRendererAccessible());
795 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
796 base::win::ScopedVariant childid_self(CHILDID_SELF);
797 HRESULT hr = document_accessible->accSelect(SELFLAG_TAKEFOCUS, childid_self);
798 ASSERT_EQ(S_OK, hr);
799 waiter->WaitForNotification();
801 // Check that the accessibility tree of the browser has been updated.
802 SCOPED_TRACE("Check updated tree after focusing document again");
803 group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
804 document_checker.CheckAccessible(GetRendererAccessible());
807 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
808 TestNotificationValueChanged) {
809 LoadInitialAccessibilityTreeFromHtml(
810 "<body><input type='text' value='old value'/></body>");
812 // Check the browser's copy of the renderer accessibility tree.
813 AccessibleChecker text_field_checker(std::wstring(), ROLE_SYSTEM_TEXT,
814 L"old value");
815 text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
816 AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
817 std::wstring());
818 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
819 std::wstring());
820 body_checker.AppendExpectedChild(&text_field_checker);
821 document_checker.AppendExpectedChild(&body_checker);
822 document_checker.CheckAccessible(GetRendererAccessible());
824 // Set the value of the text control
825 scoped_ptr<AccessibilityNotificationWaiter> waiter(
826 new AccessibilityNotificationWaiter(
827 shell(), AccessibilityModeComplete,
828 ui::AX_EVENT_VALUE_CHANGED));
829 ExecuteScript(L"document.body.children[0].value='new value'");
830 waiter->WaitForNotification();
832 // Check that the accessibility tree of the browser has been updated.
833 text_field_checker.SetExpectedValue(L"new value");
834 document_checker.CheckAccessible(GetRendererAccessible());
837 // This test verifies that the web content's accessibility tree is a
838 // descendant of the main browser window's accessibility tree, so that
839 // tools like AccExplorer32 or AccProbe can be used to examine Chrome's
840 // accessibility support.
842 // If you made a change and this test now fails, check that the NativeViewHost
843 // that wraps the tab contents returns the IAccessible implementation
844 // provided by RenderWidgetHostViewWin in GetNativeViewAccessible().
845 // flaky: http://crbug.com/402190
846 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
847 DISABLED_ContainsRendererAccessibilityTree) {
848 LoadInitialAccessibilityTreeFromHtml(
849 "<html><head><title>MyDocument</title></head>"
850 "<body>Content</body></html>");
852 // Get the accessibility object for the window tree host.
853 aura::Window* window = shell()->window();
854 CHECK(window);
855 aura::WindowTreeHost* window_tree_host = window->GetHost();
856 CHECK(window_tree_host);
857 HWND hwnd = window_tree_host->GetAcceleratedWidget();
858 CHECK(hwnd);
859 base::win::ScopedComPtr<IAccessible> browser_accessible;
860 HRESULT hr = AccessibleObjectFromWindow(
861 hwnd,
862 OBJID_WINDOW,
863 IID_IAccessible,
864 reinterpret_cast<void**>(browser_accessible.Receive()));
865 ASSERT_EQ(S_OK, hr);
867 bool found = false;
868 FindNodeInAccessibilityTree(
869 browser_accessible.get(), ROLE_SYSTEM_DOCUMENT, L"MyDocument", 0, &found);
870 ASSERT_EQ(found, true);
873 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
874 SupportsISimpleDOM) {
875 LoadInitialAccessibilityTreeFromHtml(
876 "<body><input type='checkbox' /></body>");
878 // Get the IAccessible object for the document.
879 base::win::ScopedComPtr<IAccessible> document_accessible(
880 GetRendererAccessible());
881 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
883 // Get the ISimpleDOM object for the document.
884 base::win::ScopedComPtr<IServiceProvider> service_provider;
885 HRESULT hr = static_cast<IAccessible*>(document_accessible.get())
886 ->QueryInterface(service_provider.Receive());
887 ASSERT_EQ(S_OK, hr);
888 const GUID refguid = {0x0c539790, 0x12e4, 0x11cf,
889 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
890 base::win::ScopedComPtr<ISimpleDOMNode> document_isimpledomnode;
891 hr = static_cast<IServiceProvider*>(service_provider.get())
892 ->QueryService(
893 refguid, IID_ISimpleDOMNode,
894 reinterpret_cast<void**>(document_isimpledomnode.Receive()));
895 ASSERT_EQ(S_OK, hr);
897 base::win::ScopedBstr node_name;
898 short name_space_id; // NOLINT
899 base::win::ScopedBstr node_value;
900 unsigned int num_children;
901 unsigned int unique_id;
902 unsigned short node_type; // NOLINT
903 hr = document_isimpledomnode->get_nodeInfo(
904 node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
905 &unique_id, &node_type);
906 ASSERT_EQ(S_OK, hr);
907 EXPECT_EQ(NODETYPE_DOCUMENT, node_type);
908 EXPECT_EQ(1, num_children);
909 node_name.Reset();
910 node_value.Reset();
912 base::win::ScopedComPtr<ISimpleDOMNode> body_isimpledomnode;
913 hr = document_isimpledomnode->get_firstChild(
914 body_isimpledomnode.Receive());
915 ASSERT_EQ(S_OK, hr);
916 hr = body_isimpledomnode->get_nodeInfo(
917 node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
918 &unique_id, &node_type);
919 ASSERT_EQ(S_OK, hr);
920 EXPECT_EQ(L"body", std::wstring(node_name, node_name.Length()));
921 EXPECT_EQ(NODETYPE_ELEMENT, node_type);
922 EXPECT_EQ(1, num_children);
923 node_name.Reset();
924 node_value.Reset();
926 base::win::ScopedComPtr<ISimpleDOMNode> checkbox_isimpledomnode;
927 hr = body_isimpledomnode->get_firstChild(
928 checkbox_isimpledomnode.Receive());
929 ASSERT_EQ(S_OK, hr);
930 hr = checkbox_isimpledomnode->get_nodeInfo(
931 node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
932 &unique_id, &node_type);
933 ASSERT_EQ(S_OK, hr);
934 EXPECT_EQ(L"input", std::wstring(node_name, node_name.Length()));
935 EXPECT_EQ(NODETYPE_ELEMENT, node_type);
936 EXPECT_EQ(0, num_children);
939 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestRoleGroup) {
940 LoadInitialAccessibilityTreeFromHtml(
941 "<fieldset></fieldset><div role=group></div>");
943 // Check the browser's copy of the renderer accessibility tree.
944 AccessibleChecker grouping1_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
945 std::wstring());
946 AccessibleChecker grouping2_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
947 std::wstring());
948 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
949 std::wstring());
950 document_checker.AppendExpectedChild(&grouping1_checker);
951 document_checker.AppendExpectedChild(&grouping2_checker);
952 document_checker.CheckAccessible(GetRendererAccessible());
955 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
956 TestSetCaretOffset) {
957 base::win::ScopedComPtr<IAccessibleText> input_text;
958 SetUpInputField(&input_text);
960 LONG caret_offset = 0;
961 HRESULT hr = input_text->get_caretOffset(&caret_offset);
962 EXPECT_EQ(S_OK, hr);
963 EXPECT_EQ(CONTENTS_LENGTH - 1, caret_offset);
965 AccessibilityNotificationWaiter waiter(
966 shell(), AccessibilityModeComplete,
967 ui::AX_EVENT_TEXT_SELECTION_CHANGED);
968 caret_offset = 0;
969 hr = input_text->setCaretOffset(caret_offset);
970 EXPECT_EQ(S_OK, hr);
971 waiter.WaitForNotification();
973 hr = input_text->get_caretOffset(&caret_offset);
974 EXPECT_EQ(S_OK, hr);
975 EXPECT_EQ(0, caret_offset);
978 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
979 TestMultiLineSetCaretOffset) {
980 base::win::ScopedComPtr<IAccessibleText> textarea_text;
981 SetUpTextareaField(&textarea_text);
983 LONG caret_offset = 0;
984 HRESULT hr = textarea_text->get_caretOffset(&caret_offset);
985 EXPECT_EQ(S_OK, hr);
986 EXPECT_EQ(CONTENTS_LENGTH - 1, caret_offset);
988 AccessibilityNotificationWaiter waiter(
989 shell(), AccessibilityModeComplete,
990 ui::AX_EVENT_TEXT_SELECTION_CHANGED);
991 caret_offset = 0;
992 hr = textarea_text->setCaretOffset(caret_offset);
993 EXPECT_EQ(S_OK, hr);
994 waiter.WaitForNotification();
996 hr = textarea_text->get_caretOffset(&caret_offset);
997 EXPECT_EQ(S_OK, hr);
998 EXPECT_EQ(0, caret_offset);
1001 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1002 TestTextAtOffsetWithInvalidArgs) {
1003 base::win::ScopedComPtr<IAccessibleText> input_text;
1004 SetUpInputField(&input_text);
1005 HRESULT hr = input_text->get_textAtOffset(
1006 0, IA2_TEXT_BOUNDARY_CHAR, NULL, NULL, NULL);
1007 EXPECT_EQ(E_INVALIDARG, hr);
1009 // Test invalid offset.
1010 LONG start_offset = 0;
1011 LONG end_offset = 0;
1012 base::win::ScopedBstr text;
1013 LONG invalid_offset = -5;
1014 hr = input_text->get_textAtOffset(
1015 invalid_offset, IA2_TEXT_BOUNDARY_CHAR,
1016 &start_offset, &end_offset, text.Receive());
1017 EXPECT_EQ(E_INVALIDARG, hr);
1018 EXPECT_EQ(0, start_offset);
1019 EXPECT_EQ(0, end_offset);
1020 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1021 invalid_offset = CONTENTS_LENGTH + 1;
1022 hr = input_text->get_textAtOffset(
1023 invalid_offset, IA2_TEXT_BOUNDARY_WORD,
1024 &start_offset, &end_offset, text.Receive());
1025 EXPECT_EQ(E_INVALIDARG, hr);
1026 EXPECT_EQ(0, start_offset);
1027 EXPECT_EQ(0, end_offset);
1028 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1030 // According to the IA2 Spec, only line boundaries should succeed when
1031 // the offset is one past the end of the text.
1032 invalid_offset = CONTENTS_LENGTH;
1033 hr = input_text->get_textAtOffset(
1034 invalid_offset, IA2_TEXT_BOUNDARY_CHAR,
1035 &start_offset, &end_offset, text.Receive());
1036 EXPECT_EQ(S_FALSE, hr);
1037 EXPECT_EQ(0, start_offset);
1038 EXPECT_EQ(0, end_offset);
1039 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1040 hr = input_text->get_textAtOffset(
1041 invalid_offset, IA2_TEXT_BOUNDARY_WORD,
1042 &start_offset, &end_offset, text.Receive());
1043 EXPECT_EQ(S_FALSE, hr);
1044 EXPECT_EQ(0, start_offset);
1045 EXPECT_EQ(0, end_offset);
1046 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1047 hr = input_text->get_textAtOffset(
1048 invalid_offset, IA2_TEXT_BOUNDARY_SENTENCE,
1049 &start_offset, &end_offset, text.Receive());
1050 EXPECT_EQ(S_FALSE, hr);
1051 EXPECT_EQ(0, start_offset);
1052 EXPECT_EQ(0, end_offset);
1053 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1054 hr = input_text->get_textAtOffset(
1055 invalid_offset, IA2_TEXT_BOUNDARY_ALL,
1056 &start_offset, &end_offset, text.Receive());
1057 EXPECT_EQ(S_FALSE, hr);
1058 EXPECT_EQ(0, start_offset);
1059 EXPECT_EQ(0, end_offset);
1060 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1062 // The same behavior should be observed when the special offset
1063 // IA2_TEXT_OFFSET_LENGTH is used.
1064 hr = input_text->get_textAtOffset(
1065 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_CHAR,
1066 &start_offset, &end_offset, text.Receive());
1067 EXPECT_EQ(S_FALSE, hr);
1068 EXPECT_EQ(0, start_offset);
1069 EXPECT_EQ(0, end_offset);
1070 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1071 hr = input_text->get_textAtOffset(
1072 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_WORD,
1073 &start_offset, &end_offset, text.Receive());
1074 EXPECT_EQ(S_FALSE, hr);
1075 EXPECT_EQ(0, start_offset);
1076 EXPECT_EQ(0, end_offset);
1077 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1078 hr = input_text->get_textAtOffset(
1079 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_SENTENCE,
1080 &start_offset, &end_offset, text.Receive());
1081 EXPECT_EQ(S_FALSE, hr);
1082 EXPECT_EQ(0, start_offset);
1083 EXPECT_EQ(0, end_offset);
1084 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1085 hr = input_text->get_textAtOffset(
1086 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_ALL,
1087 &start_offset, &end_offset, text.Receive());
1088 EXPECT_EQ(S_FALSE, hr);
1089 EXPECT_EQ(0, start_offset);
1090 EXPECT_EQ(0, end_offset);
1091 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1094 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1095 TestMultiLineTextAtOffsetWithInvalidArgs) {
1096 base::win::ScopedComPtr<IAccessibleText> textarea_text;
1097 SetUpTextareaField(&textarea_text);
1098 HRESULT hr = textarea_text->get_textAtOffset(
1099 0, IA2_TEXT_BOUNDARY_CHAR, NULL, NULL, NULL);
1100 EXPECT_EQ(E_INVALIDARG, hr);
1102 // Test invalid offset.
1103 LONG start_offset = 0;
1104 LONG end_offset = 0;
1105 base::win::ScopedBstr text;
1106 LONG invalid_offset = -5;
1107 hr = textarea_text->get_textAtOffset(
1108 invalid_offset, IA2_TEXT_BOUNDARY_CHAR,
1109 &start_offset, &end_offset, text.Receive());
1110 EXPECT_EQ(E_INVALIDARG, hr);
1111 EXPECT_EQ(0, start_offset);
1112 EXPECT_EQ(0, end_offset);
1113 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1114 invalid_offset = CONTENTS_LENGTH + 1;
1115 hr = textarea_text->get_textAtOffset(
1116 invalid_offset, IA2_TEXT_BOUNDARY_WORD,
1117 &start_offset, &end_offset, text.Receive());
1118 EXPECT_EQ(E_INVALIDARG, hr);
1119 EXPECT_EQ(0, start_offset);
1120 EXPECT_EQ(0, end_offset);
1121 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1123 // According to the IA2 Spec, only line boundaries should succeed when
1124 // the offset is one past the end of the text.
1125 invalid_offset = CONTENTS_LENGTH;
1126 hr = textarea_text->get_textAtOffset(
1127 invalid_offset, IA2_TEXT_BOUNDARY_CHAR,
1128 &start_offset, &end_offset, text.Receive());
1129 EXPECT_EQ(S_FALSE, hr);
1130 EXPECT_EQ(0, start_offset);
1131 EXPECT_EQ(0, end_offset);
1132 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1133 hr = textarea_text->get_textAtOffset(
1134 invalid_offset, IA2_TEXT_BOUNDARY_WORD,
1135 &start_offset, &end_offset, text.Receive());
1136 EXPECT_EQ(S_FALSE, hr);
1137 EXPECT_EQ(0, start_offset);
1138 EXPECT_EQ(0, end_offset);
1139 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1140 hr = textarea_text->get_textAtOffset(
1141 invalid_offset, IA2_TEXT_BOUNDARY_SENTENCE,
1142 &start_offset, &end_offset, text.Receive());
1143 EXPECT_EQ(S_FALSE, hr);
1144 EXPECT_EQ(0, start_offset);
1145 EXPECT_EQ(0, end_offset);
1146 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1147 hr = textarea_text->get_textAtOffset(
1148 invalid_offset, IA2_TEXT_BOUNDARY_ALL,
1149 &start_offset, &end_offset, text.Receive());
1150 EXPECT_EQ(S_FALSE, hr);
1151 EXPECT_EQ(0, start_offset);
1152 EXPECT_EQ(0, end_offset);
1153 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1155 // The same behavior should be observed when the special offset
1156 // IA2_TEXT_OFFSET_LENGTH is used.
1157 hr = textarea_text->get_textAtOffset(
1158 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_CHAR,
1159 &start_offset, &end_offset, text.Receive());
1160 EXPECT_EQ(S_FALSE, hr);
1161 EXPECT_EQ(0, start_offset);
1162 EXPECT_EQ(0, end_offset);
1163 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1164 hr = textarea_text->get_textAtOffset(
1165 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_WORD,
1166 &start_offset, &end_offset, text.Receive());
1167 EXPECT_EQ(S_FALSE, hr);
1168 EXPECT_EQ(0, start_offset);
1169 EXPECT_EQ(0, end_offset);
1170 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1171 hr = textarea_text->get_textAtOffset(
1172 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_SENTENCE,
1173 &start_offset, &end_offset, text.Receive());
1174 EXPECT_EQ(S_FALSE, hr);
1175 EXPECT_EQ(0, start_offset);
1176 EXPECT_EQ(0, end_offset);
1177 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1178 hr = textarea_text->get_textAtOffset(
1179 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_ALL,
1180 &start_offset, &end_offset, text.Receive());
1181 EXPECT_EQ(S_FALSE, hr);
1182 EXPECT_EQ(0, start_offset);
1183 EXPECT_EQ(0, end_offset);
1184 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1187 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1188 TestTextAtOffsetWithBoundaryCharacter) {
1189 base::win::ScopedComPtr<IAccessibleText> input_text;
1190 SetUpInputField(&input_text);
1191 for (LONG offset = 0; offset < CONTENTS_LENGTH; ++offset) {
1192 std::wstring expected_text(1, INPUT_CONTENTS[offset]);
1193 LONG expected_start_offset = offset;
1194 LONG expected_end_offset = offset + 1;
1195 CheckTextAtOffset(input_text, offset, IA2_TEXT_BOUNDARY_CHAR,
1196 expected_start_offset, expected_end_offset, expected_text);
1199 for (LONG offset = CONTENTS_LENGTH - 1; offset >= 0; --offset) {
1200 std::wstring expected_text(1, INPUT_CONTENTS[offset]);
1201 LONG expected_start_offset = offset;
1202 LONG expected_end_offset = offset + 1;
1203 CheckTextAtOffset(input_text, offset, IA2_TEXT_BOUNDARY_CHAR,
1204 expected_start_offset, expected_end_offset, expected_text);
1207 CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET,
1208 IA2_TEXT_BOUNDARY_CHAR, CONTENTS_LENGTH - 1, CONTENTS_LENGTH, L".");
1211 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1212 DISABLED_TestMultiLineTextAtOffsetWithBoundaryCharacter) {
1213 base::win::ScopedComPtr<IAccessibleText> textarea_text;
1214 SetUpTextareaField(&textarea_text);
1215 for (LONG offset = 0; offset < CONTENTS_LENGTH; ++offset) {
1216 std::wstring expected_text(1, TEXTAREA_CONTENTS[offset]);
1217 LONG expected_start_offset = offset;
1218 LONG expected_end_offset = offset + 1;
1219 CheckTextAtOffset(textarea_text, offset, IA2_TEXT_BOUNDARY_CHAR,
1220 expected_start_offset, expected_end_offset, expected_text);
1223 for (LONG offset = CONTENTS_LENGTH - 1; offset >= 0; --offset) {
1224 std::wstring expected_text(1, TEXTAREA_CONTENTS[offset]);
1225 LONG expected_start_offset = offset;
1226 LONG expected_end_offset = offset + 1;
1227 CheckTextAtOffset(textarea_text, offset, IA2_TEXT_BOUNDARY_CHAR,
1228 expected_start_offset, expected_end_offset, expected_text);
1231 CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET,
1232 IA2_TEXT_BOUNDARY_CHAR, CONTENTS_LENGTH - 1, CONTENTS_LENGTH, L".");
1235 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1236 TestTextAtOffsetWithBoundaryWord) {
1237 base::win::ScopedComPtr<IAccessibleText> input_text;
1238 SetUpInputField(&input_text);
1240 // Trailing punctuation should be included as part of the previous word.
1241 CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_WORD,
1242 0, 4, L"Moz/");
1243 CheckTextAtOffset(input_text, 2, IA2_TEXT_BOUNDARY_WORD,
1244 0, 4, L"Moz/");
1246 // If the offset is at the punctuation, it should return
1247 // the previous word.
1248 CheckTextAtOffset(input_text, 3, IA2_TEXT_BOUNDARY_WORD,
1249 0, 4, L"Moz/");
1251 // Numbers with a decimal point ("." for U.S), should be treated as one word.
1252 // Also, trailing punctuation that occurs after empty space should be part of
1253 // the word. ("5.0 (" and not "5.0 ".)
1254 CheckTextAtOffset(input_text, 4, IA2_TEXT_BOUNDARY_WORD,
1255 4, 9, L"5.0 (");
1256 CheckTextAtOffset(input_text, 5, IA2_TEXT_BOUNDARY_WORD,
1257 4, 9, L"5.0 (");
1258 CheckTextAtOffset(input_text, 6, IA2_TEXT_BOUNDARY_WORD,
1259 4, 9, L"5.0 (");
1260 CheckTextAtOffset(input_text, 7, IA2_TEXT_BOUNDARY_WORD,
1261 4, 9, L"5.0 (");
1263 // Leading punctuation should not be included with the word after it.
1264 CheckTextAtOffset(input_text, 8, IA2_TEXT_BOUNDARY_WORD,
1265 4, 9, L"5.0 (");
1266 CheckTextAtOffset(input_text, 11, IA2_TEXT_BOUNDARY_WORD,
1267 9, 12, L"ST ");
1269 // Numbers separated from letters with trailing punctuation should
1270 // be split into two words. Same for abreviations like "i.e.".
1271 CheckTextAtOffset(input_text, 12, IA2_TEXT_BOUNDARY_WORD,
1272 12, 14, L"6.");
1273 CheckTextAtOffset(input_text, 15, IA2_TEXT_BOUNDARY_WORD,
1274 14, 17, L"x; ");
1276 // Words with numbers should be treated like ordinary words.
1277 CheckTextAtOffset(input_text, 17, IA2_TEXT_BOUNDARY_WORD,
1278 17, 24, L"WWW33) ");
1279 CheckTextAtOffset(input_text, 23, IA2_TEXT_BOUNDARY_WORD,
1280 17, 24, L"WWW33) ");
1282 // Multiple trailing empty spaces should be part of the word preceding it.
1283 CheckTextAtOffset(input_text, 28, IA2_TEXT_BOUNDARY_WORD,
1284 24, 33, L"WebKit \"");
1285 CheckTextAtOffset(input_text, 31, IA2_TEXT_BOUNDARY_WORD,
1286 24, 33, L"WebKit \"");
1287 CheckTextAtOffset(input_text, 32, IA2_TEXT_BOUNDARY_WORD,
1288 24, 33, L"WebKit \"");
1290 // Leading punctuation such as quotation marks should not be part of the word.
1291 CheckTextAtOffset(input_text, 33, IA2_TEXT_BOUNDARY_WORD,
1292 33, 40, L"KHTML, ");
1293 CheckTextAtOffset(input_text, 38, IA2_TEXT_BOUNDARY_WORD,
1294 33, 40, L"KHTML, ");
1296 // Trailing final punctuation should be part of the last word.
1297 CheckTextAtOffset(input_text, 41, IA2_TEXT_BOUNDARY_WORD,
1298 40, CONTENTS_LENGTH, L"like\".");
1299 CheckTextAtOffset(input_text, 45, IA2_TEXT_BOUNDARY_WORD,
1300 40, CONTENTS_LENGTH, L"like\".");
1302 // Test special offsets.
1303 CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET,
1304 IA2_TEXT_BOUNDARY_WORD, 40, CONTENTS_LENGTH, L"like\".");
1307 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1308 DISABLED_TestMultiLineTextAtOffsetWithBoundaryWord) {
1309 base::win::ScopedComPtr<IAccessibleText> textarea_text;
1310 SetUpTextareaField(&textarea_text);
1312 // Trailing punctuation should be included as part of the previous word.
1313 CheckTextAtOffset(textarea_text, 0, IA2_TEXT_BOUNDARY_WORD,
1314 0, 4, L"Moz/");
1315 CheckTextAtOffset(textarea_text, 2, IA2_TEXT_BOUNDARY_WORD,
1316 0, 4, L"Moz/");
1318 // If the offset is at the punctuation, it should return
1319 // the previous word.
1320 CheckTextAtOffset(textarea_text, 3, IA2_TEXT_BOUNDARY_WORD,
1321 0, 4, L"Moz/");
1323 // Numbers with a decimal point ("." for U.S), should be treated as one word.
1324 // Also, trailing punctuation that occurs after empty space should be part of
1325 // the word. ("5.0 (" and not "5.0 ".)
1326 CheckTextAtOffset(textarea_text, 4, IA2_TEXT_BOUNDARY_WORD,
1327 4, 9, L"5.0 (");
1328 CheckTextAtOffset(textarea_text, 5, IA2_TEXT_BOUNDARY_WORD,
1329 4, 9, L"5.0 (");
1330 CheckTextAtOffset(textarea_text, 6, IA2_TEXT_BOUNDARY_WORD,
1331 4, 9, L"5.0 (");
1332 CheckTextAtOffset(textarea_text, 7, IA2_TEXT_BOUNDARY_WORD,
1333 4, 9, L"5.0 (");
1335 // Leading punctuation should not be included with the word after it.
1336 CheckTextAtOffset(textarea_text, 8, IA2_TEXT_BOUNDARY_WORD,
1337 4, 9, L"5.0 (");
1338 CheckTextAtOffset(textarea_text, 11, IA2_TEXT_BOUNDARY_WORD,
1339 9, 12, L"ST ");
1341 // Numbers separated from letters with trailing punctuation should
1342 // be split into two words. Same for abreviations like "i.e.".
1343 CheckTextAtOffset(textarea_text, 12, IA2_TEXT_BOUNDARY_WORD,
1344 12, 14, L"6.");
1345 CheckTextAtOffset(textarea_text, 15, IA2_TEXT_BOUNDARY_WORD,
1346 14, 17, L"x; ");
1348 // Words with numbers should be treated like ordinary words.
1349 CheckTextAtOffset(textarea_text, 17, IA2_TEXT_BOUNDARY_WORD,
1350 17, 24, L"WWW33)\n");
1351 CheckTextAtOffset(textarea_text, 23, IA2_TEXT_BOUNDARY_WORD,
1352 17, 24, L"WWW33)\n");
1354 // Multiple trailing empty spaces should be part of the word preceding it.
1355 CheckTextAtOffset(textarea_text, 28, IA2_TEXT_BOUNDARY_WORD,
1356 24, 33, L"WebKit \n\"");
1357 CheckTextAtOffset(textarea_text, 31, IA2_TEXT_BOUNDARY_WORD,
1358 24, 33, L"WebKit \n\"");
1359 CheckTextAtOffset(textarea_text, 32, IA2_TEXT_BOUNDARY_WORD,
1360 24, 33, L"WebKit \n\"");
1362 // Leading punctuation such as quotation marks should not be part of the word.
1363 CheckTextAtOffset(textarea_text, 33, IA2_TEXT_BOUNDARY_WORD,
1364 33, 40, L"KHTML, ");
1365 CheckTextAtOffset(textarea_text, 38, IA2_TEXT_BOUNDARY_WORD,
1366 33, 40, L"KHTML, ");
1368 // Trailing final punctuation should be part of the last word.
1369 CheckTextAtOffset(textarea_text, 41, IA2_TEXT_BOUNDARY_WORD,
1370 40, CONTENTS_LENGTH, L"like\".");
1371 CheckTextAtOffset(textarea_text, 45, IA2_TEXT_BOUNDARY_WORD,
1372 40, CONTENTS_LENGTH, L"like\".");
1374 // Test special offsets.
1375 CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET,
1376 IA2_TEXT_BOUNDARY_WORD, 40, CONTENTS_LENGTH, L"like\".");
1379 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1380 TestTextAtOffsetWithBoundarySentence) {
1381 base::win::ScopedComPtr<IAccessibleText> input_text;
1382 SetUpInputField(&input_text);
1384 // Sentence navigation is not currently implemented.
1385 LONG start_offset = 0;
1386 LONG end_offset = 0;
1387 base::win::ScopedBstr text;
1388 HRESULT hr = input_text->get_textAtOffset(
1389 5, IA2_TEXT_BOUNDARY_SENTENCE,
1390 &start_offset, &end_offset, text.Receive());
1391 EXPECT_EQ(S_FALSE, hr);
1394 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1395 TestMultiLineTextAtOffsetWithBoundarySentence) {
1396 base::win::ScopedComPtr<IAccessibleText> textarea_text;
1397 SetUpTextareaField(&textarea_text);
1399 // Sentence navigation is not currently implemented.
1400 LONG start_offset = 0;
1401 LONG end_offset = 0;
1402 base::win::ScopedBstr text;
1403 HRESULT hr = textarea_text->get_textAtOffset(
1404 25, IA2_TEXT_BOUNDARY_SENTENCE,
1405 &start_offset, &end_offset, text.Receive());
1406 EXPECT_EQ(S_FALSE, hr);
1409 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1410 TestTextAtOffsetWithBoundaryLine) {
1411 base::win::ScopedComPtr<IAccessibleText> input_text;
1412 SetUpInputField(&input_text);
1414 // Single line text fields should return the whole text.
1415 CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_LINE,
1416 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS));
1418 // Test special offsets.
1419 CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_LINE,
1420 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS));
1421 CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, IA2_TEXT_BOUNDARY_LINE,
1422 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS));
1425 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1426 DISABLED_TestMultiLineTextAtOffsetWithBoundaryLine) {
1427 base::win::ScopedComPtr<IAccessibleText> textarea_text;
1428 SetUpTextareaField(&textarea_text);
1430 CheckTextAtOffset(textarea_text, 0, IA2_TEXT_BOUNDARY_LINE,
1431 0, 24, L"Moz/5.0 (ST 6.x; WWW33)\n");
1433 // If the offset is at the newline, return the line preceding it.
1434 CheckTextAtOffset(textarea_text, 31, IA2_TEXT_BOUNDARY_LINE,
1435 24, 32, L"WebKit \n");
1437 // Last line does not have a trailing newline.
1438 CheckTextAtOffset(textarea_text, 32, IA2_TEXT_BOUNDARY_LINE,
1439 32, CONTENTS_LENGTH, L"\"KHTML, like\".");
1441 // An offset one past the last character should return the last line.
1442 CheckTextAtOffset(textarea_text, CONTENTS_LENGTH, IA2_TEXT_BOUNDARY_LINE,
1443 32, CONTENTS_LENGTH, L"\"KHTML, like\".");
1445 // Test special offsets.
1446 CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_LENGTH,
1447 IA2_TEXT_BOUNDARY_LINE, 32, CONTENTS_LENGTH, L"\"KHTML, like\".");
1448 CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET,
1449 IA2_TEXT_BOUNDARY_LINE, 32, CONTENTS_LENGTH, L"\"KHTML, like\".");
1452 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1453 TestTextAtOffsetWithBoundaryAll) {
1454 base::win::ScopedComPtr<IAccessibleText> input_text;
1455 SetUpInputField(&input_text);
1457 CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_ALL,
1458 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS));
1461 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1462 TestMultiLineTextAtOffsetWithBoundaryAll) {
1463 base::win::ScopedComPtr<IAccessibleText> textarea_text;
1464 SetUpTextareaField(&textarea_text);
1466 CheckTextAtOffset(textarea_text, CONTENTS_LENGTH - 1, IA2_TEXT_BOUNDARY_ALL,
1467 0, CONTENTS_LENGTH, base::SysUTF8ToWide(TEXTAREA_CONTENTS));
1470 } // namespace content