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 virtual ~AccessibilityWinBrowserTest();
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
);
64 static base::win::ScopedComPtr
<IAccessible
>
65 AccessibilityWinBrowserTest::GetAccessibleFromVariant(
68 static HRESULT
QueryIAccessible2(IAccessible
* accessible
,
69 IAccessible2
** accessible2
);
70 static void FindNodeInAccessibilityTree(IAccessible
* node
,
72 const std::wstring
& expected_name
,
75 static void CheckTextAtOffset(
76 base::win::ScopedComPtr
<IAccessibleText
>& element
,
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
);
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());
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());
147 hr
= input
->role(&input_role
);
149 ASSERT_EQ(ROLE_SYSTEM_TEXT
, input_role
);
151 // Retrieve the IAccessibleText interface for the field.
152 hr
= input
.QueryInterface(input_text
->Receive());
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());
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());
198 LONG textarea_role
= 0;
199 hr
= textarea
->role(&textarea_role
);
201 ASSERT_EQ(ROLE_SYSTEM_TEXT
, textarea_role
);
203 // Retrieve the IAccessibleText interface for the field.
204 hr
= textarea
.QueryInterface(textarea_text
->Receive());
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(
228 base::win::ScopedComPtr
<IAccessible
> ptr
;
231 IDispatch
* dispatch
= V_DISPATCH(var
);
233 dispatch
->QueryInterface(ptr
.Receive());
238 base::win::ScopedComPtr
<IDispatch
> dispatch
;
239 HRESULT hr
= parent
->get_accChild(*var
, dispatch
.Receive());
240 EXPECT_TRUE(SUCCEEDED(hr
));
242 dispatch
.QueryInterface(ptr
.Receive());
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
263 void AccessibilityWinBrowserTest::FindNodeInAccessibilityTree(
266 const std::wstring
& expected_name
,
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
++)
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
) {
290 std::vector
<base::win::ScopedVariant
> children
= GetAllAccessibleChildren(
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,
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
,
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;
321 base::win::ScopedBstr text
;
322 HRESULT hr
= element
->get_textAtOffset(
323 offset
, boundary_type
,
324 &start_offset
, &end_offset
, text
.Receive());
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
);
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
);
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
]);
356 // AccessibleChecker ----------------------------------------------------------
358 class AccessibilityWinBrowserTest::AccessibleChecker
{
360 // This constructor can be used if the IA2 role will be the same as the MSAA
362 AccessibleChecker(const std::wstring
& expected_name
,
364 const std::wstring
& expected_value
);
365 AccessibleChecker(const std::wstring
& expected_name
,
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
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
);
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.
403 // Expected accessible role. Checked against IAccessible::get_accRole.
404 base::win::ScopedVariant role_
;
406 // Expected IAccessible2 role. Checked against IAccessible2::role.
409 // Expected accessible value. Checked against IAccessible::get_accValue.
412 // Expected accessible state. Checked against IAccessible::get_accState.
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
,
423 const std::wstring
& expected_value
)
424 : name_(expected_name
),
425 role_(expected_role
),
426 ia2_role_(expected_role
),
427 value_(expected_value
),
431 AccessibilityWinBrowserTest::AccessibleChecker::AccessibleChecker(
432 const std::wstring
& expected_name
,
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
),
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
),
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());
489 // If the object doesn't have name S_FALSE should be returned.
490 EXPECT_EQ(S_FALSE
, hr
);
492 // Test that the correct string was returned.
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());
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());
515 hr
= accessible2
->role(&ia2_role
);
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());
531 if (role
.type() == VT_I4
&& V_I4(&role
) == ROLE_SYSTEM_DOCUMENT
)
535 base::win::ScopedBstr value
;
536 hr
= accessible
->get_accValue(childid_self
, value
.Receive());
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
) {
548 base::win::ScopedVariant state
;
549 base::win::ScopedVariant
childid_self(CHILDID_SELF
);
550 HRESULT hr
= accessible
->get_accState(childid_self
, state
.Receive());
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());
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();
597 // Tests ----------------------------------------------------------------------
599 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest
,
600 TestBusyAccessibilityTree
) {
601 if (GetBaseAccessibilityMode() != AccessibilityModeOff
)
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
,
610 document1_checker
.SetExpectedState(
611 STATE_SYSTEM_READONLY
| STATE_SYSTEM_FOCUSABLE
| STATE_SYSTEM_FOCUSED
|
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
,
626 AccessibleChecker
static_text_checker(L
"li", ROLE_SYSTEM_TEXT
,
628 AccessibleChecker
list_item_checker(std::wstring(), ROLE_SYSTEM_LISTITEM
,
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
,
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
));
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
,
678 checkbox_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
679 AccessibleChecker
body_checker(std::wstring(), L
"body", IA2_ROLE_SECTION
,
681 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
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
,
709 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
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(
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
,
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
,
752 AccessibleChecker
group_checker(std::wstring(), ROLE_SYSTEM_GROUPING
,
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
,
768 group_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
769 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
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.
790 new AccessibilityNotificationWaiter(
791 shell(), AccessibilityModeComplete
,
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
);
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
,
815 text_field_checker
.SetExpectedState(STATE_SYSTEM_FOCUSABLE
);
816 AccessibleChecker
body_checker(std::wstring(), L
"body", IA2_ROLE_SECTION
,
818 AccessibleChecker
document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT
,
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();
855 aura::WindowTreeHost
* window_tree_host
= window
->GetHost();
856 CHECK(window_tree_host
);
857 HWND hwnd
= window_tree_host
->GetAcceleratedWidget();
859 base::win::ScopedComPtr
<IAccessible
> browser_accessible
;
860 HRESULT hr
= AccessibleObjectFromWindow(
864 reinterpret_cast<void**>(browser_accessible
.Receive()));
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());
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())
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 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
,
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 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
,
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 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