Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / browser / accessibility / accessibility_win_browsertest.cc
blob163ee4ceb986a58c595fc01767a2861850944833
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <vector>
7 #include "base/memory/scoped_ptr.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "base/win/scoped_bstr.h"
11 #include "base/win/scoped_comptr.h"
12 #include "base/win/scoped_variant.h"
13 #include "content/browser/accessibility/accessibility_mode_helper.h"
14 #include "content/browser/accessibility/accessibility_tree_formatter.h"
15 #include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
16 #include "content/browser/renderer_host/render_view_host_impl.h"
17 #include "content/public/browser/notification_service.h"
18 #include "content/public/browser/notification_types.h"
19 #include "content/public/browser/render_frame_host.h"
20 #include "content/public/browser/render_widget_host_view.h"
21 #include "content/public/browser/web_contents.h"
22 #include "content/public/common/url_constants.h"
23 #include "content/public/test/content_browser_test.h"
24 #include "content/public/test/content_browser_test_utils.h"
25 #include "content/shell/browser/shell.h"
26 #include "content/test/accessibility_browser_test_utils.h"
27 #include "net/base/escape.h"
28 #include "third_party/iaccessible2/ia2_api_all.h"
29 #include "third_party/isimpledom/ISimpleDOMNode.h"
30 #include "ui/aura/window.h"
31 #include "ui/aura/window_tree_host.h"
33 namespace content {
35 namespace {
38 const char INPUT_CONTENTS[] = "Moz/5.0 (ST 6.x; WWW33) "
39 "WebKit \"KHTML, like\".";
40 const char TEXTAREA_CONTENTS[] = "Moz/5.0 (ST 6.x; WWW33)\n"
41 "WebKit \n\"KHTML, like\".";
42 const LONG CONTENTS_LENGTH = static_cast<LONG>(
43 (sizeof(INPUT_CONTENTS) - 1) / sizeof(char));
46 // AccessibilityWinBrowserTest ------------------------------------------------
48 class AccessibilityWinBrowserTest : public ContentBrowserTest {
49 public:
50 AccessibilityWinBrowserTest();
51 ~AccessibilityWinBrowserTest() override;
53 protected:
54 class AccessibleChecker;
55 void LoadInitialAccessibilityTreeFromHtml(const std::string& html);
56 IAccessible* GetRendererAccessible();
57 void ExecuteScript(const std::wstring& script);
58 void SetUpInputField(
59 base::win::ScopedComPtr<IAccessibleText>* input_text);
60 void SetUpTextareaField(
61 base::win::ScopedComPtr<IAccessibleText>* textarea_text);
63 static base::win::ScopedComPtr<IAccessible> GetAccessibleFromVariant(
64 IAccessible* parent,
65 VARIANT* var);
66 static HRESULT QueryIAccessible2(IAccessible* accessible,
67 IAccessible2** accessible2);
68 static void FindNodeInAccessibilityTree(IAccessible* node,
69 int32 expected_role,
70 const std::wstring& expected_name,
71 int32 depth,
72 bool* found);
73 static void CheckTextAtOffset(
74 base::win::ScopedComPtr<IAccessibleText>& element,
75 LONG offset,
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);
83 private:
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());
134 ASSERT_EQ(S_OK, hr);
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());
143 ASSERT_EQ(S_OK, hr);
144 LONG input_role = 0;
145 hr = input->role(&input_role);
146 ASSERT_EQ(S_OK, hr);
147 ASSERT_EQ(ROLE_SYSTEM_TEXT, input_role);
149 // Retrieve the IAccessibleText interface for the field.
150 hr = input.QueryInterface(input_text->Receive());
151 ASSERT_EQ(S_OK, hr);
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());
186 ASSERT_EQ(S_OK, hr);
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());
195 ASSERT_EQ(S_OK, hr);
196 LONG textarea_role = 0;
197 hr = textarea->role(&textarea_role);
198 ASSERT_EQ(S_OK, hr);
199 ASSERT_EQ(ROLE_SYSTEM_TEXT, textarea_role);
201 // Retrieve the IAccessibleText interface for the field.
202 hr = textarea.QueryInterface(textarea_text->Receive());
203 ASSERT_EQ(S_OK, hr);
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(
224 IAccessible* parent,
225 VARIANT* var) {
226 base::win::ScopedComPtr<IAccessible> ptr;
227 switch (V_VT(var)) {
228 case VT_DISPATCH: {
229 IDispatch* dispatch = V_DISPATCH(var);
230 if (dispatch)
231 dispatch->QueryInterface(ptr.Receive());
232 break;
235 case VT_I4: {
236 base::win::ScopedComPtr<IDispatch> dispatch;
237 HRESULT hr = parent->get_accChild(*var, dispatch.Receive());
238 EXPECT_TRUE(SUCCEEDED(hr));
239 if (dispatch.get())
240 dispatch.QueryInterface(ptr.Receive());
241 break;
244 return ptr;
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
260 // and name.
261 void AccessibilityWinBrowserTest::FindNodeInAccessibilityTree(
262 IAccessible* node,
263 int32 expected_role,
264 const std::wstring& expected_name,
265 int32 depth,
266 bool* found) {
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++)
278 printf(" ");
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) {
284 *found = true;
285 return;
288 std::vector<base::win::ScopedVariant> children = GetAllAccessibleChildren(
289 node);
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,
296 found);
297 if (*found)
298 return;
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,
307 LONG offset,
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;
318 LONG end_offset = 0;
319 base::win::ScopedBstr text;
320 HRESULT hr = element->get_textAtOffset(
321 offset, boundary_type,
322 &start_offset, &end_offset, text.Receive());
323 EXPECT_EQ(S_OK, hr);
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);
334 EXPECT_EQ(S_OK, hr);
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);
342 EXPECT_EQ(S_OK, hr);
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]);
350 return children;
354 // AccessibleChecker ----------------------------------------------------------
356 class AccessibilityWinBrowserTest::AccessibleChecker {
357 public:
358 // This constructor can be used if the IA2 role will be the same as the MSAA
359 // role.
360 AccessibleChecker(const std::wstring& expected_name,
361 int32 expected_role,
362 const std::wstring& expected_value);
363 AccessibleChecker(const std::wstring& expected_name,
364 int32 expected_role,
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
378 // initialized with.
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);
387 private:
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.
399 std::wstring name_;
401 // Expected accessible role. Checked against IAccessible::get_accRole.
402 base::win::ScopedVariant role_;
404 // Expected IAccessible2 role. Checked against IAccessible2::role.
405 int32 ia2_role_;
407 // Expected accessible value. Checked against IAccessible::get_accValue.
408 std::wstring value_;
410 // Expected accessible state. Checked against IAccessible::get_accState.
411 LONG state_;
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,
420 int32 expected_role,
421 const std::wstring& expected_value)
422 : name_(expected_name),
423 role_(expected_role),
424 ia2_role_(expected_role),
425 value_(expected_value),
426 state_(-1) {
429 AccessibilityWinBrowserTest::AccessibleChecker::AccessibleChecker(
430 const std::wstring& expected_name,
431 int32 expected_role,
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),
438 state_(-1) {
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),
450 state_(-1) {
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());
486 if (name_.empty()) {
487 // If the object doesn't have name S_FALSE should be returned.
488 EXPECT_EQ(S_FALSE, hr);
489 } else {
490 // Test that the correct string was returned.
491 EXPECT_EQ(S_OK, hr);
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());
501 ASSERT_EQ(S_OK, hr);
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());
511 ASSERT_EQ(S_OK, hr);
512 long ia2_role = 0;
513 hr = accessible2->role(&ia2_role);
514 ASSERT_EQ(S_OK, hr);
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());
528 ASSERT_EQ(S_OK, hr);
529 if (role.type() == VT_I4 && V_I4(role.ptr()) == ROLE_SYSTEM_DOCUMENT)
530 return;
532 // Get the value.
533 base::win::ScopedBstr value;
534 hr = accessible->get_accValue(childid_self, value.Receive());
535 EXPECT_EQ(S_OK, hr);
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) {
543 if (state_ < 0)
544 return;
546 base::win::ScopedVariant state;
547 base::win::ScopedVariant childid_self(CHILDID_SELF);
548 HRESULT hr = accessible->get_accState(childid_self, state.Receive());
549 EXPECT_EQ(S_OK, hr);
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());
582 base::string16
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();
592 } // namespace
595 // Tests ----------------------------------------------------------------------
597 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
598 TestBusyAccessibilityTree) {
599 if (GetBaseAccessibilityMode() != AccessibilityModeOff)
600 return;
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,
607 std::wstring());
608 document1_checker.SetExpectedState(
609 STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED |
610 STATE_SYSTEM_BUSY);
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,
623 std::wstring());
624 AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT,
625 std::wstring());
626 AccessibleChecker list_item_checker(std::wstring(), ROLE_SYSTEM_LISTITEM,
627 std::wstring());
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,
633 std::wstring());
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));
657 ExecuteScript(
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,
675 std::wstring());
676 checkbox_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
677 AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
678 std::wstring());
679 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
680 std::wstring());
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,
706 std::wstring());
707 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
708 std::wstring());
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(
715 shell(),
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,
736 std::wstring());
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,
749 std::wstring());
750 AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
751 std::wstring());
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,
765 std::wstring());
766 group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
767 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
768 std::wstring());
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.
787 waiter.reset(
788 new AccessibilityNotificationWaiter(
789 shell(), AccessibilityModeComplete,
790 ui::AX_EVENT_BLUR));
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);
796 ASSERT_EQ(S_OK, hr);
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,
812 L"old value");
813 text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
814 AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
815 std::wstring());
816 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
817 std::wstring());
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();
852 CHECK(window);
853 aura::WindowTreeHost* window_tree_host = window->GetHost();
854 CHECK(window_tree_host);
855 HWND hwnd = window_tree_host->GetAcceleratedWidget();
856 CHECK(hwnd);
857 base::win::ScopedComPtr<IAccessible> browser_accessible;
858 HRESULT hr = AccessibleObjectFromWindow(
859 hwnd,
860 OBJID_WINDOW,
861 IID_IAccessible,
862 reinterpret_cast<void**>(browser_accessible.Receive()));
863 ASSERT_EQ(S_OK, hr);
865 bool found = false;
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());
885 ASSERT_EQ(S_OK, hr);
886 const GUID refguid = {0x0c539790,
887 0x12e4,
888 0x11cf,
889 {0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8}};
890 base::win::ScopedComPtr<ISimpleDOMNode> document_isimpledomnode;
891 hr = static_cast<IServiceProvider*>(service_provider.get())
892 ->QueryService(
893 refguid, IID_ISimpleDOMNode,
894 reinterpret_cast<void**>(document_isimpledomnode.Receive()));
895 ASSERT_EQ(S_OK, hr);
897 base::win::ScopedBstr node_name;
898 short name_space_id; // NOLINT
899 base::win::ScopedBstr node_value;
900 unsigned int num_children;
901 unsigned int unique_id;
902 unsigned short node_type; // NOLINT
903 hr = document_isimpledomnode->get_nodeInfo(
904 node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
905 &unique_id, &node_type);
906 ASSERT_EQ(S_OK, hr);
907 EXPECT_EQ(NODETYPE_DOCUMENT, node_type);
908 EXPECT_EQ(1, num_children);
909 node_name.Reset();
910 node_value.Reset();
912 base::win::ScopedComPtr<ISimpleDOMNode> body_isimpledomnode;
913 hr = document_isimpledomnode->get_firstChild(
914 body_isimpledomnode.Receive());
915 ASSERT_EQ(S_OK, hr);
916 hr = body_isimpledomnode->get_nodeInfo(
917 node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
918 &unique_id, &node_type);
919 ASSERT_EQ(S_OK, hr);
920 EXPECT_EQ(L"body", std::wstring(node_name, node_name.Length()));
921 EXPECT_EQ(NODETYPE_ELEMENT, node_type);
922 EXPECT_EQ(1, num_children);
923 node_name.Reset();
924 node_value.Reset();
926 base::win::ScopedComPtr<ISimpleDOMNode> checkbox_isimpledomnode;
927 hr = body_isimpledomnode->get_firstChild(
928 checkbox_isimpledomnode.Receive());
929 ASSERT_EQ(S_OK, hr);
930 hr = checkbox_isimpledomnode->get_nodeInfo(
931 node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
932 &unique_id, &node_type);
933 ASSERT_EQ(S_OK, hr);
934 EXPECT_EQ(L"input", std::wstring(node_name, node_name.Length()));
935 EXPECT_EQ(NODETYPE_ELEMENT, node_type);
936 EXPECT_EQ(0, num_children);
939 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestRoleGroup) {
940 LoadInitialAccessibilityTreeFromHtml(
941 "<fieldset></fieldset><div role=group></div>");
943 // Check the browser's copy of the renderer accessibility tree.
944 AccessibleChecker grouping1_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
945 std::wstring());
946 AccessibleChecker grouping2_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
947 std::wstring());
948 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
949 std::wstring());
950 document_checker.AppendExpectedChild(&grouping1_checker);
951 document_checker.AppendExpectedChild(&grouping2_checker);
952 document_checker.CheckAccessible(GetRendererAccessible());
955 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
956 TestSetCaretOffset) {
957 base::win::ScopedComPtr<IAccessibleText> input_text;
958 SetUpInputField(&input_text);
960 LONG caret_offset = 0;
961 HRESULT hr = input_text->get_caretOffset(&caret_offset);
962 EXPECT_EQ(S_OK, hr);
963 EXPECT_EQ(CONTENTS_LENGTH - 1, caret_offset);
965 AccessibilityNotificationWaiter waiter(
966 shell(), AccessibilityModeComplete,
967 ui::AX_EVENT_TEXT_SELECTION_CHANGED);
968 caret_offset = 0;
969 hr = input_text->setCaretOffset(caret_offset);
970 EXPECT_EQ(S_OK, hr);
971 waiter.WaitForNotification();
973 hr = input_text->get_caretOffset(&caret_offset);
974 EXPECT_EQ(S_OK, hr);
975 EXPECT_EQ(0, caret_offset);
978 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
979 TestMultiLineSetCaretOffset) {
980 base::win::ScopedComPtr<IAccessibleText> textarea_text;
981 SetUpTextareaField(&textarea_text);
983 LONG caret_offset = 0;
984 HRESULT hr = textarea_text->get_caretOffset(&caret_offset);
985 EXPECT_EQ(S_OK, hr);
986 EXPECT_EQ(CONTENTS_LENGTH - 1, caret_offset);
988 AccessibilityNotificationWaiter waiter(
989 shell(), AccessibilityModeComplete,
990 ui::AX_EVENT_TEXT_SELECTION_CHANGED);
991 caret_offset = 0;
992 hr = textarea_text->setCaretOffset(caret_offset);
993 EXPECT_EQ(S_OK, hr);
994 waiter.WaitForNotification();
996 hr = textarea_text->get_caretOffset(&caret_offset);
997 EXPECT_EQ(S_OK, hr);
998 EXPECT_EQ(0, caret_offset);
1001 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1002 TestTextAtOffsetWithInvalidArgs) {
1003 base::win::ScopedComPtr<IAccessibleText> input_text;
1004 SetUpInputField(&input_text);
1005 HRESULT hr = input_text->get_textAtOffset(
1006 0, IA2_TEXT_BOUNDARY_CHAR, NULL, NULL, NULL);
1007 EXPECT_EQ(E_INVALIDARG, hr);
1009 // Test invalid offset.
1010 LONG start_offset = 0;
1011 LONG end_offset = 0;
1012 base::win::ScopedBstr text;
1013 LONG invalid_offset = -5;
1014 hr = input_text->get_textAtOffset(
1015 invalid_offset, IA2_TEXT_BOUNDARY_CHAR,
1016 &start_offset, &end_offset, text.Receive());
1017 EXPECT_EQ(E_INVALIDARG, hr);
1018 EXPECT_EQ(0, start_offset);
1019 EXPECT_EQ(0, end_offset);
1020 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1021 invalid_offset = CONTENTS_LENGTH + 1;
1022 hr = input_text->get_textAtOffset(
1023 invalid_offset, IA2_TEXT_BOUNDARY_WORD,
1024 &start_offset, &end_offset, text.Receive());
1025 EXPECT_EQ(E_INVALIDARG, hr);
1026 EXPECT_EQ(0, start_offset);
1027 EXPECT_EQ(0, end_offset);
1028 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1030 // According to the IA2 Spec, only line boundaries should succeed when
1031 // the offset is one past the end of the text.
1032 invalid_offset = CONTENTS_LENGTH;
1033 hr = input_text->get_textAtOffset(
1034 invalid_offset, IA2_TEXT_BOUNDARY_CHAR,
1035 &start_offset, &end_offset, text.Receive());
1036 EXPECT_EQ(S_FALSE, hr);
1037 EXPECT_EQ(0, start_offset);
1038 EXPECT_EQ(0, end_offset);
1039 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1040 hr = input_text->get_textAtOffset(
1041 invalid_offset, IA2_TEXT_BOUNDARY_WORD,
1042 &start_offset, &end_offset, text.Receive());
1043 EXPECT_EQ(S_FALSE, hr);
1044 EXPECT_EQ(0, start_offset);
1045 EXPECT_EQ(0, end_offset);
1046 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1047 hr = input_text->get_textAtOffset(
1048 invalid_offset, IA2_TEXT_BOUNDARY_SENTENCE,
1049 &start_offset, &end_offset, text.Receive());
1050 EXPECT_EQ(S_FALSE, hr);
1051 EXPECT_EQ(0, start_offset);
1052 EXPECT_EQ(0, end_offset);
1053 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1054 hr = input_text->get_textAtOffset(
1055 invalid_offset, IA2_TEXT_BOUNDARY_ALL,
1056 &start_offset, &end_offset, text.Receive());
1057 EXPECT_EQ(S_FALSE, hr);
1058 EXPECT_EQ(0, start_offset);
1059 EXPECT_EQ(0, end_offset);
1060 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1062 // The same behavior should be observed when the special offset
1063 // IA2_TEXT_OFFSET_LENGTH is used.
1064 hr = input_text->get_textAtOffset(
1065 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_CHAR,
1066 &start_offset, &end_offset, text.Receive());
1067 EXPECT_EQ(S_FALSE, hr);
1068 EXPECT_EQ(0, start_offset);
1069 EXPECT_EQ(0, end_offset);
1070 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1071 hr = input_text->get_textAtOffset(
1072 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_WORD,
1073 &start_offset, &end_offset, text.Receive());
1074 EXPECT_EQ(S_FALSE, hr);
1075 EXPECT_EQ(0, start_offset);
1076 EXPECT_EQ(0, end_offset);
1077 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1078 hr = input_text->get_textAtOffset(
1079 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_SENTENCE,
1080 &start_offset, &end_offset, text.Receive());
1081 EXPECT_EQ(S_FALSE, hr);
1082 EXPECT_EQ(0, start_offset);
1083 EXPECT_EQ(0, end_offset);
1084 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1085 hr = input_text->get_textAtOffset(
1086 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_ALL,
1087 &start_offset, &end_offset, text.Receive());
1088 EXPECT_EQ(S_FALSE, hr);
1089 EXPECT_EQ(0, start_offset);
1090 EXPECT_EQ(0, end_offset);
1091 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1094 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1095 TestMultiLineTextAtOffsetWithInvalidArgs) {
1096 base::win::ScopedComPtr<IAccessibleText> textarea_text;
1097 SetUpTextareaField(&textarea_text);
1098 HRESULT hr = textarea_text->get_textAtOffset(
1099 0, IA2_TEXT_BOUNDARY_CHAR, NULL, NULL, NULL);
1100 EXPECT_EQ(E_INVALIDARG, hr);
1102 // Test invalid offset.
1103 LONG start_offset = 0;
1104 LONG end_offset = 0;
1105 base::win::ScopedBstr text;
1106 LONG invalid_offset = -5;
1107 hr = textarea_text->get_textAtOffset(
1108 invalid_offset, IA2_TEXT_BOUNDARY_CHAR,
1109 &start_offset, &end_offset, text.Receive());
1110 EXPECT_EQ(E_INVALIDARG, hr);
1111 EXPECT_EQ(0, start_offset);
1112 EXPECT_EQ(0, end_offset);
1113 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1114 invalid_offset = CONTENTS_LENGTH + 1;
1115 hr = textarea_text->get_textAtOffset(
1116 invalid_offset, IA2_TEXT_BOUNDARY_WORD,
1117 &start_offset, &end_offset, text.Receive());
1118 EXPECT_EQ(E_INVALIDARG, hr);
1119 EXPECT_EQ(0, start_offset);
1120 EXPECT_EQ(0, end_offset);
1121 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1123 // According to the IA2 Spec, only line boundaries should succeed when
1124 // the offset is one past the end of the text.
1125 invalid_offset = CONTENTS_LENGTH;
1126 hr = textarea_text->get_textAtOffset(
1127 invalid_offset, IA2_TEXT_BOUNDARY_CHAR,
1128 &start_offset, &end_offset, text.Receive());
1129 EXPECT_EQ(S_FALSE, hr);
1130 EXPECT_EQ(0, start_offset);
1131 EXPECT_EQ(0, end_offset);
1132 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1133 hr = textarea_text->get_textAtOffset(
1134 invalid_offset, IA2_TEXT_BOUNDARY_WORD,
1135 &start_offset, &end_offset, text.Receive());
1136 EXPECT_EQ(S_FALSE, hr);
1137 EXPECT_EQ(0, start_offset);
1138 EXPECT_EQ(0, end_offset);
1139 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1140 hr = textarea_text->get_textAtOffset(
1141 invalid_offset, IA2_TEXT_BOUNDARY_SENTENCE,
1142 &start_offset, &end_offset, text.Receive());
1143 EXPECT_EQ(S_FALSE, hr);
1144 EXPECT_EQ(0, start_offset);
1145 EXPECT_EQ(0, end_offset);
1146 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1147 hr = textarea_text->get_textAtOffset(
1148 invalid_offset, IA2_TEXT_BOUNDARY_ALL,
1149 &start_offset, &end_offset, text.Receive());
1150 EXPECT_EQ(S_FALSE, hr);
1151 EXPECT_EQ(0, start_offset);
1152 EXPECT_EQ(0, end_offset);
1153 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1155 // The same behavior should be observed when the special offset
1156 // IA2_TEXT_OFFSET_LENGTH is used.
1157 hr = textarea_text->get_textAtOffset(
1158 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_CHAR,
1159 &start_offset, &end_offset, text.Receive());
1160 EXPECT_EQ(S_FALSE, hr);
1161 EXPECT_EQ(0, start_offset);
1162 EXPECT_EQ(0, end_offset);
1163 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1164 hr = textarea_text->get_textAtOffset(
1165 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_WORD,
1166 &start_offset, &end_offset, text.Receive());
1167 EXPECT_EQ(S_FALSE, hr);
1168 EXPECT_EQ(0, start_offset);
1169 EXPECT_EQ(0, end_offset);
1170 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1171 hr = textarea_text->get_textAtOffset(
1172 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_SENTENCE,
1173 &start_offset, &end_offset, text.Receive());
1174 EXPECT_EQ(S_FALSE, hr);
1175 EXPECT_EQ(0, start_offset);
1176 EXPECT_EQ(0, end_offset);
1177 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1178 hr = textarea_text->get_textAtOffset(
1179 IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_ALL,
1180 &start_offset, &end_offset, text.Receive());
1181 EXPECT_EQ(S_FALSE, hr);
1182 EXPECT_EQ(0, start_offset);
1183 EXPECT_EQ(0, end_offset);
1184 EXPECT_EQ(nullptr, static_cast<BSTR>(text));
1187 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1188 TestTextAtOffsetWithBoundaryCharacter) {
1189 base::win::ScopedComPtr<IAccessibleText> input_text;
1190 SetUpInputField(&input_text);
1191 for (LONG offset = 0; offset < CONTENTS_LENGTH; ++offset) {
1192 std::wstring expected_text(1, INPUT_CONTENTS[offset]);
1193 LONG expected_start_offset = offset;
1194 LONG expected_end_offset = offset + 1;
1195 CheckTextAtOffset(input_text, offset, IA2_TEXT_BOUNDARY_CHAR,
1196 expected_start_offset, expected_end_offset, expected_text);
1199 for (LONG offset = CONTENTS_LENGTH - 1; offset >= 0; --offset) {
1200 std::wstring expected_text(1, INPUT_CONTENTS[offset]);
1201 LONG expected_start_offset = offset;
1202 LONG expected_end_offset = offset + 1;
1203 CheckTextAtOffset(input_text, offset, IA2_TEXT_BOUNDARY_CHAR,
1204 expected_start_offset, expected_end_offset, expected_text);
1207 CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET,
1208 IA2_TEXT_BOUNDARY_CHAR, CONTENTS_LENGTH - 1, CONTENTS_LENGTH, L".");
1211 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1212 TestMultiLineTextAtOffsetWithBoundaryCharacter) {
1213 base::win::ScopedComPtr<IAccessibleText> textarea_text;
1214 SetUpTextareaField(&textarea_text);
1215 for (LONG offset = 0; offset < CONTENTS_LENGTH; ++offset) {
1216 std::wstring expected_text(1, TEXTAREA_CONTENTS[offset]);
1217 LONG expected_start_offset = offset;
1218 LONG expected_end_offset = offset + 1;
1219 CheckTextAtOffset(textarea_text, offset, IA2_TEXT_BOUNDARY_CHAR,
1220 expected_start_offset, expected_end_offset, expected_text);
1223 for (LONG offset = CONTENTS_LENGTH - 1; offset >= 0; --offset) {
1224 std::wstring expected_text(1, TEXTAREA_CONTENTS[offset]);
1225 LONG expected_start_offset = offset;
1226 LONG expected_end_offset = offset + 1;
1227 CheckTextAtOffset(textarea_text, offset, IA2_TEXT_BOUNDARY_CHAR,
1228 expected_start_offset, expected_end_offset, expected_text);
1231 CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET,
1232 IA2_TEXT_BOUNDARY_CHAR, CONTENTS_LENGTH - 1, CONTENTS_LENGTH, L".");
1235 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1236 TestTextAtOffsetWithBoundaryWord) {
1237 base::win::ScopedComPtr<IAccessibleText> input_text;
1238 SetUpInputField(&input_text);
1240 // Trailing punctuation should be included as part of the previous word.
1241 CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_WORD,
1242 0, 4, L"Moz/");
1243 CheckTextAtOffset(input_text, 2, IA2_TEXT_BOUNDARY_WORD,
1244 0, 4, L"Moz/");
1246 // If the offset is at the punctuation, it should return
1247 // the previous word.
1248 CheckTextAtOffset(input_text, 3, IA2_TEXT_BOUNDARY_WORD,
1249 0, 4, L"Moz/");
1251 // Numbers with a decimal point ("." for U.S), should be treated as one word.
1252 // Also, trailing punctuation that occurs after empty space should be part of
1253 // the word. ("5.0 (" and not "5.0 ".)
1254 CheckTextAtOffset(input_text, 4, IA2_TEXT_BOUNDARY_WORD,
1255 4, 9, L"5.0 (");
1256 CheckTextAtOffset(input_text, 5, IA2_TEXT_BOUNDARY_WORD,
1257 4, 9, L"5.0 (");
1258 CheckTextAtOffset(input_text, 6, IA2_TEXT_BOUNDARY_WORD,
1259 4, 9, L"5.0 (");
1260 CheckTextAtOffset(input_text, 7, IA2_TEXT_BOUNDARY_WORD,
1261 4, 9, L"5.0 (");
1263 // Leading punctuation should not be included with the word after it.
1264 CheckTextAtOffset(input_text, 8, IA2_TEXT_BOUNDARY_WORD,
1265 4, 9, L"5.0 (");
1266 CheckTextAtOffset(input_text, 11, IA2_TEXT_BOUNDARY_WORD,
1267 9, 12, L"ST ");
1269 // Numbers separated from letters with trailing punctuation should
1270 // be split into two words. Same for abreviations like "i.e.".
1271 CheckTextAtOffset(input_text, 12, IA2_TEXT_BOUNDARY_WORD,
1272 12, 14, L"6.");
1273 CheckTextAtOffset(input_text, 15, IA2_TEXT_BOUNDARY_WORD,
1274 14, 17, L"x; ");
1276 // Words with numbers should be treated like ordinary words.
1277 CheckTextAtOffset(input_text, 17, IA2_TEXT_BOUNDARY_WORD,
1278 17, 24, L"WWW33) ");
1279 CheckTextAtOffset(input_text, 23, IA2_TEXT_BOUNDARY_WORD,
1280 17, 24, L"WWW33) ");
1282 // Multiple trailing empty spaces should be part of the word preceding it.
1283 CheckTextAtOffset(input_text, 28, IA2_TEXT_BOUNDARY_WORD,
1284 24, 33, L"WebKit \"");
1285 CheckTextAtOffset(input_text, 31, IA2_TEXT_BOUNDARY_WORD,
1286 24, 33, L"WebKit \"");
1287 CheckTextAtOffset(input_text, 32, IA2_TEXT_BOUNDARY_WORD,
1288 24, 33, L"WebKit \"");
1290 // Leading punctuation such as quotation marks should not be part of the word.
1291 CheckTextAtOffset(input_text, 33, IA2_TEXT_BOUNDARY_WORD,
1292 33, 40, L"KHTML, ");
1293 CheckTextAtOffset(input_text, 38, IA2_TEXT_BOUNDARY_WORD,
1294 33, 40, L"KHTML, ");
1296 // Trailing final punctuation should be part of the last word.
1297 CheckTextAtOffset(input_text, 41, IA2_TEXT_BOUNDARY_WORD,
1298 40, CONTENTS_LENGTH, L"like\".");
1299 CheckTextAtOffset(input_text, 45, IA2_TEXT_BOUNDARY_WORD,
1300 40, CONTENTS_LENGTH, L"like\".");
1302 // Test special offsets.
1303 CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET,
1304 IA2_TEXT_BOUNDARY_WORD, 40, CONTENTS_LENGTH, L"like\".");
1307 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1308 TestMultiLineTextAtOffsetWithBoundaryWord) {
1309 base::win::ScopedComPtr<IAccessibleText> textarea_text;
1310 SetUpTextareaField(&textarea_text);
1312 // Trailing punctuation should be included as part of the previous word.
1313 CheckTextAtOffset(textarea_text, 0, IA2_TEXT_BOUNDARY_WORD,
1314 0, 4, L"Moz/");
1315 CheckTextAtOffset(textarea_text, 2, IA2_TEXT_BOUNDARY_WORD,
1316 0, 4, L"Moz/");
1318 // If the offset is at the punctuation, it should return
1319 // the previous word.
1320 CheckTextAtOffset(textarea_text, 3, IA2_TEXT_BOUNDARY_WORD,
1321 0, 4, L"Moz/");
1323 // Numbers with a decimal point ("." for U.S), should be treated as one word.
1324 // Also, trailing punctuation that occurs after empty space should be part of
1325 // the word. ("5.0 (" and not "5.0 ".)
1326 CheckTextAtOffset(textarea_text, 4, IA2_TEXT_BOUNDARY_WORD,
1327 4, 9, L"5.0 (");
1328 CheckTextAtOffset(textarea_text, 5, IA2_TEXT_BOUNDARY_WORD,
1329 4, 9, L"5.0 (");
1330 CheckTextAtOffset(textarea_text, 6, IA2_TEXT_BOUNDARY_WORD,
1331 4, 9, L"5.0 (");
1332 CheckTextAtOffset(textarea_text, 7, IA2_TEXT_BOUNDARY_WORD,
1333 4, 9, L"5.0 (");
1335 // Leading punctuation should not be included with the word after it.
1336 CheckTextAtOffset(textarea_text, 8, IA2_TEXT_BOUNDARY_WORD,
1337 4, 9, L"5.0 (");
1338 CheckTextAtOffset(textarea_text, 11, IA2_TEXT_BOUNDARY_WORD,
1339 9, 12, L"ST ");
1341 // Numbers separated from letters with trailing punctuation should
1342 // be split into two words. Same for abreviations like "i.e.".
1343 CheckTextAtOffset(textarea_text, 12, IA2_TEXT_BOUNDARY_WORD,
1344 12, 14, L"6.");
1345 CheckTextAtOffset(textarea_text, 15, IA2_TEXT_BOUNDARY_WORD,
1346 14, 17, L"x; ");
1348 // Words with numbers should be treated like ordinary words.
1349 CheckTextAtOffset(textarea_text, 17, IA2_TEXT_BOUNDARY_WORD,
1350 17, 24, L"WWW33)\n");
1351 CheckTextAtOffset(textarea_text, 23, IA2_TEXT_BOUNDARY_WORD,
1352 17, 24, L"WWW33)\n");
1354 // Multiple trailing empty spaces should be part of the word preceding it.
1355 CheckTextAtOffset(textarea_text, 28, IA2_TEXT_BOUNDARY_WORD,
1356 24, 33, L"WebKit \n\"");
1357 CheckTextAtOffset(textarea_text, 31, IA2_TEXT_BOUNDARY_WORD,
1358 24, 33, L"WebKit \n\"");
1359 CheckTextAtOffset(textarea_text, 32, IA2_TEXT_BOUNDARY_WORD,
1360 24, 33, L"WebKit \n\"");
1362 // Leading punctuation such as quotation marks should not be part of the word.
1363 CheckTextAtOffset(textarea_text, 33, IA2_TEXT_BOUNDARY_WORD,
1364 33, 40, L"KHTML, ");
1365 CheckTextAtOffset(textarea_text, 38, IA2_TEXT_BOUNDARY_WORD,
1366 33, 40, L"KHTML, ");
1368 // Trailing final punctuation should be part of the last word.
1369 CheckTextAtOffset(textarea_text, 41, IA2_TEXT_BOUNDARY_WORD,
1370 40, CONTENTS_LENGTH, L"like\".");
1371 CheckTextAtOffset(textarea_text, 45, IA2_TEXT_BOUNDARY_WORD,
1372 40, CONTENTS_LENGTH, L"like\".");
1374 // Test special offsets.
1375 CheckTextAtOffset(textarea_text, IA2_TEXT_OFFSET_CARET,
1376 IA2_TEXT_BOUNDARY_WORD, 40, CONTENTS_LENGTH, L"like\".");
1379 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1380 TestTextAtOffsetWithBoundarySentence) {
1381 base::win::ScopedComPtr<IAccessibleText> input_text;
1382 SetUpInputField(&input_text);
1384 // Sentence navigation is not currently implemented.
1385 LONG start_offset = 0;
1386 LONG end_offset = 0;
1387 base::win::ScopedBstr text;
1388 HRESULT hr = input_text->get_textAtOffset(
1389 5, IA2_TEXT_BOUNDARY_SENTENCE,
1390 &start_offset, &end_offset, text.Receive());
1391 EXPECT_EQ(S_FALSE, hr);
1394 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1395 TestMultiLineTextAtOffsetWithBoundarySentence) {
1396 base::win::ScopedComPtr<IAccessibleText> textarea_text;
1397 SetUpTextareaField(&textarea_text);
1399 // Sentence navigation is not currently implemented.
1400 LONG start_offset = 0;
1401 LONG end_offset = 0;
1402 base::win::ScopedBstr text;
1403 HRESULT hr = textarea_text->get_textAtOffset(
1404 25, IA2_TEXT_BOUNDARY_SENTENCE,
1405 &start_offset, &end_offset, text.Receive());
1406 EXPECT_EQ(S_FALSE, hr);
1409 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1410 TestTextAtOffsetWithBoundaryLine) {
1411 base::win::ScopedComPtr<IAccessibleText> input_text;
1412 SetUpInputField(&input_text);
1414 // Single line text fields should return the whole text.
1415 CheckTextAtOffset(input_text, 0, IA2_TEXT_BOUNDARY_LINE,
1416 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS));
1418 // Test special offsets.
1419 CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_LENGTH, IA2_TEXT_BOUNDARY_LINE,
1420 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS));
1421 CheckTextAtOffset(input_text, IA2_TEXT_OFFSET_CARET, IA2_TEXT_BOUNDARY_LINE,
1422 0, CONTENTS_LENGTH, base::SysUTF8ToWide(INPUT_CONTENTS));
1425 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
1426 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