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.
4
5 #include <vector>
6
7 #include "base/memory/scoped_ptr.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/win/scoped_bstr.h"
10 #include "base/win/scoped_comptr.h"
11 #include "base/win/scoped_variant.h"
12 #include "content/browser/accessibility/accessibility_mode_helper.h"
13 #include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
14 #include "content/browser/renderer_host/render_view_host_impl.h"
15 #include "content/public/browser/notification_service.h"
16 #include "content/public/browser/notification_types.h"
17 #include "content/public/browser/render_frame_host.h"
18 #include "content/public/browser/render_widget_host_view.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/url_constants.h"
21 #include "content/public/test/content_browser_test.h"
22 #include "content/public/test/content_browser_test_utils.h"
23 #include "content/shell/browser/shell.h"
24 #include "content/test/accessibility_browser_test_utils.h"
25 #include "third_party/iaccessible2/ia2_api_all.h"
26 #include "third_party/isimpledom/ISimpleDOMNode.h"
27 #include "ui/aura/window.h"
28 #include "ui/aura/window_tree_host.h"
29
30 namespace content {
31
32 namespace {
33
34 // Helpers --------------------------------------------------------------------
35
GetAccessibleFromResultVariant(IAccessible * parent,VARIANT * var)36 base::win::ScopedComPtr<IAccessible> GetAccessibleFromResultVariant(
37 IAccessible* parent,
38 VARIANT* var) {
39 base::win::ScopedComPtr<IAccessible> ptr;
40 switch (V_VT(var)) {
41 case VT_DISPATCH: {
42 IDispatch* dispatch = V_DISPATCH(var);
43 if (dispatch)
44 ptr.QueryFrom(dispatch);
45 break;
46 }
47
48 case VT_I4: {
49 base::win::ScopedComPtr<IDispatch> dispatch;
50 HRESULT hr = parent->get_accChild(*var, dispatch.Receive());
51 EXPECT_TRUE(SUCCEEDED(hr));
52 if (dispatch)
53 dispatch.QueryInterface(ptr.Receive());
54 break;
55 }
56 }
57 return ptr;
58 }
59
QueryIAccessible2(IAccessible * accessible,IAccessible2 ** accessible2)60 HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) {
61 // TODO(ctguil): For some reason querying the IAccessible2 interface from
62 // IAccessible fails.
63 base::win::ScopedComPtr<IServiceProvider> service_provider;
64 HRESULT hr = accessible->QueryInterface(service_provider.Receive());
65 return SUCCEEDED(hr) ?
66 service_provider->QueryService(IID_IAccessible2, accessible2) : hr;
67 }
68
69 // Recursively search through all of the descendants reachable from an
70 // IAccessible node and return true if we find one with the given role
71 // and name.
RecursiveFindNodeInAccessibilityTree(IAccessible * node,int32 expected_role,const std::wstring & expected_name,int32 depth,bool * found)72 void RecursiveFindNodeInAccessibilityTree(IAccessible* node,
73 int32 expected_role,
74 const std::wstring& expected_name,
75 int32 depth,
76 bool* found) {
77 base::win::ScopedBstr name_bstr;
78 base::win::ScopedVariant childid_self(CHILDID_SELF);
79 node->get_accName(childid_self, name_bstr.Receive());
80 std::wstring name(name_bstr, name_bstr.Length());
81 base::win::ScopedVariant role;
82 node->get_accRole(childid_self, role.Receive());
83 ASSERT_EQ(VT_I4, role.type());
84
85 // Print the accessibility tree as we go, because if this test fails
86 // on the bots, this is really helpful in figuring out why.
87 for (int i = 0; i < depth; i++)
88 printf(" ");
89 printf("role=%s name=%s\n",
90 base::WideToUTF8(IAccessibleRoleToString(V_I4(&role))).c_str(),
91 base::WideToUTF8(name).c_str());
92
93 if (expected_role == V_I4(&role) && expected_name == name) {
94 *found = true;
95 return;
96 }
97
98 LONG child_count = 0;
99 HRESULT hr = node->get_accChildCount(&child_count);
100 ASSERT_EQ(S_OK, hr);
101
102 scoped_ptr<VARIANT[]> child_array(new VARIANT[child_count]);
103 LONG obtained_count = 0;
104 hr = AccessibleChildren(
105 node, 0, child_count, child_array.get(), &obtained_count);
106 ASSERT_EQ(S_OK, hr);
107 ASSERT_EQ(child_count, obtained_count);
108
109 for (int index = 0; index < obtained_count; index++) {
110 base::win::ScopedComPtr<IAccessible> child_accessible(
111 GetAccessibleFromResultVariant(node, &child_array.get()[index]));
112 if (child_accessible) {
113 RecursiveFindNodeInAccessibilityTree(
114 child_accessible.get(), expected_role, expected_name, depth + 1,
115 found);
116 if (*found)
117 return;
118 }
119 }
120 }
121
122
123 // AccessibilityWinBrowserTest ------------------------------------------------
124
125 class AccessibilityWinBrowserTest : public ContentBrowserTest {
126 public:
127 AccessibilityWinBrowserTest();
128 virtual ~AccessibilityWinBrowserTest();
129
130 protected:
131 void LoadInitialAccessibilityTreeFromHtml(const std::string& html);
132 IAccessible* GetRendererAccessible();
133 void ExecuteScript(const std::wstring& script);
134
135 private:
136 DISALLOW_COPY_AND_ASSIGN(AccessibilityWinBrowserTest);
137 };
138
AccessibilityWinBrowserTest()139 AccessibilityWinBrowserTest::AccessibilityWinBrowserTest() {
140 }
141
~AccessibilityWinBrowserTest()142 AccessibilityWinBrowserTest::~AccessibilityWinBrowserTest() {
143 }
144
LoadInitialAccessibilityTreeFromHtml(const std::string & html)145 void AccessibilityWinBrowserTest::LoadInitialAccessibilityTreeFromHtml(
146 const std::string& html) {
147 AccessibilityNotificationWaiter waiter(
148 shell(), AccessibilityModeComplete,
149 ui::AX_EVENT_LOAD_COMPLETE);
150 GURL html_data_url("data:text/html," + html);
151 NavigateToURL(shell(), html_data_url);
152 waiter.WaitForNotification();
153 }
154
155 // Retrieve the MSAA client accessibility object for the Render Widget Host View
156 // of the selected tab.
GetRendererAccessible()157 IAccessible* AccessibilityWinBrowserTest::GetRendererAccessible() {
158 content::WebContents* web_contents = shell()->web_contents();
159 return web_contents->GetRenderWidgetHostView()->GetNativeViewAccessible();
160 }
161
ExecuteScript(const std::wstring & script)162 void AccessibilityWinBrowserTest::ExecuteScript(const std::wstring& script) {
163 shell()->web_contents()->GetMainFrame()->ExecuteJavaScript(script);
164 }
165
166
167 // AccessibleChecker ----------------------------------------------------------
168
169 class AccessibleChecker {
170 public:
171 // This constructor can be used if the IA2 role will be the same as the MSAA
172 // role.
173 AccessibleChecker(const std::wstring& expected_name,
174 int32 expected_role,
175 const std::wstring& expected_value);
176 AccessibleChecker(const std::wstring& expected_name,
177 int32 expected_role,
178 int32 expected_ia2_role,
179 const std::wstring& expected_value);
180 AccessibleChecker(const std::wstring& expected_name,
181 const std::wstring& expected_role,
182 int32 expected_ia2_role,
183 const std::wstring& expected_value);
184
185 // Append an AccessibleChecker that verifies accessibility information for
186 // a child IAccessible. Order is important.
187 void AppendExpectedChild(AccessibleChecker* expected_child);
188
189 // Check that the name and role of the given IAccessible instance and its
190 // descendants match the expected names and roles that this object was
191 // initialized with.
192 void CheckAccessible(IAccessible* accessible);
193
194 // Set the expected value for this AccessibleChecker.
195 void SetExpectedValue(const std::wstring& expected_value);
196
197 // Set the expected state for this AccessibleChecker.
198 void SetExpectedState(LONG expected_state);
199
200 private:
201 typedef std::vector<AccessibleChecker*> AccessibleCheckerVector;
202
203 void CheckAccessibleName(IAccessible* accessible);
204 void CheckAccessibleRole(IAccessible* accessible);
205 void CheckIA2Role(IAccessible* accessible);
206 void CheckAccessibleValue(IAccessible* accessible);
207 void CheckAccessibleState(IAccessible* accessible);
208 void CheckAccessibleChildren(IAccessible* accessible);
209 base::string16 RoleVariantToString(const base::win::ScopedVariant& role);
210
211 // Expected accessible name. Checked against IAccessible::get_accName.
212 std::wstring name_;
213
214 // Expected accessible role. Checked against IAccessible::get_accRole.
215 base::win::ScopedVariant role_;
216
217 // Expected IAccessible2 role. Checked against IAccessible2::role.
218 int32 ia2_role_;
219
220 // Expected accessible value. Checked against IAccessible::get_accValue.
221 std::wstring value_;
222
223 // Expected accessible state. Checked against IAccessible::get_accState.
224 LONG state_;
225
226 // Expected accessible children. Checked using IAccessible::get_accChildCount
227 // and ::AccessibleChildren.
228 AccessibleCheckerVector children_;
229 };
230
AccessibleChecker(const std::wstring & expected_name,int32 expected_role,const std::wstring & expected_value)231 AccessibleChecker::AccessibleChecker(const std::wstring& expected_name,
232 int32 expected_role,
233 const std::wstring& expected_value)
234 : name_(expected_name),
235 role_(expected_role),
236 ia2_role_(expected_role),
237 value_(expected_value),
238 state_(-1) {
239 }
240
AccessibleChecker(const std::wstring & expected_name,int32 expected_role,int32 expected_ia2_role,const std::wstring & expected_value)241 AccessibleChecker::AccessibleChecker(const std::wstring& expected_name,
242 int32 expected_role,
243 int32 expected_ia2_role,
244 const std::wstring& expected_value)
245 : name_(expected_name),
246 role_(expected_role),
247 ia2_role_(expected_ia2_role),
248 value_(expected_value),
249 state_(-1) {
250 }
251
AccessibleChecker(const std::wstring & expected_name,const std::wstring & expected_role,int32 expected_ia2_role,const std::wstring & expected_value)252 AccessibleChecker::AccessibleChecker(const std::wstring& expected_name,
253 const std::wstring& expected_role,
254 int32 expected_ia2_role,
255 const std::wstring& expected_value)
256 : name_(expected_name),
257 role_(expected_role.c_str()),
258 ia2_role_(expected_ia2_role),
259 value_(expected_value),
260 state_(-1) {
261 }
262
AppendExpectedChild(AccessibleChecker * expected_child)263 void AccessibleChecker::AppendExpectedChild(
264 AccessibleChecker* expected_child) {
265 children_.push_back(expected_child);
266 }
267
CheckAccessible(IAccessible * accessible)268 void AccessibleChecker::CheckAccessible(IAccessible* accessible) {
269 SCOPED_TRACE("while checking " +
270 base::UTF16ToUTF8(RoleVariantToString(role_)));
271 CheckAccessibleName(accessible);
272 CheckAccessibleRole(accessible);
273 CheckIA2Role(accessible);
274 CheckAccessibleValue(accessible);
275 CheckAccessibleState(accessible);
276 CheckAccessibleChildren(accessible);
277 }
278
SetExpectedValue(const std::wstring & expected_value)279 void AccessibleChecker::SetExpectedValue(const std::wstring& expected_value) {
280 value_ = expected_value;
281 }
282
SetExpectedState(LONG expected_state)283 void AccessibleChecker::SetExpectedState(LONG expected_state) {
284 state_ = expected_state;
285 }
286
CheckAccessibleName(IAccessible * accessible)287 void AccessibleChecker::CheckAccessibleName(IAccessible* accessible) {
288 base::win::ScopedBstr name;
289 base::win::ScopedVariant childid_self(CHILDID_SELF);
290 HRESULT hr = accessible->get_accName(childid_self, name.Receive());
291
292 if (name_.empty()) {
293 // If the object doesn't have name S_FALSE should be returned.
294 EXPECT_EQ(S_FALSE, hr);
295 } else {
296 // Test that the correct string was returned.
297 EXPECT_EQ(S_OK, hr);
298 EXPECT_EQ(name_, std::wstring(name, name.Length()));
299 }
300 }
301
CheckAccessibleRole(IAccessible * accessible)302 void AccessibleChecker::CheckAccessibleRole(IAccessible* accessible) {
303 base::win::ScopedVariant role;
304 base::win::ScopedVariant childid_self(CHILDID_SELF);
305 HRESULT hr = accessible->get_accRole(childid_self, role.Receive());
306 ASSERT_EQ(S_OK, hr);
307 EXPECT_EQ(0, role_.Compare(role))
308 << "Expected role: " << RoleVariantToString(role_)
309 << "\nGot role: " << RoleVariantToString(role);
310 }
311
CheckIA2Role(IAccessible * accessible)312 void AccessibleChecker::CheckIA2Role(IAccessible* accessible) {
313 base::win::ScopedComPtr<IAccessible2> accessible2;
314 HRESULT hr = QueryIAccessible2(accessible, accessible2.Receive());
315 ASSERT_EQ(S_OK, hr);
316 long ia2_role = 0;
317 hr = accessible2->role(&ia2_role);
318 ASSERT_EQ(S_OK, hr);
319 EXPECT_EQ(ia2_role_, ia2_role)
320 << "Expected ia2 role: " << IAccessible2RoleToString(ia2_role_)
321 << "\nGot ia2 role: " << IAccessible2RoleToString(ia2_role);
322 }
323
CheckAccessibleValue(IAccessible * accessible)324 void AccessibleChecker::CheckAccessibleValue(IAccessible* accessible) {
325 // Don't check the value if if's a DOCUMENT role, because the value
326 // is supposed to be the url (and we don't keep track of that in the
327 // test expectations).
328 base::win::ScopedVariant role;
329 base::win::ScopedVariant childid_self(CHILDID_SELF);
330 HRESULT hr = accessible->get_accRole(childid_self, role.Receive());
331 ASSERT_EQ(S_OK, hr);
332 if (role.type() == VT_I4 && V_I4(&role) == ROLE_SYSTEM_DOCUMENT)
333 return;
334
335 // Get the value.
336 base::win::ScopedBstr value;
337 hr = accessible->get_accValue(childid_self, value.Receive());
338 EXPECT_EQ(S_OK, hr);
339
340 // Test that the correct string was returned.
341 EXPECT_EQ(value_, std::wstring(value, value.Length()));
342 }
343
CheckAccessibleState(IAccessible * accessible)344 void AccessibleChecker::CheckAccessibleState(IAccessible* accessible) {
345 if (state_ < 0)
346 return;
347
348 base::win::ScopedVariant state;
349 base::win::ScopedVariant childid_self(CHILDID_SELF);
350 HRESULT hr = accessible->get_accState(childid_self, state.Receive());
351 EXPECT_EQ(S_OK, hr);
352 ASSERT_EQ(VT_I4, state.type());
353 LONG obj_state = V_I4(&state);
354 // Avoid flakiness. The "offscreen" state depends on whether the browser
355 // window is frontmost or not, and "hottracked" depends on whether the
356 // mouse cursor happens to be over the element.
357 obj_state &= ~(STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_HOTTRACKED);
358 EXPECT_EQ(state_, obj_state)
359 << "Expected state: " << IAccessibleStateToString(state_)
360 << "\nGot state: " << IAccessibleStateToString(obj_state);
361 }
362
CheckAccessibleChildren(IAccessible * parent)363 void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) {
364 LONG child_count = 0;
365 HRESULT hr = parent->get_accChildCount(&child_count);
366 EXPECT_EQ(S_OK, hr);
367 ASSERT_EQ(child_count, children_.size());
368
369 scoped_ptr<VARIANT[]> child_array(new VARIANT[child_count]);
370 LONG obtained_count = 0;
371 hr = AccessibleChildren(parent, 0, child_count,
372 child_array.get(), &obtained_count);
373 ASSERT_EQ(S_OK, hr);
374 ASSERT_EQ(child_count, obtained_count);
375
376 VARIANT* child = child_array.get();
377 for (AccessibleCheckerVector::iterator child_checker = children_.begin();
378 child_checker != children_.end();
379 ++child_checker, ++child) {
380 base::win::ScopedComPtr<IAccessible> child_accessible(
381 GetAccessibleFromResultVariant(parent, child));
382 ASSERT_TRUE(child_accessible.get());
383 (*child_checker)->CheckAccessible(child_accessible);
384 }
385 }
386
RoleVariantToString(const base::win::ScopedVariant & role)387 base::string16 AccessibleChecker::RoleVariantToString(
388 const base::win::ScopedVariant& role) {
389 if (role.type() == VT_I4)
390 return IAccessibleRoleToString(V_I4(&role));
391 if (role.type() == VT_BSTR)
392 return base::string16(V_BSTR(&role), SysStringLen(V_BSTR(&role)));
393 return base::string16();
394 }
395
396 } // namespace
397
398
399 // Tests ----------------------------------------------------------------------
400
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestBusyAccessibilityTree)401 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
402 TestBusyAccessibilityTree) {
403 if (GetBaseAccessibilityMode() != AccessibilityModeOff)
404 return;
405
406 NavigateToURL(shell(), GURL(url::kAboutBlankURL));
407
408 // The initial accessible returned should have state STATE_SYSTEM_BUSY while
409 // the accessibility tree is being requested from the renderer.
410 AccessibleChecker document1_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
411 std::wstring());
412 document1_checker.SetExpectedState(
413 STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED |
414 STATE_SYSTEM_BUSY);
415 document1_checker.CheckAccessible(GetRendererAccessible());
416 }
417
418 // Periodically failing. See crbug.com/145537
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,DISABLED_TestNotificationActiveDescendantChanged)419 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
420 DISABLED_TestNotificationActiveDescendantChanged) {
421 LoadInitialAccessibilityTreeFromHtml(
422 "<ul tabindex='-1' role='radiogroup' aria-label='ul'>"
423 "<li id='li'>li</li></ul>");
424
425 // Check the browser's copy of the renderer accessibility tree.
426 AccessibleChecker list_marker_checker(L"\x2022", ROLE_SYSTEM_TEXT,
427 std::wstring());
428 AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT,
429 std::wstring());
430 AccessibleChecker list_item_checker(std::wstring(), ROLE_SYSTEM_LISTITEM,
431 std::wstring());
432 list_item_checker.SetExpectedState(STATE_SYSTEM_READONLY);
433 AccessibleChecker radio_group_checker(L"ul", ROLE_SYSTEM_GROUPING,
434 IA2_ROLE_SECTION, std::wstring());
435 radio_group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
436 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
437 std::wstring());
438 list_item_checker.AppendExpectedChild(&list_marker_checker);
439 list_item_checker.AppendExpectedChild(&static_text_checker);
440 radio_group_checker.AppendExpectedChild(&list_item_checker);
441 document_checker.AppendExpectedChild(&radio_group_checker);
442 document_checker.CheckAccessible(GetRendererAccessible());
443
444 // Set focus to the radio group.
445 scoped_ptr<AccessibilityNotificationWaiter> waiter(
446 new AccessibilityNotificationWaiter(
447 shell(), AccessibilityModeComplete,
448 ui::AX_EVENT_FOCUS));
449 ExecuteScript(L"document.body.children[0].focus()");
450 waiter->WaitForNotification();
451
452 // Check that the accessibility tree of the browser has been updated.
453 radio_group_checker.SetExpectedState(
454 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED);
455 document_checker.CheckAccessible(GetRendererAccessible());
456
457 // Set the active descendant of the radio group
458 waiter.reset(new AccessibilityNotificationWaiter(
459 shell(), AccessibilityModeComplete,
460 ui::AX_EVENT_FOCUS));
461 ExecuteScript(
462 L"document.body.children[0].setAttribute('aria-activedescendant', 'li')");
463 waiter->WaitForNotification();
464
465 // Check that the accessibility tree of the browser has been updated.
466 list_item_checker.SetExpectedState(
467 STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED);
468 radio_group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
469 document_checker.CheckAccessible(GetRendererAccessible());
470 }
471
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationCheckedStateChanged)472 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
473 TestNotificationCheckedStateChanged) {
474 LoadInitialAccessibilityTreeFromHtml(
475 "<body><input type='checkbox' /></body>");
476
477 // Check the browser's copy of the renderer accessibility tree.
478 AccessibleChecker checkbox_checker(std::wstring(), ROLE_SYSTEM_CHECKBUTTON,
479 std::wstring());
480 checkbox_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
481 AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
482 std::wstring());
483 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
484 std::wstring());
485 body_checker.AppendExpectedChild(&checkbox_checker);
486 document_checker.AppendExpectedChild(&body_checker);
487 document_checker.CheckAccessible(GetRendererAccessible());
488
489 // Check the checkbox.
490 scoped_ptr<AccessibilityNotificationWaiter> waiter(
491 new AccessibilityNotificationWaiter(
492 shell(), AccessibilityModeComplete,
493 ui::AX_EVENT_CHECKED_STATE_CHANGED));
494 ExecuteScript(L"document.body.children[0].checked=true");
495 waiter->WaitForNotification();
496
497 // Check that the accessibility tree of the browser has been updated.
498 checkbox_checker.SetExpectedState(
499 STATE_SYSTEM_CHECKED | STATE_SYSTEM_FOCUSABLE);
500 document_checker.CheckAccessible(GetRendererAccessible());
501 }
502
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationChildrenChanged)503 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
504 TestNotificationChildrenChanged) {
505 // The role attribute causes the node to be in the accessibility tree.
506 LoadInitialAccessibilityTreeFromHtml("<body role=group></body>");
507
508 // Check the browser's copy of the renderer accessibility tree.
509 AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
510 std::wstring());
511 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
512 std::wstring());
513 document_checker.AppendExpectedChild(&group_checker);
514 document_checker.CheckAccessible(GetRendererAccessible());
515
516 // Change the children of the document body.
517 scoped_ptr<AccessibilityNotificationWaiter> waiter(
518 new AccessibilityNotificationWaiter(
519 shell(),
520 AccessibilityModeComplete,
521 ui::AX_EVENT_CHILDREN_CHANGED));
522 ExecuteScript(L"document.body.innerHTML='<b>new text</b>'");
523 waiter->WaitForNotification();
524
525 // Check that the accessibility tree of the browser has been updated.
526 AccessibleChecker text_checker(
527 L"new text", ROLE_SYSTEM_STATICTEXT, std::wstring());
528 group_checker.AppendExpectedChild(&text_checker);
529 document_checker.CheckAccessible(GetRendererAccessible());
530 }
531
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationChildrenChanged2)532 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
533 TestNotificationChildrenChanged2) {
534 // The role attribute causes the node to be in the accessibility tree.
535 LoadInitialAccessibilityTreeFromHtml(
536 "<div role=group style='visibility: hidden'>text</div>");
537
538 // Check the accessible tree of the browser.
539 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
540 std::wstring());
541 document_checker.CheckAccessible(GetRendererAccessible());
542
543 // Change the children of the document body.
544 scoped_ptr<AccessibilityNotificationWaiter> waiter(
545 new AccessibilityNotificationWaiter(
546 shell(), AccessibilityModeComplete,
547 ui::AX_EVENT_CHILDREN_CHANGED));
548 ExecuteScript(L"document.body.children[0].style.visibility='visible'");
549 waiter->WaitForNotification();
550
551 // Check that the accessibility tree of the browser has been updated.
552 AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_STATICTEXT,
553 std::wstring());
554 AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
555 std::wstring());
556 document_checker.AppendExpectedChild(&group_checker);
557 group_checker.AppendExpectedChild(&static_text_checker);
558 document_checker.CheckAccessible(GetRendererAccessible());
559 }
560
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationFocusChanged)561 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
562 TestNotificationFocusChanged) {
563 // The role attribute causes the node to be in the accessibility tree.
564 LoadInitialAccessibilityTreeFromHtml("<div role=group tabindex='-1'></div>");
565
566 // Check the browser's copy of the renderer accessibility tree.
567 SCOPED_TRACE("Check initial tree");
568 AccessibleChecker group_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
569 std::wstring());
570 group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
571 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
572 std::wstring());
573 document_checker.AppendExpectedChild(&group_checker);
574 document_checker.CheckAccessible(GetRendererAccessible());
575
576 // Focus the div in the document
577 scoped_ptr<AccessibilityNotificationWaiter> waiter(
578 new AccessibilityNotificationWaiter(
579 shell(), AccessibilityModeComplete,
580 ui::AX_EVENT_FOCUS));
581 ExecuteScript(L"document.body.children[0].focus()");
582 waiter->WaitForNotification();
583
584 // Check that the accessibility tree of the browser has been updated.
585 SCOPED_TRACE("Check updated tree after focusing div");
586 group_checker.SetExpectedState(
587 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED);
588 document_checker.CheckAccessible(GetRendererAccessible());
589
590 // Focus the document accessible. This will un-focus the current node.
591 waiter.reset(
592 new AccessibilityNotificationWaiter(
593 shell(), AccessibilityModeComplete,
594 ui::AX_EVENT_BLUR));
595 base::win::ScopedComPtr<IAccessible> document_accessible(
596 GetRendererAccessible());
597 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
598 base::win::ScopedVariant childid_self(CHILDID_SELF);
599 HRESULT hr = document_accessible->accSelect(SELFLAG_TAKEFOCUS, childid_self);
600 ASSERT_EQ(S_OK, hr);
601 waiter->WaitForNotification();
602
603 // Check that the accessibility tree of the browser has been updated.
604 SCOPED_TRACE("Check updated tree after focusing document again");
605 group_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
606 document_checker.CheckAccessible(GetRendererAccessible());
607 }
608
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationValueChanged)609 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
610 TestNotificationValueChanged) {
611 LoadInitialAccessibilityTreeFromHtml(
612 "<body><input type='text' value='old value'/></body>");
613
614 // Check the browser's copy of the renderer accessibility tree.
615 AccessibleChecker text_field_checker(std::wstring(), ROLE_SYSTEM_TEXT,
616 L"old value");
617 text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
618 AccessibleChecker body_checker(std::wstring(), L"body", IA2_ROLE_SECTION,
619 std::wstring());
620 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
621 std::wstring());
622 body_checker.AppendExpectedChild(&text_field_checker);
623 document_checker.AppendExpectedChild(&body_checker);
624 document_checker.CheckAccessible(GetRendererAccessible());
625
626 // Set the value of the text control
627 scoped_ptr<AccessibilityNotificationWaiter> waiter(
628 new AccessibilityNotificationWaiter(
629 shell(), AccessibilityModeComplete,
630 ui::AX_EVENT_VALUE_CHANGED));
631 ExecuteScript(L"document.body.children[0].value='new value'");
632 waiter->WaitForNotification();
633
634 // Check that the accessibility tree of the browser has been updated.
635 text_field_checker.SetExpectedValue(L"new value");
636 document_checker.CheckAccessible(GetRendererAccessible());
637 }
638
639 // This test verifies that the web content's accessibility tree is a
640 // descendant of the main browser window's accessibility tree, so that
641 // tools like AccExplorer32 or AccProbe can be used to examine Chrome's
642 // accessibility support.
643 //
644 // If you made a change and this test now fails, check that the NativeViewHost
645 // that wraps the tab contents returns the IAccessible implementation
646 // provided by RenderWidgetHostViewWin in GetNativeViewAccessible().
647 // flaky: http://crbug.com/402190
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,DISABLED_ContainsRendererAccessibilityTree)648 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
649 DISABLED_ContainsRendererAccessibilityTree) {
650 LoadInitialAccessibilityTreeFromHtml(
651 "<html><head><title>MyDocument</title></head>"
652 "<body>Content</body></html>");
653
654 // Get the accessibility object for the window tree host.
655 aura::Window* window = shell()->window();
656 CHECK(window);
657 aura::WindowTreeHost* window_tree_host = window->GetHost();
658 CHECK(window_tree_host);
659 HWND hwnd = window_tree_host->GetAcceleratedWidget();
660 CHECK(hwnd);
661 base::win::ScopedComPtr<IAccessible> browser_accessible;
662 HRESULT hr = AccessibleObjectFromWindow(
663 hwnd,
664 OBJID_WINDOW,
665 IID_IAccessible,
666 reinterpret_cast<void**>(browser_accessible.Receive()));
667 ASSERT_EQ(S_OK, hr);
668
669 bool found = false;
670 RecursiveFindNodeInAccessibilityTree(
671 browser_accessible.get(), ROLE_SYSTEM_DOCUMENT, L"MyDocument", 0, &found);
672 ASSERT_EQ(found, true);
673 }
674
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,SupportsISimpleDOM)675 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
676 SupportsISimpleDOM) {
677 LoadInitialAccessibilityTreeFromHtml(
678 "<body><input type='checkbox' /></body>");
679
680 // Get the IAccessible object for the document.
681 base::win::ScopedComPtr<IAccessible> document_accessible(
682 GetRendererAccessible());
683 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
684
685 // Get the ISimpleDOM object for the document.
686 base::win::ScopedComPtr<IServiceProvider> service_provider;
687 HRESULT hr = static_cast<IAccessible*>(document_accessible)->QueryInterface(
688 service_provider.Receive());
689 ASSERT_EQ(S_OK, hr);
690 const GUID refguid = {0x0c539790, 0x12e4, 0x11cf,
691 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
692 base::win::ScopedComPtr<ISimpleDOMNode> document_isimpledomnode;
693 hr = static_cast<IServiceProvider *>(service_provider)->QueryService(
694 refguid, IID_ISimpleDOMNode,
695 reinterpret_cast<void**>(document_isimpledomnode.Receive()));
696 ASSERT_EQ(S_OK, hr);
697
698 base::win::ScopedBstr node_name;
699 short name_space_id; // NOLINT
700 base::win::ScopedBstr node_value;
701 unsigned int num_children;
702 unsigned int unique_id;
703 unsigned short node_type; // NOLINT
704 hr = document_isimpledomnode->get_nodeInfo(
705 node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
706 &unique_id, &node_type);
707 ASSERT_EQ(S_OK, hr);
708 EXPECT_EQ(NODETYPE_DOCUMENT, node_type);
709 EXPECT_EQ(1, num_children);
710 node_name.Reset();
711 node_value.Reset();
712
713 base::win::ScopedComPtr<ISimpleDOMNode> body_isimpledomnode;
714 hr = document_isimpledomnode->get_firstChild(
715 body_isimpledomnode.Receive());
716 ASSERT_EQ(S_OK, hr);
717 hr = body_isimpledomnode->get_nodeInfo(
718 node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
719 &unique_id, &node_type);
720 ASSERT_EQ(S_OK, hr);
721 EXPECT_EQ(L"body", std::wstring(node_name, node_name.Length()));
722 EXPECT_EQ(NODETYPE_ELEMENT, node_type);
723 EXPECT_EQ(1, num_children);
724 node_name.Reset();
725 node_value.Reset();
726
727 base::win::ScopedComPtr<ISimpleDOMNode> checkbox_isimpledomnode;
728 hr = body_isimpledomnode->get_firstChild(
729 checkbox_isimpledomnode.Receive());
730 ASSERT_EQ(S_OK, hr);
731 hr = checkbox_isimpledomnode->get_nodeInfo(
732 node_name.Receive(), &name_space_id, node_value.Receive(), &num_children,
733 &unique_id, &node_type);
734 ASSERT_EQ(S_OK, hr);
735 EXPECT_EQ(L"input", std::wstring(node_name, node_name.Length()));
736 EXPECT_EQ(NODETYPE_ELEMENT, node_type);
737 EXPECT_EQ(0, num_children);
738 }
739
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestRoleGroup)740 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, TestRoleGroup) {
741 LoadInitialAccessibilityTreeFromHtml(
742 "<fieldset></fieldset><div role=group></div>");
743
744 // Check the browser's copy of the renderer accessibility tree.
745 AccessibleChecker grouping1_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
746 std::wstring());
747 AccessibleChecker grouping2_checker(std::wstring(), ROLE_SYSTEM_GROUPING,
748 std::wstring());
749 AccessibleChecker document_checker(std::wstring(), ROLE_SYSTEM_DOCUMENT,
750 std::wstring());
751 document_checker.AppendExpectedChild(&grouping1_checker);
752 document_checker.AppendExpectedChild(&grouping2_checker);
753 document_checker.CheckAccessible(GetRendererAccessible());
754 }
755
756 } // namespace content
757