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.
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"
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
{
50 AccessibilityWinBrowserTest();
51 ~AccessibilityWinBrowserTest() override
;
54 class AccessibleChecker
;
55 void LoadInitialAccessibilityTreeFromHtml(const std::string
& html
);
56 IAccessible
* GetRendererAccessible();
57 void ExecuteScript(const std::wstring
& script
);
59 base::win::ScopedComPtr
<IAccessibleText
>* input_text
);
60 void SetUpTextareaField(
61 base::win::ScopedComPtr
<IAccessibleText
>* textarea_text
);
63 static base::win::ScopedComPtr
<IAccessible
> GetAccessibleFromVariant(
66 static HRESULT
QueryIAccessible2(IAccessible
* accessible
,
67 IAccessible2
** accessible2
);
68 static void FindNodeInAccessibilityTree(IAccessible
* node
,
70 const std::wstring
& expected_name
,
73 static void CheckTextAtOffset(
74 base::win::ScopedComPtr
<IAccessibleText
>& element
,
76 IA2TextBoundaryType boundary_type
,
77 LONG expected_start_offset
,
78 LONG expected_end_offset
,
79 const std::wstring
& expected_text
);
80 static std::vector
<base::win::ScopedVariant
> GetAllAccessibleChildren(
81 IAccessible
* element
);
84 DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest
);
87 AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() {
90 AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() {
93 void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml(
94 const std::string
& html
) {
95 AccessibilityNotificationWaiter
waiter(
96 shell(), AccessibilityModeComplete
,
97 ui::AX_EVENT_LOAD_COMPLETE
);
98 GURL
html_data_url("data:text/html," + html
);
99 NavigateToURL(shell(), html_data_url
);
100 waiter
.WaitForNotification();
103 // Retrieve the MSAA client accessibility object for the Render Widget Host View
104 // of the selected tab.
105 IAccessible
* AccessibilityWinBrowserTest::GetRendererAccessible() {
106 content::WebContents
* web_contents
= shell()->web_contents();
107 return web_contents
->GetRenderWidgetHostView()->GetNativeViewAccessible();
110 void AccessibilityWinBrowserTest::ExecuteScript(const std::wstring
& script
) {
111 shell()->web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(script
);
114 // Loads a page with an input text field and places sample text in it. Also,
115 // places the caret on the last character.
116 void AccessibilityWinBrowserTest::SetUpInputField(
117 base::win::ScopedComPtr
<IAccessibleText
>* input_text
) {
118 ASSERT_NE(nullptr, input_text
);
119 LoadInitialAccessibilityTreeFromHtml(std::string("<!DOCTYPE html><html><body>"
120 "<form><label for='textField'>Browser name:</label>"
121 "<input type='text' id='textField' name='name' value='") +
122 net::EscapeQueryParamValue(INPUT_CONTENTS
, false) + std::string(
123 "'></form></body></html>"));
125 // Retrieve the IAccessible interface for the web page.
126 base::win::ScopedComPtr
<IAccessible
> document(GetRendererAccessible());
127 std::vector
<base::win::ScopedVariant
> document_children
=
128 GetAllAccessibleChildren(document
.get());
129 ASSERT_EQ(1, document_children
.size());
131 base::win::ScopedComPtr
<IAccessible2
> form
;
132 HRESULT hr
= QueryIAccessible2(GetAccessibleFromVariant(
133 document
.get(), document_children
[0].AsInput()).get(), form
.Receive());
135 std::vector
<base::win::ScopedVariant
> form_children
=
136 GetAllAccessibleChildren(form
.get());
137 ASSERT_EQ(2, form_children
.size());
139 // Find the input text field.
140 base::win::ScopedComPtr
<IAccessible2
> input
;
141 hr
= QueryIAccessible2(GetAccessibleFromVariant(
142 form
.get(), form_children
[1].AsInput()).get(), input
.Receive());
145 hr
= input
->role(&input_role
);
147 ASSERT_EQ(ROLE_SYSTEM_TEXT
, input_role
);
149 // Retrieve the IAccessibleText interface for the field.
150 hr
= input
.QueryInterface(input_text
->Receive());
153 // Set the caret on the last character.
154 AccessibilityNotificationWaiter
waiter(
155 shell(), AccessibilityModeComplete
,
156 ui::AX_EVENT_TEXT_SELECTION_CHANGED
);
157 std::wstring caret_offset
= base::UTF16ToWide(base::IntToString16(
158 static_cast<int>(CONTENTS_LENGTH
- 1)));
159 ExecuteScript(std::wstring(
160 L
"var textField = document.getElementById('textField');"
161 L
"textField.focus();"
162 L
"textField.setSelectionRange(") +
163 caret_offset
+ L
"," + caret_offset
+ L
");");
164 waiter
.WaitForNotification();
167 // Loads a page with a textarea text field and places sample text in it. Also,
168 // places the caret on the last character.
169 void AccessibilityWinBrowserTest::SetUpTextareaField(
170 base::win::ScopedComPtr
<IAccessibleText
>* textarea_text
) {
171 ASSERT_NE(nullptr, textarea_text
);
172 LoadInitialAccessibilityTreeFromHtml(std::string("<!DOCTYPE html><html><body>"
173 "<textarea id='textField' rows='3' cols='60'>") +
174 net::EscapeQueryParamValue(TEXTAREA_CONTENTS
, false) + std::string(
175 "</textarea></body></html>"));
177 // Retrieve the IAccessible interface for the web page.
178 base::win::ScopedComPtr
<IAccessible
> document(GetRendererAccessible());
179 std::vector
<base::win::ScopedVariant
> document_children
=
180 GetAllAccessibleChildren(document
.get());
181 ASSERT_EQ(1, document_children
.size());
183 base::win::ScopedComPtr
<IAccessible2
> section
;
184 HRESULT hr
= QueryIAccessible2(GetAccessibleFromVariant(
185 document
.get(), document_children
[0].AsInput()).get(), section
.Receive());
187 std::vector
<base::win::ScopedVariant
> section_children
=
188 GetAllAccessibleChildren(section
.get());
189 ASSERT_EQ(1, section_children
.size());
191 // Find the textarea text field.
192 base::win::ScopedComPtr
<IAccessible2
> textarea
;
193 hr
= QueryIAccessible2(GetAccessibleFromVariant(
194 section
.get(), section_children
[0].AsInput()).get(), textarea
.Receive());
196 LONG textarea_role
= 0;
197 hr
= textarea
->role(&textarea_role
);
199 ASSERT_EQ(ROLE_SYSTEM_TEXT
, textarea_role
);
201 // Retrieve the IAccessibleText interface for the field.
202 hr
= textarea
.QueryInterface(textarea_text
->Receive());
205 // Set the caret on the last character.
206 AccessibilityNotificationWaiter
waiter(
207 shell(), AccessibilityModeComplete
,
208 ui::AX_EVENT_TEXT_SELECTION_CHANGED
);
209 std::wstring caret_offset
= base::UTF16ToWide(base::IntToString16(
210 static_cast<int>(CONTENTS_LENGTH
- 1)));
211 ExecuteScript(std::wstring(
212 L
"var textField = document.getElementById('textField');"
213 L
"textField.focus();"
214 L
"textField.setSelectionRange(") +
215 caret_offset
+ L
"," + caret_offset
+ L
");");
216 waiter
.WaitForNotification();
220 // Static helpers ------------------------------------------------
222 base::win::ScopedComPtr
<IAccessible
>
223 AccessibilityWinBrowserTest::GetAccessibleFromVariant(
226 base::win::ScopedComPtr
<IAccessible
> ptr
;
229 IDispatch
* dispatch
= V_DISPATCH(var
);
231 dispatch
->QueryInterface(ptr
.Receive());
236 base::win::ScopedComPtr
<IDispatch
> dispatch
;
237 HRESULT hr
= parent
->get_accChild(*var
, dispatch
.Receive());
238 EXPECT_TRUE(SUCCEEDED(hr
));
240 dispatch
.QueryInterface(ptr
.Receive());
247 HRESULT
AccessibilityWinBrowserTest::QueryIAccessible2(
248 IAccessible
* accessible
,
249 IAccessible2
** accessible2
) {
250 // IA2 Spec dictates that IServiceProvider should be used instead of
251 // QueryInterface when retrieving IAccessible2.
252 base::win::ScopedComPtr
<IServiceProvider
> service_provider
;
253 HRESULT hr
= accessible
->QueryInterface(service_provider
.Receive());
254 return SUCCEEDED(hr
) ?
255 service_provider
->QueryService(IID_IAccessible2
, accessible2
) : hr
;
258 // Recursively search through all of the descendants reachable from an
259 // IAccessible node and return true if we find one with the given role
261 void AccessibilityWinBrowserTest::FindNodeInAccessibilityTree(
264 const std::wstring
& expected_name
,
267 base::win::ScopedBstr name_bstr
;
268 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
269 node
->get_accName(childid_self
, name_bstr
.Receive());
270 std::wstring
name(name_bstr
, name_bstr
.Length());
271 base::win::ScopedVariant role
;
272 node
->get_accRole(childid_self
, role
.Receive());
273 ASSERT_EQ(VT_I4
, role
.type());
275 // Print the accessibility tree as we go, because if this test fails
276 // on the bots, this is really helpful in figuring out why.
277 for (int i
= 0; i
< depth
; i
++)
279 printf("role=%s name=%s\n",
280 base::WideToUTF8(IAccessibleRoleToString(V_I4(role
.ptr()))).c_str(),
281 base::WideToUTF8(name
).c_str());
283 if (expected_role
== V_I4(role
.ptr()) && expected_name
== name
) {
288 std::vector
<base::win::ScopedVariant
> children
= GetAllAccessibleChildren(
290 for (size_t i
= 0; i
< children
.size(); ++i
) {
291 base::win::ScopedComPtr
<IAccessible
> child_accessible(
292 GetAccessibleFromVariant(node
, children
[i
].AsInput()));
293 if (child_accessible
) {
294 FindNodeInAccessibilityTree(
295 child_accessible
.get(), expected_role
, expected_name
, depth
+ 1,
303 // Ensures that the text and the start and end offsets retrieved using
304 // get_textAtOffset match the expected values.
305 void AccessibilityWinBrowserTest::CheckTextAtOffset(
306 base::win::ScopedComPtr
<IAccessibleText
>& element
,
308 IA2TextBoundaryType boundary_type
,
309 LONG expected_start_offset
,
310 LONG expected_end_offset
,
311 const std::wstring
& expected_text
) {
312 testing::Message message
;
313 message
<< "While checking for \'" << expected_text
<< "\' at " <<
314 expected_start_offset
<< '-' << expected_end_offset
<< '.';
315 SCOPED_TRACE(message
);
317 LONG start_offset
= 0;
319 base::win::ScopedBstr text
;
320 HRESULT hr
= element
->get_textAtOffset(
321 offset
, boundary_type
,
322 &start_offset
, &end_offset
, text
.Receive());
324 EXPECT_EQ(expected_start_offset
, start_offset
);
325 EXPECT_EQ(expected_end_offset
, end_offset
);
326 EXPECT_EQ(expected_text
, std::wstring(text
, text
.Length()));
329 std::vector
<base::win::ScopedVariant
>
330 AccessibilityWinBrowserTest::GetAllAccessibleChildren(
331 IAccessible
* element
) {
332 LONG child_count
= 0;
333 HRESULT hr
= element
->get_accChildCount(&child_count
);
335 if (child_count
<= 0)
336 return std::vector
<base::win::ScopedVariant
>();
338 scoped_ptr
<VARIANT
[]> children_array(new VARIANT
[child_count
]);
339 LONG obtained_count
= 0;
340 hr
= AccessibleChildren(
341 element
, 0, child_count
, children_array
.get(), &obtained_count
);
343 EXPECT_EQ(child_count
, obtained_count
);
345 std::vector
<base::win::ScopedVariant
> children(
346 static_cast<size_t>(child_count
));
347 for (size_t i
= 0; i
< children
.size(); i
++) {
348 children
[i
].Reset(children_array
[i
]);
354 // AccessibleChecker ----------------------------------------------------------
356 class AccessibilityWinBrowserTest::AccessibleChecker
{
358 // This constructor can be used if the IA2 role will be the same as the MSAA
360 AccessibleChecker(const std::wstring
& expected_name
,
362 const std::wstring
& expected_value
);
363 AccessibleChecker(const std::wstring
& expected_name
,
365 int32 expected_ia2_role
,
366 const std::wstring
& expected_value
);
367 AccessibleChecker(const std::wstring
& expected_name
,
368 const std::wstring
& expected_role
,
369 int32 expected_ia2_role
,
370 const std::wstring
& expected_value
);
372 // Append an AccessibleChecker that verifies accessibility information for
373 // a child IAccessible. Order is important.
374 void AppendExpectedChild(AccessibleChecker
* expected_child
);
376 // Check that the name and role of the given IAccessible instance and its
377 // descendants match the expected names and roles that this object was
379 void CheckAccessible(IAccessible
* accessible
);
381 // Set the expected value for this AccessibleChecker.
382 void SetExpectedValue(const std::wstring
& expected_value
);
384 // Set the expected state for this AccessibleChecker.
385 void SetExpectedState(LONG expected_state
);
388 typedef std::vector
<AccessibleChecker
*> AccessibleCheckerVector
;
390 void CheckAccessibleName(IAccessible
* accessible
);
391 void CheckAccessibleRole(IAccessible
* accessible
);
392 void CheckIA2Role(IAccessible
* accessible
);
393 void CheckAccessibleValue(IAccessible
* accessible
);
394 void CheckAccessibleState(IAccessible
* accessible
);
395 void CheckAccessibleChildren(IAccessible
* accessible
);
396 base::string16
RoleVariantToString(const base::win::ScopedVariant
& role
);
398 // Expected accessible name. Checked against IAccessible::get_accName.
401 // Expected accessible role. Checked against IAccessible::get_accRole.
402 base::win::ScopedVariant role_
;
404 // Expected IAccessible2 role. Checked against IAccessible2::role.
407 // Expected accessible value. Checked against IAccessible::get_accValue.
410 // Expected accessible state. Checked against IAccessible::get_accState.
413 // Expected accessible children. Checked using IAccessible::get_accChildCount
414 // and ::AccessibleChildren.
415 AccessibleCheckerVector children_
;
418 AccessibilityWinBrowserTest::AccessibleChecker::AccessibleChecker(
419 const std::wstring
& expected_name
,
421 const std::wstring
& expected_value
)
422 : name_(expected_name
),
423 role_(expected_role
),
424 ia2_role_(expected_role
),
425 value_(expected_value
),
429 AccessibilityWinBrowserTest::AccessibleChecker::AccessibleChecker(
430 const std::wstring
& expected_name
,
432 int32 expected_ia2_role
,
433 const std::wstring
& expected_value
)
434 : name_(expected_name
),
435 role_(expected_role
),
436 ia2_role_(expected_ia2_role
),
437 value_(expected_value
),
441 AccessibilityWinBrowserTest::AccessibleChecker::AccessibleChecker(
442 const std::wstring
& expected_name
,
443 const std::wstring
& expected_role
,
444 int32 expected_ia2_role
,
445 const std::wstring
& expected_value
)
446 : name_(expected_name
),
447 role_(expected_role
.c_str()),
448 ia2_role_(expected_ia2_role
),
449 value_(expected_value
),
453 void AccessibilityWinBrowserTest::AccessibleChecker::AppendExpectedChild(
454 AccessibleChecker
* expected_child
) {
455 children_
.push_back(expected_child
);
458 void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessible(
459 IAccessible
* accessible
) {
460 SCOPED_TRACE("While checking "
461 + base::UTF16ToUTF8(RoleVariantToString(role_
)));
462 CheckAccessibleName(accessible
);
463 CheckAccessibleRole(accessible
);
464 CheckIA2Role(accessible
);
465 CheckAccessibleValue(accessible
);
466 CheckAccessibleState(accessible
);
467 CheckAccessibleChildren(accessible
);
470 void AccessibilityWinBrowserTest::AccessibleChecker::SetExpectedValue(
471 const std::wstring
& expected_value
) {
472 value_
= expected_value
;
475 void AccessibilityWinBrowserTest::AccessibleChecker::SetExpectedState(
476 LONG expected_state
) {
477 state_
= expected_state
;
480 void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleName(
481 IAccessible
* accessible
) {
482 base::win::ScopedBstr name
;
483 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
484 HRESULT hr
= accessible
->get_accName(childid_self
, name
.Receive());
487 // If the object doesn't have name S_FALSE should be returned.
488 EXPECT_EQ(S_FALSE
, hr
);
490 // Test that the correct string was returned.
492 EXPECT_EQ(name_
, std::wstring(name
, name
.Length()));
496 void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleRole(
497 IAccessible
* accessible
) {
498 base::win::ScopedVariant role
;
499 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
500 HRESULT hr
= accessible
->get_accRole(childid_self
, role
.Receive());
502 EXPECT_EQ(0, role_
.Compare(role
))
503 << "Expected role: " << RoleVariantToString(role_
)
504 << "\nGot role: " << RoleVariantToString(role
);
507 void AccessibilityWinBrowserTest::AccessibleChecker::CheckIA2Role(
508 IAccessible
* accessible
) {
509 base::win::ScopedComPtr
<IAccessible2
> accessible2
;
510 HRESULT hr
= QueryIAccessible2(accessible
, accessible2
.Receive());
513 hr
= accessible2
->role(&ia2_role
);
515 EXPECT_EQ(ia2_role_
, ia2_role
)
516 << "Expected ia2 role: " << IAccessible2RoleToString(ia2_role_
)
517 << "\nGot ia2 role: " << IAccessible2RoleToString(ia2_role
);
520 void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleValue(
521 IAccessible
* accessible
) {
522 // Don't check the value if if's a DOCUMENT role, because the value
523 // is supposed to be the url (and we don't keep track of that in the
524 // test expectations).
525 base::win::ScopedVariant role
;
526 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
527 HRESULT hr
= accessible
->get_accRole(childid_self
, role
.Receive());
529 if (role
.type() == VT_I4
&& V_I4(role
.ptr()) == ROLE_SYSTEM_DOCUMENT
)
533 base::win::ScopedBstr value
;
534 hr
= accessible
->get_accValue(childid_self
, value
.Receive());
537 // Test that the correct string was returned.
538 EXPECT_EQ(value_
, std::wstring(value
, value
.Length()));
541 void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleState(
542 IAccessible
* accessible
) {
546 base::win::ScopedVariant state
;
547 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
548 HRESULT hr
= accessible
->get_accState(childid_self
, state
.Receive());
550 ASSERT_EQ(VT_I4
, state
.type());
551 LONG obj_state
= V_I4(state
.ptr());
552 // Avoid flakiness. The "offscreen" state depends on whether the browser
553 // window is frontmost or not, and "hottracked" depends on whether the
554 // mouse cursor happens to be over the element.
555 obj_state
&= ~(STATE_SYSTEM_OFFSCREEN
| STATE_SYSTEM_HOTTRACKED
);
556 EXPECT_EQ(state_
, obj_state
)
557 << "Expected state: " << IAccessibleStateToString(state_
)
558 << "\nGot state: " << IAccessibleStateToString(obj_state
);
561 void AccessibilityWinBrowserTest::AccessibleChecker::CheckAccessibleChildren(
562 IAccessible
* parent
) {
563 std::vector
<base::win::ScopedVariant
> obtained_children
=
564 GetAllAccessibleChildren(parent
);
565 size_t child_count
= obtained_children
.size();
566 ASSERT_EQ(child_count
, children_
.size());
568 AccessibleCheckerVector::iterator child_checker
;
569 std::vector
<base::win::ScopedVariant
>::iterator child
;
570 for (child_checker
= children_
.begin(),
571 child
= obtained_children
.begin();
572 child_checker
!= children_
.end()
573 && child
!= obtained_children
.end();
574 ++child_checker
, ++child
) {
575 base::win::ScopedComPtr
<IAccessible
> child_accessible(
576 GetAccessibleFromVariant(parent
, child
->AsInput()));
577 ASSERT_TRUE(child_accessible
.get());
578 (*child_checker
)->CheckAccessible(child_accessible
.get());
583 AccessibilityWinBrowserTest::AccessibleChecker::RoleVariantToString(
584 const base::win::ScopedVariant
& role
) {
585 if (role
.type() == VT_I4
)
586 return IAccessibleRoleToString(V_I4(role
.ptr()));
587 if (role
.type() == VT_BSTR
)
588 return base::string16(V_BSTR(role
.ptr()), SysStringLen(V_BSTR(role
.ptr())));
589 return base::string16();
595 // Tests ----------------------------------------------------------------------
597 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
598 TestBusyAccessibilityTree
) {
599 if (GetBaseAccessibilityMode() != AccessibilityModeOff
)
602 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
604 // The initial accessible returned should have state STATE_SYSTEM_BUSY while
605 // the accessibility tree is being requested from the renderer.
606 AccessibleChecker
document1_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
608 document1_checker
.SetExpectedState(
609 STATE_SYSTEM_READONLY
| STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_FOCUSED
|
611 document1_checker
.CheckAccessible(GetRendererAccessible());
614 // Periodically failing. See crbug.com/145537
615 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
616 DISABLED_TestNotificationActiveDescendantChanged
) {
617 LoadInitialAccessibilityTreeFromHtml(
618 "<ul tabindex='-1' role='radiogroup' aria-label='ul'>"
619 "<li id='li'>li</li></ul>");
621 // Check the browser's copy of the renderer accessibility tree.
622 AccessibleChecker
list_marker_checker(L
"\x2022", ROLE_SYSTEM_TEXT
,
624 AccessibleChecker
static_text_checker(L
"li", ROLE_SYSTEM_TEXT
,
626 AccessibleChecker
list_item_checker(std::wstring(), ROLE_SYSTEM_LISTITEM
,
628 list_item_checker
.SetExpectedState(STATE_SYSTEM_READONLY
);
629 AccessibleChecker
radio_group_checker(L
"ul", ROLE_SYSTEM_GROUPING
,
630 IA2_ROLE_SECTION
, std::wstring());
631 radio_group_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
632 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
634 list_item_checker
.AppendExpectedChild(&list_marker_checker
);
635 list_item_checker
.AppendExpectedChild(&static_text_checker
);
636 radio_group_checker
.AppendExpectedChild(&list_item_checker
);
637 document_checker
.AppendExpectedChild(&radio_group_checker
);
638 document_checker
.CheckAccessible(GetRendererAccessible());
640 // Set focus to the radio group.
641 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
642 new AccessibilityNotificationWaiter(
643 shell(), AccessibilityModeComplete
,
644 ui::AX_EVENT_FOCUS
));
645 ExecuteScript(L
"document.body.children[0].focus()");
646 waiter
->WaitForNotification();
648 // Check that the accessibility tree of the browser has been updated.
649 radio_group_checker
.SetExpectedState(
650 STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_FOCUSED
);
651 document_checker
.CheckAccessible(GetRendererAccessible());
653 // Set the active descendant of the radio group
654 waiter
.reset(new AccessibilityNotificationWaiter(
655 shell(), AccessibilityModeComplete
,
656 ui::AX_EVENT_FOCUS
));
658 L
"document.body.children[0].setAttribute('aria-activedescendant', 'li')");
659 waiter
->WaitForNotification();
661 // Check that the accessibility tree of the browser has been updated.
662 list_item_checker
.SetExpectedState(
663 STATE_SYSTEM_READONLY
| STATE_SYSTEM_FOCUSED
);
664 radio_group_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
665 document_checker
.CheckAccessible(GetRendererAccessible());
668 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
669 TestNotificationCheckedStateChanged
) {
670 LoadInitialAccessibilityTreeFromHtml(
671 "<body><input type='checkbox' /></body>");
673 // Check the browser's copy of the renderer accessibility tree.
674 AccessibleChecker
checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON
,
676 checkbox_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
677 AccessibleChecker
body_checker(std::wstring(), L
"body", IA2_ROLE_SECTION
,
679 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
681 body_checker
.AppendExpectedChild(&checkbox_checker
);
682 document_checker
.AppendExpectedChild(&body_checker
);
683 document_checker
.CheckAccessible(GetRendererAccessible());
685 // Check the checkbox.
686 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
687 new AccessibilityNotificationWaiter(
688 shell(), AccessibilityModeComplete
,
689 ui::AX_EVENT_CHECKED_STATE_CHANGED
));
690 ExecuteScript(L
"document.body.children[0].checked=true");
691 waiter
->WaitForNotification();
693 // Check that the accessibility tree of the browser has been updated.
694 checkbox_checker
.SetExpectedState(
695 STATE_SYSTEM_CHECKED
| STATE_SYSTEM_FOCUSABLE
);
696 document_checker
.CheckAccessible(GetRendererAccessible());
699 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
700 TestNotificationChildrenChanged
) {
701 // The role attribute causes the node to be in the accessibility tree.
702 LoadInitialAccessibilityTreeFromHtml("<body role=group></body>");
704 // Check the browser's copy of the renderer accessibility tree.
705 AccessibleChecker
group_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
707 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
709 document_checker
.AppendExpectedChild(&group_checker
);
710 document_checker
.CheckAccessible(GetRendererAccessible());
712 // Change the children of the document body.
713 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
714 new AccessibilityNotificationWaiter(
716 AccessibilityModeComplete
,
717 ui::AX_EVENT_CHILDREN_CHANGED
));
718 ExecuteScript(L
"document.body.innerHTML='<b>new text</b>'");
719 waiter
->WaitForNotification();
721 // Check that the accessibility tree of the browser has been updated.
722 AccessibleChecker
text_checker(
723 L
"new text", ROLE_SYSTEM_STATICTEXT
, std::wstring());
724 group_checker
.AppendExpectedChild(&text_checker
);
725 document_checker
.CheckAccessible(GetRendererAccessible());
728 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
729 TestNotificationChildrenChanged2
) {
730 // The role attribute causes the node to be in the accessibility tree.
731 LoadInitialAccessibilityTreeFromHtml(
732 "<div role=group style='visibility: hidden'>text</div>");
734 // Check the accessible tree of the browser.
735 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
737 document_checker
.CheckAccessible(GetRendererAccessible());
739 // Change the children of the document body.
740 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
741 new AccessibilityNotificationWaiter(
742 shell(), AccessibilityModeComplete
,
743 ui::AX_EVENT_CHILDREN_CHANGED
));
744 ExecuteScript(L
"document.body.children[0].style.visibility='visible'");
745 waiter
->WaitForNotification();
747 // Check that the accessibility tree of the browser has been updated.
748 AccessibleChecker
static_text_checker(L
"text", ROLE_SYSTEM_STATICTEXT
,
750 AccessibleChecker
group_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
752 document_checker
.AppendExpectedChild(&group_checker
);
753 group_checker
.AppendExpectedChild(&static_text_checker
);
754 document_checker
.CheckAccessible(GetRendererAccessible());
757 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
758 TestNotificationFocusChanged
) {
759 // The role attribute causes the node to be in the accessibility tree.
760 LoadInitialAccessibilityTreeFromHtml("<div role=group tabindex='-1'></div>");
762 // Check the browser's copy of the renderer accessibility tree.
763 SCOPED_TRACE("Check initial tree");
764 AccessibleChecker
group_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
766 group_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
767 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
769 document_checker
.AppendExpectedChild(&group_checker
);
770 document_checker
.CheckAccessible(GetRendererAccessible());
772 // Focus the div in the document
773 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
774 new AccessibilityNotificationWaiter(
775 shell(), AccessibilityModeComplete
,
776 ui::AX_EVENT_FOCUS
));
777 ExecuteScript(L
"document.body.children[0].focus()");
778 waiter
->WaitForNotification();
780 // Check that the accessibility tree of the browser has been updated.
781 SCOPED_TRACE("Check updated tree after focusing div");
782 group_checker
.SetExpectedState(
783 STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_FOCUSED
);
784 document_checker
.CheckAccessible(GetRendererAccessible());
786 // Focus the document accessible. This will un-focus the current node.
788 new AccessibilityNotificationWaiter(
789 shell(), AccessibilityModeComplete
,
791 base::win::ScopedComPtr
<IAccessible
> document_accessible(
792 GetRendererAccessible());
793 ASSERT_NE(document_accessible
.get(), reinterpret_cast<IAccessible
*>(NULL
));
794 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
795 HRESULT hr
= document_accessible
->accSelect(SELFLAG_TAKEFOCUS
, childid_self
);
797 waiter
->WaitForNotification();
799 // Check that the accessibility tree of the browser has been updated.
800 SCOPED_TRACE("Check updated tree after focusing document again");
801 group_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
802 document_checker
.CheckAccessible(GetRendererAccessible());
805 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
806 TestNotificationValueChanged
) {
807 LoadInitialAccessibilityTreeFromHtml(
808 "<body><input type='text' value='old value'/></body>");
810 // Check the browser's copy of the renderer accessibility tree.
811 AccessibleChecker
text_field_checker(std::wstring(), ROLE_SYSTEM_TEXT
,
813 text_field_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
814 AccessibleChecker
body_checker(std::wstring(), L
"body", IA2_ROLE_SECTION
,
816 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
818 body_checker
.AppendExpectedChild(&text_field_checker
);
819 document_checker
.AppendExpectedChild(&body_checker
);
820 document_checker
.CheckAccessible(GetRendererAccessible());
822 // Set the value of the text control
823 scoped_ptr
<AccessibilityNotificationWaiter
> waiter(
824 new AccessibilityNotificationWaiter(
825 shell(), AccessibilityModeComplete
,
826 ui::AX_EVENT_VALUE_CHANGED
));
827 ExecuteScript(L
"document.body.children[0].value='new value'");
828 waiter
->WaitForNotification();
830 // Check that the accessibility tree of the browser has been updated.
831 text_field_checker
.SetExpectedValue(L
"new value");
832 document_checker
.CheckAccessible(GetRendererAccessible());
835 // This test verifies that the web content's accessibility tree is a
836 // descendant of the main browser window's accessibility tree, so that
837 // tools like AccExplorer32 or AccProbe can be used to examine Chrome's
838 // accessibility support.
840 // If you made a change and this test now fails, check that the NativeViewHost
841 // that wraps the tab contents returns the IAccessible implementation
842 // provided by RenderWidgetHostViewWin in GetNativeViewAccessible().
843 // flaky: http://crbug.com/402190
844 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
845 DISABLED_ContainsRendererAccessibilityTree
) {
846 LoadInitialAccessibilityTreeFromHtml(
847 "<html><head><title>MyDocument</title></head>"
848 "<body>Content</body></html>");
850 // Get the accessibility object for the window tree host.
851 aura::Window
* window
= shell()->window();
853 aura::WindowTreeHost
* window_tree_host
= window
->GetHost();
854 CHECK(window_tree_host
);
855 HWND hwnd
= window_tree_host
->GetAcceleratedWidget();
857 base::win::ScopedComPtr
<IAccessible
> browser_accessible
;
858 HRESULT hr
= AccessibleObjectFromWindow(
862 reinterpret_cast<void**>(browser_accessible
.Receive()));
866 FindNodeInAccessibilityTree(
867 browser_accessible
.get(), ROLE_SYSTEM_DOCUMENT
, L
"MyDocument", 0, &found
);
868 ASSERT_EQ(found
, true);
871 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
872 SupportsISimpleDOM
) {
873 LoadInitialAccessibilityTreeFromHtml(
874 "<body><input type='checkbox' /></body>");
876 // Get the IAccessible object for the document.
877 base::win::ScopedComPtr
<IAccessible
> document_accessible(
878 GetRendererAccessible());
879 ASSERT_NE(document_accessible
.get(), reinterpret_cast<IAccessible
*>(NULL
));
881 // Get the ISimpleDOM object for the document.
882 base::win::ScopedComPtr
<IServiceProvider
> service_provider
;
883 HRESULT hr
= static_cast<IAccessible
*>(document_accessible
.get())
884 ->QueryInterface(service_provider
.Receive());
886 const GUID refguid
= {0x0c539790,
889 {0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8}};
890 base::win::ScopedComPtr
<ISimpleDOMNode
> document_isimpledomnode
;
891 hr
= static_cast<IServiceProvider
*>(service_provider
.get())
893 refguid
, IID_ISimpleDOMNode
,
894 reinterpret_cast<void**>(document_isimpledomnode
.Receive()));
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
);
907 EXPECT_EQ(NODETYPE_DOCUMENT
, node_type
);
908 EXPECT_EQ(1, num_children
);
912 base::win::ScopedComPtr
<ISimpleDOMNode
> body_isimpledomnode
;
913 hr
= document_isimpledomnode
->get_firstChild(
914 body_isimpledomnode
.Receive());
916 hr
= body_isimpledomnode
->get_nodeInfo(
917 node_name
.Receive(), &name_space_id
, node_value
.Receive(), &num_children
,
918 &unique_id
, &node_type
);
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
);
926 base::win::ScopedComPtr
<ISimpleDOMNode
> checkbox_isimpledomnode
;
927 hr
= body_isimpledomnode
->get_firstChild(
928 checkbox_isimpledomnode
.Receive());
930 hr
= checkbox_isimpledomnode
->get_nodeInfo(
931 node_name
.Receive(), &name_space_id
, node_value
.Receive(), &num_children
,
932 &unique_id
, &node_type
);
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
,
946 AccessibleChecker
grouping2_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
948 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
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
);
963 EXPECT_EQ(CONTENTS_LENGTH
- 1, caret_offset
);
965 AccessibilityNotificationWaiter
waiter(
966 shell(), AccessibilityModeComplete
,
967 ui::AX_EVENT_TEXT_SELECTION_CHANGED
);
969 hr
= input_text
->setCaretOffset(caret_offset
);
971 waiter
.WaitForNotification();
973 hr
= input_text
->get_caretOffset(&caret_offset
);
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
);
986 EXPECT_EQ(CONTENTS_LENGTH
- 1, caret_offset
);
988 AccessibilityNotificationWaiter
waiter(
989 shell(), AccessibilityModeComplete
,
990 ui::AX_EVENT_TEXT_SELECTION_CHANGED
);
992 hr
= textarea_text
->setCaretOffset(caret_offset
);
994 waiter
.WaitForNotification();
996 hr
= textarea_text
->get_caretOffset(&caret_offset
);
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 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
,
1243 CheckTextAtOffset(input_text
, 2, IA2_TEXT_BOUNDARY_WORD
,
1246 // If the offset is at the punctuation, it should return
1247 // the previous word.
1248 CheckTextAtOffset(input_text
, 3, IA2_TEXT_BOUNDARY_WORD
,
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
,
1256 CheckTextAtOffset(input_text
, 5, IA2_TEXT_BOUNDARY_WORD
,
1258 CheckTextAtOffset(input_text
, 6, IA2_TEXT_BOUNDARY_WORD
,
1260 CheckTextAtOffset(input_text
, 7, IA2_TEXT_BOUNDARY_WORD
,
1263 // Leading punctuation should not be included with the word after it.
1264 CheckTextAtOffset(input_text
, 8, IA2_TEXT_BOUNDARY_WORD
,
1266 CheckTextAtOffset(input_text
, 11, IA2_TEXT_BOUNDARY_WORD
,
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
,
1273 CheckTextAtOffset(input_text
, 15, IA2_TEXT_BOUNDARY_WORD
,
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 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
,
1315 CheckTextAtOffset(textarea_text
, 2, IA2_TEXT_BOUNDARY_WORD
,
1318 // If the offset is at the punctuation, it should return
1319 // the previous word.
1320 CheckTextAtOffset(textarea_text
, 3, IA2_TEXT_BOUNDARY_WORD
,
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
,
1328 CheckTextAtOffset(textarea_text
, 5, IA2_TEXT_BOUNDARY_WORD
,
1330 CheckTextAtOffset(textarea_text
, 6, IA2_TEXT_BOUNDARY_WORD
,
1332 CheckTextAtOffset(textarea_text
, 7, IA2_TEXT_BOUNDARY_WORD
,
1335 // Leading punctuation should not be included with the word after it.
1336 CheckTextAtOffset(textarea_text
, 8, IA2_TEXT_BOUNDARY_WORD
,
1338 CheckTextAtOffset(textarea_text
, 11, IA2_TEXT_BOUNDARY_WORD
,
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
,
1345 CheckTextAtOffset(textarea_text
, 15, IA2_TEXT_BOUNDARY_WORD
,
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 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