1 // Copyright (c) 2011 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 <atlbase.h>
6 #include <vector>
7
8 #include "base/win/scoped_comptr.h"
9 #include "chrome/browser/automation/ui_controls.h"
10 #include "chrome/browser/renderer_host/render_widget_host_view_win.h"
11 #include "chrome/browser/ui/browser.h"
12 #include "chrome/browser/ui/browser_window.h"
13 #include "chrome/test/in_process_browser_test.h"
14 #include "chrome/test/ui_test_utils.h"
15 #include "content/browser/renderer_host/render_view_host.h"
16 #include "content/browser/tab_contents/tab_contents.h"
17 #include "content/common/notification_type.h"
18 #include "ia2_api_all.h" // Generated NOLINT
19 #include "ISimpleDOMNode.h" // Generated NOLINT
20
21 using std::auto_ptr;
22 using std::vector;
23 using std::wstring;
24
25 namespace {
26
27 class AccessibilityWinBrowserTest : public InProcessBrowserTest {
28 public:
AccessibilityWinBrowserTest()29 AccessibilityWinBrowserTest() {}
30
31 // InProcessBrowserTest
32 void SetUpInProcessBrowserTestFixture();
33
34 protected:
35 IAccessible* GetRendererAccessible();
36 void ExecuteScript(wstring script);
37 };
38
SetUpInProcessBrowserTestFixture()39 void AccessibilityWinBrowserTest::SetUpInProcessBrowserTestFixture() {
40 // If the mouse happens to be on the document then it will have the unexpected
41 // STATE_SYSTEM_HOTTRACKED state. Move it to a non-document location.
42 ui_controls::SendMouseMove(0, 0);
43 }
44
45 class AccessibleChecker {
46 public:
47 AccessibleChecker(
48 wstring expected_name,
49 int32 expected_role,
50 wstring expected_value);
51 AccessibleChecker(
52 wstring expected_name,
53 wstring expected_role,
54 wstring expected_value);
55
56 // Append an AccessibleChecker that verifies accessibility information for
57 // a child IAccessible. Order is important.
58 void AppendExpectedChild(AccessibleChecker* expected_child);
59
60 // Check that the name and role of the given IAccessible instance and its
61 // descendants match the expected names and roles that this object was
62 // initialized with.
63 void CheckAccessible(IAccessible* accessible);
64
65 // Set the expected value for this AccessibleChecker.
66 void SetExpectedValue(wstring expected_value);
67
68 // Set the expected state for this AccessibleChecker.
69 void SetExpectedState(LONG expected_state);
70
71 private:
72 void CheckAccessibleName(IAccessible* accessible);
73 void CheckAccessibleRole(IAccessible* accessible);
74 void CheckAccessibleValue(IAccessible* accessible);
75 void CheckAccessibleState(IAccessible* accessible);
76 void CheckAccessibleChildren(IAccessible* accessible);
77
78 private:
79 typedef vector<AccessibleChecker*> AccessibleCheckerVector;
80
81 // Expected accessible name. Checked against IAccessible::get_accName.
82 wstring name_;
83
84 // Expected accessible role. Checked against IAccessible::get_accRole.
85 CComVariant role_;
86
87 // Expected accessible value. Checked against IAccessible::get_accValue.
88 wstring value_;
89
90 // Expected accessible state. Checked against IAccessible::get_accState.
91 LONG state_;
92
93 // Expected accessible children. Checked using IAccessible::get_accChildCount
94 // and ::AccessibleChildren.
95 AccessibleCheckerVector children_;
96 };
97
CreateI4Variant(LONG value)98 VARIANT CreateI4Variant(LONG value) {
99 VARIANT variant = {0};
100
101 V_VT(&variant) = VT_I4;
102 V_I4(&variant) = value;
103
104 return variant;
105 }
106
GetAccessibleFromResultVariant(IAccessible * parent,VARIANT * var)107 IAccessible* GetAccessibleFromResultVariant(IAccessible* parent, VARIANT *var) {
108 switch (V_VT(var)) {
109 case VT_DISPATCH:
110 return CComQIPtr<IAccessible>(V_DISPATCH(var)).Detach();
111 break;
112
113 case VT_I4: {
114 CComPtr<IDispatch> dispatch;
115 HRESULT hr = parent->get_accChild(CreateI4Variant(V_I4(var)), &dispatch);
116 EXPECT_TRUE(SUCCEEDED(hr));
117 return CComQIPtr<IAccessible>(dispatch).Detach();
118 break;
119 }
120 }
121
122 return NULL;
123 }
124
QueryIAccessible2(IAccessible * accessible,IAccessible2 ** accessible2)125 HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) {
126 // TODO(ctguil): For some reason querying the IAccessible2 interface from
127 // IAccessible fails.
128 base::win::ScopedComPtr<IServiceProvider> service_provider;
129 HRESULT hr = accessible->QueryInterface(service_provider.Receive());
130 if (FAILED(hr))
131 return hr;
132
133 hr = service_provider->QueryService(IID_IAccessible2, accessible2);
134 return hr;
135 }
136
137 // Sets result to true if the child is located in the parent's tree. An
138 // exhustive search is perform here because we determine equality using
139 // IAccessible2::get_unique_id which is only supported by the child node.
AccessibleContainsAccessible(IAccessible * parent,IAccessible2 * child,bool * result)140 void AccessibleContainsAccessible(
141 IAccessible* parent, IAccessible2* child, bool* result) {
142 vector<base::win::ScopedComPtr<IAccessible>> accessible_list;
143 accessible_list.push_back(base::win::ScopedComPtr<IAccessible>(parent));
144
145 LONG unique_id;
146 HRESULT hr = child->get_uniqueID(&unique_id);
147 ASSERT_EQ(S_OK, hr);
148 *result = false;
149
150 while (accessible_list.size()) {
151 base::win::ScopedComPtr<IAccessible> accessible = accessible_list.back();
152 accessible_list.pop_back();
153
154 base::win::ScopedComPtr<IAccessible2> accessible2;
155 hr = QueryIAccessible2(accessible, accessible2.Receive());
156 if (SUCCEEDED(hr)) {
157 LONG child_id;
158 accessible2->get_uniqueID(&child_id);
159 if (child_id == unique_id) {
160 *result = true;
161 break;
162 }
163 }
164
165 LONG child_count;
166 hr = accessible->get_accChildCount(&child_count);
167 ASSERT_EQ(S_OK, hr);
168 if (child_count == 0)
169 continue;
170
171 auto_ptr<VARIANT> child_array(new VARIANT[child_count]);
172 LONG obtained_count = 0;
173 hr = AccessibleChildren(
174 accessible, 0, child_count, child_array.get(), &obtained_count);
175 ASSERT_EQ(S_OK, hr);
176 ASSERT_EQ(child_count, obtained_count);
177
178 for (int index = 0; index < obtained_count; index++) {
179 base::win::ScopedComPtr<IAccessible> child_accessible(
180 GetAccessibleFromResultVariant(accessible, &child_array.get()[index]));
181 if (child_accessible.get()) {
182 accessible_list.push_back(
183 base::win::ScopedComPtr<IAccessible>(child_accessible));
184 }
185 }
186 }
187 }
188
189 // Retrieve the MSAA client accessibility object for the Render Widget Host View
190 // of the selected tab.
191 IAccessible*
GetRendererAccessible()192 AccessibilityWinBrowserTest::GetRendererAccessible() {
193 HWND hwnd_render_widget_host_view =
194 browser()->GetSelectedTabContents()->GetRenderWidgetHostView()->
195 GetNativeView();
196
197 // Invoke windows screen reader detection by sending the WM_GETOBJECT message
198 // with kIdCustom as the LPARAM.
199 const int32 kIdCustom = 1;
200 SendMessage(
201 hwnd_render_widget_host_view, WM_GETOBJECT, OBJID_CLIENT, kIdCustom);
202
203 IAccessible* accessible;
204 HRESULT hr = AccessibleObjectFromWindow(
205 hwnd_render_widget_host_view, OBJID_CLIENT,
206 IID_IAccessible, reinterpret_cast<void**>(&accessible));
207 EXPECT_EQ(S_OK, hr);
208 EXPECT_NE(accessible, reinterpret_cast<IAccessible*>(NULL));
209
210 return accessible;
211 }
212
ExecuteScript(wstring script)213 void AccessibilityWinBrowserTest::ExecuteScript(wstring script) {
214 browser()->GetSelectedTabContents()->render_view_host()->
215 ExecuteJavascriptInWebFrame(L"", script);
216 }
217
AccessibleChecker(wstring expected_name,int32 expected_role,wstring expected_value)218 AccessibleChecker::AccessibleChecker(
219 wstring expected_name, int32 expected_role, wstring expected_value) :
220 name_(expected_name),
221 role_(expected_role),
222 value_(expected_value),
223 state_(-1) {
224 }
225
AccessibleChecker(wstring expected_name,wstring expected_role,wstring expected_value)226 AccessibleChecker::AccessibleChecker(
227 wstring expected_name, wstring expected_role, wstring expected_value) :
228 name_(expected_name),
229 role_(expected_role.c_str()),
230 value_(expected_value),
231 state_(-1) {
232 }
233
AppendExpectedChild(AccessibleChecker * expected_child)234 void AccessibleChecker::AppendExpectedChild(
235 AccessibleChecker* expected_child) {
236 children_.push_back(expected_child);
237 }
238
CheckAccessible(IAccessible * accessible)239 void AccessibleChecker::CheckAccessible(IAccessible* accessible) {
240 CheckAccessibleName(accessible);
241 CheckAccessibleRole(accessible);
242 CheckAccessibleValue(accessible);
243 CheckAccessibleState(accessible);
244 CheckAccessibleChildren(accessible);
245 }
246
SetExpectedValue(wstring expected_value)247 void AccessibleChecker::SetExpectedValue(wstring expected_value) {
248 value_ = expected_value;
249 }
250
SetExpectedState(LONG expected_state)251 void AccessibleChecker::SetExpectedState(LONG expected_state) {
252 state_ = expected_state;
253 }
254
CheckAccessibleName(IAccessible * accessible)255 void AccessibleChecker::CheckAccessibleName(IAccessible* accessible) {
256 CComBSTR name;
257 HRESULT hr =
258 accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name);
259
260 if (name_.empty()) {
261 // If the object doesn't have name S_FALSE should be returned.
262 EXPECT_EQ(hr, S_FALSE);
263 } else {
264 // Test that the correct string was returned.
265 EXPECT_EQ(S_OK, hr);
266 EXPECT_STREQ(name_.c_str(),
267 wstring(name.m_str, SysStringLen(name)).c_str());
268 }
269 }
270
CheckAccessibleRole(IAccessible * accessible)271 void AccessibleChecker::CheckAccessibleRole(IAccessible* accessible) {
272 VARIANT var_role = {0};
273 HRESULT hr =
274 accessible->get_accRole(CreateI4Variant(CHILDID_SELF), &var_role);
275 ASSERT_EQ(S_OK, hr);
276 EXPECT_TRUE(role_ == var_role);
277 }
278
CheckAccessibleValue(IAccessible * accessible)279 void AccessibleChecker::CheckAccessibleValue(IAccessible* accessible) {
280 CComBSTR value;
281 HRESULT hr =
282 accessible->get_accValue(CreateI4Variant(CHILDID_SELF), &value);
283 EXPECT_EQ(S_OK, hr);
284
285 // Test that the correct string was returned.
286 EXPECT_STREQ(value_.c_str(),
287 wstring(value.m_str, SysStringLen(value)).c_str());
288 }
289
CheckAccessibleState(IAccessible * accessible)290 void AccessibleChecker::CheckAccessibleState(IAccessible* accessible) {
291 if (state_ < 0)
292 return;
293
294 VARIANT var_state = {0};
295 HRESULT hr =
296 accessible->get_accState(CreateI4Variant(CHILDID_SELF), &var_state);
297 EXPECT_EQ(S_OK, hr);
298 ASSERT_EQ(VT_I4, V_VT(&var_state));
299 EXPECT_EQ(state_, V_I4(&var_state));
300 }
301
CheckAccessibleChildren(IAccessible * parent)302 void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) {
303 LONG child_count = 0;
304 HRESULT hr = parent->get_accChildCount(&child_count);
305 EXPECT_EQ(S_OK, hr);
306 ASSERT_EQ(child_count, children_.size());
307
308 auto_ptr<VARIANT> child_array(new VARIANT[child_count]);
309 LONG obtained_count = 0;
310 hr = AccessibleChildren(parent, 0, child_count,
311 child_array.get(), &obtained_count);
312 ASSERT_EQ(S_OK, hr);
313 ASSERT_EQ(child_count, obtained_count);
314
315 VARIANT* child = child_array.get();
316 for (AccessibleCheckerVector::iterator child_checker = children_.begin();
317 child_checker != children_.end();
318 ++child_checker, ++child) {
319 base::win::ScopedComPtr<IAccessible> child_accessible;
320 child_accessible.Attach(GetAccessibleFromResultVariant(parent, child));
321 ASSERT_TRUE(child_accessible.get());
322 (*child_checker)->CheckAccessible(child_accessible);
323 }
324 }
325
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestRendererAccessibilityTree)326 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
327 TestRendererAccessibilityTree) {
328 // The initial accessible returned should have state STATE_SYSTEM_BUSY while
329 // the accessibility tree is being requested from the renderer.
330 AccessibleChecker document1_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
331 document1_checker.SetExpectedState(
332 STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED |
333 STATE_SYSTEM_BUSY);
334 document1_checker.CheckAccessible(GetRendererAccessible());
335
336 // Wait for the initial accessibility tree to load. Busy state should clear.
337 ui_test_utils::WaitForNotification(
338 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
339 document1_checker.SetExpectedState(
340 STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED);
341 document1_checker.CheckAccessible(GetRendererAccessible());
342
343 GURL tree_url(
344 "data:text/html,<html><head><title>Accessibility Win Test</title></head>"
345 "<body><input type='button' value='push' /><input type='checkbox' />"
346 "</body></html>");
347 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
348 ui_test_utils::WaitForNotification(
349 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
350
351 // Check the browser's copy of the renderer accessibility tree.
352 AccessibleChecker button_checker(L"push", ROLE_SYSTEM_PUSHBUTTON, L"push");
353 AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON, L"");
354 AccessibleChecker body_checker(L"", L"body", L"");
355 AccessibleChecker document2_checker(
356 L"Accessibility Win Test", ROLE_SYSTEM_DOCUMENT, L"");
357 body_checker.AppendExpectedChild(&button_checker);
358 body_checker.AppendExpectedChild(&checkbox_checker);
359 document2_checker.AppendExpectedChild(&body_checker);
360 document2_checker.CheckAccessible(GetRendererAccessible());
361
362 // Check that document accessible has a parent accessible.
363 base::win::ScopedComPtr<IAccessible> document_accessible(
364 GetRendererAccessible());
365 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
366 base::win::ScopedComPtr<IDispatch> parent_dispatch;
367 HRESULT hr = document_accessible->get_accParent(parent_dispatch.Receive());
368 EXPECT_EQ(S_OK, hr);
369 EXPECT_NE(parent_dispatch, reinterpret_cast<IDispatch*>(NULL));
370
371 // Navigate to another page.
372 GURL about_url("about:");
373 ui_test_utils::NavigateToURL(browser(), about_url);
374
375 // Verify that the IAccessible reference still points to a valid object and
376 // that calls to its methods fail since the tree is no longer valid after
377 // the page navagation.
378 CComBSTR name;
379 hr = document_accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name);
380 ASSERT_EQ(E_FAIL, hr);
381 }
382
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationActiveDescendantChanged)383 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
384 TestNotificationActiveDescendantChanged) {
385 GURL tree_url("data:text/html,<ul tabindex='-1' role='radiogroup'><li id='li'"
386 ">li</li></ul>");
387 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
388 GetRendererAccessible();
389 ui_test_utils::WaitForNotification(
390 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
391
392 // Check the browser's copy of the renderer accessibility tree.
393 AccessibleChecker list_marker_checker(L"", ROLE_SYSTEM_LISTITEM, L"\x2022");
394 AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT, L"");
395 AccessibleChecker list_item_checker(L"", ROLE_SYSTEM_LISTITEM, L"");
396 list_item_checker.SetExpectedState(
397 STATE_SYSTEM_READONLY);
398 AccessibleChecker radio_group_checker(L"", ROLE_SYSTEM_GROUPING, L"");
399 radio_group_checker.SetExpectedState(
400 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
401 AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
402 list_item_checker.AppendExpectedChild(&list_marker_checker);
403 list_item_checker.AppendExpectedChild(&static_text_checker);
404 radio_group_checker.AppendExpectedChild(&list_item_checker);
405 document_checker.AppendExpectedChild(&radio_group_checker);
406 document_checker.CheckAccessible(GetRendererAccessible());
407
408 // Set focus to the radio group.
409 ExecuteScript(L"document.body.children[0].focus()");
410 ui_test_utils::WaitForNotification(
411 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
412
413 // Check that the accessibility tree of the browser has been updated.
414 radio_group_checker.SetExpectedState(
415 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED);
416 document_checker.CheckAccessible(GetRendererAccessible());
417
418 // Set the active descendant of the radio group
419 ExecuteScript(
420 L"document.body.children[0].setAttribute('aria-activedescendant', 'li')");
421 ui_test_utils::WaitForNotification(
422 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
423
424 // Check that the accessibility tree of the browser has been updated.
425 list_item_checker.SetExpectedState(
426 STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED);
427 radio_group_checker.SetExpectedState(
428 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
429 document_checker.CheckAccessible(GetRendererAccessible());
430 }
431
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationCheckedStateChanged)432 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
433 TestNotificationCheckedStateChanged) {
434 GURL tree_url("data:text/html,<body><input type='checkbox' /></body>");
435 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
436 GetRendererAccessible();
437 ui_test_utils::WaitForNotification(
438 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
439
440 // Check the browser's copy of the renderer accessibility tree.
441 AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON, L"");
442 checkbox_checker.SetExpectedState(
443 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
444 AccessibleChecker body_checker(L"", L"body", L"");
445 AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
446 body_checker.AppendExpectedChild(&checkbox_checker);
447 document_checker.AppendExpectedChild(&body_checker);
448 document_checker.CheckAccessible(GetRendererAccessible());
449
450 // Check the checkbox.
451 ExecuteScript(L"document.body.children[0].checked=true");
452 ui_test_utils::WaitForNotification(
453 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
454
455 // Check that the accessibility tree of the browser has been updated.
456 checkbox_checker.SetExpectedState(
457 STATE_SYSTEM_CHECKED | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
458 document_checker.CheckAccessible(GetRendererAccessible());
459 }
460
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationChildrenChanged)461 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
462 TestNotificationChildrenChanged) {
463 // The role attribute causes the node to be in the accessibility tree.
464 GURL tree_url(
465 "data:text/html,<body role=group></body>");
466 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
467 GetRendererAccessible();
468 ui_test_utils::WaitForNotification(
469 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
470
471 // Check the browser's copy of the renderer accessibility tree.
472 AccessibleChecker body_checker(L"", L"body", L"");
473 AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
474 document_checker.AppendExpectedChild(&body_checker);
475 document_checker.CheckAccessible(GetRendererAccessible());
476
477 // Change the children of the document body.
478 ExecuteScript(L"document.body.innerHTML='<b>new text</b>'");
479 ui_test_utils::WaitForNotification(
480 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
481
482 // Check that the accessibility tree of the browser has been updated.
483 AccessibleChecker text_checker(L"new text", ROLE_SYSTEM_TEXT, L"");
484 body_checker.AppendExpectedChild(&text_checker);
485 document_checker.CheckAccessible(GetRendererAccessible());
486 }
487
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationChildrenChanged2)488 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
489 TestNotificationChildrenChanged2) {
490 // The role attribute causes the node to be in the accessibility tree.
491 GURL tree_url(
492 "data:text/html,<div role=group style='visibility: hidden'>text"
493 "</div>");
494 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
495 GetRendererAccessible();
496 ui_test_utils::WaitForNotification(
497 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
498
499 // Check the accessible tree of the browser.
500 AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
501 document_checker.CheckAccessible(GetRendererAccessible());
502
503 // Change the children of the document body.
504 ExecuteScript(L"document.body.children[0].style.visibility='visible'");
505 ui_test_utils::WaitForNotification(
506 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
507
508 // Check that the accessibility tree of the browser has been updated.
509 AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_TEXT, L"");
510 AccessibleChecker div_checker(L"", L"div", L"");
511 document_checker.AppendExpectedChild(&div_checker);
512 div_checker.AppendExpectedChild(&static_text_checker);
513 document_checker.CheckAccessible(GetRendererAccessible());
514 }
515
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationFocusChanged)516 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
517 TestNotificationFocusChanged) {
518 // The role attribute causes the node to be in the accessibility tree.
519 GURL tree_url(
520 "data:text/html,<div role=group tabindex='-1'></div>");
521 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
522 GetRendererAccessible();
523 ui_test_utils::WaitForNotification(
524 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
525
526 // Check the browser's copy of the renderer accessibility tree.
527 AccessibleChecker div_checker(L"", L"div", L"");
528 div_checker.SetExpectedState(
529 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_READONLY);
530 AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
531 document_checker.AppendExpectedChild(&div_checker);
532 document_checker.CheckAccessible(GetRendererAccessible());
533
534 // Focus the div in the document
535 ExecuteScript(L"document.body.children[0].focus()");
536 ui_test_utils::WaitForNotification(
537 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
538
539 // Check that the accessibility tree of the browser has been updated.
540 div_checker.SetExpectedState(
541 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED);
542 document_checker.CheckAccessible(GetRendererAccessible());
543
544 // Focus the document accessible. This will un-focus the current node.
545 base::win::ScopedComPtr<IAccessible> document_accessible(
546 GetRendererAccessible());
547 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
548 HRESULT hr = document_accessible->accSelect(
549 SELFLAG_TAKEFOCUS, CreateI4Variant(CHILDID_SELF));
550 ASSERT_EQ(S_OK, hr);
551 ui_test_utils::WaitForNotification(
552 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
553
554 // Check that the accessibility tree of the browser has been updated.
555 div_checker.SetExpectedState(
556 STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
557 document_checker.CheckAccessible(GetRendererAccessible());
558 }
559
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,TestNotificationValueChanged)560 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
561 TestNotificationValueChanged) {
562 GURL tree_url("data:text/html,<body><input type='text' value='old value'/>"
563 "</body>");
564 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
565 GetRendererAccessible();
566 ui_test_utils::WaitForNotification(
567 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
568
569 // Check the browser's copy of the renderer accessibility tree.
570
571 AccessibleChecker text_field_div_checker(L"", L"div", L"");
572 AccessibleChecker text_field_checker(L"", ROLE_SYSTEM_TEXT, L"old value");
573 text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
574 AccessibleChecker body_checker(L"", L"body", L"");
575 AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
576 text_field_checker.AppendExpectedChild(&text_field_div_checker);
577 body_checker.AppendExpectedChild(&text_field_checker);
578 document_checker.AppendExpectedChild(&body_checker);
579 document_checker.CheckAccessible(GetRendererAccessible());
580
581 // Set the value of the text control
582 ExecuteScript(L"document.body.children[0].value='new value'");
583 ui_test_utils::WaitForNotification(
584 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
585
586 // Check that the accessibility tree of the browser has been updated.
587 text_field_checker.SetExpectedValue(L"new value");
588 document_checker.CheckAccessible(GetRendererAccessible());
589 }
590
591 // FAILS crbug.com/54220
592 // This test verifies that browser-side cache of the renderer accessibility
593 // tree is reachable from the browser's tree. Tools that analyze windows
594 // accessibility trees like AccExplorer32 should be able to drill into the
595 // cached renderer accessibility tree.
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,DISABLED_ContainsRendererAccessibilityTree)596 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
597 DISABLED_ContainsRendererAccessibilityTree) {
598 GURL tree_url("data:text/html,<body><input type='checkbox' /></body>");
599 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
600 GetRendererAccessible();
601 ui_test_utils::WaitForNotification(
602 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
603
604 // Get the accessibility object for the browser window.
605 HWND browser_hwnd = browser()->window()->GetNativeHandle();
606 base::win::ScopedComPtr<IAccessible> browser_accessible;
607 HRESULT hr = AccessibleObjectFromWindow(
608 browser_hwnd,
609 OBJID_WINDOW,
610 IID_IAccessible,
611 reinterpret_cast<void**>(browser_accessible.Receive()));
612 ASSERT_EQ(S_OK, hr);
613
614 // Get the accessibility object for the renderer client document.
615 base::win::ScopedComPtr<IAccessible> document_accessible(
616 GetRendererAccessible());
617 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
618 base::win::ScopedComPtr<IAccessible2> document_accessible2;
619 hr = QueryIAccessible2(document_accessible, document_accessible2.Receive());
620 ASSERT_EQ(S_OK, hr);
621
622 // TODO(ctguil): Pointer comparison of retrieved IAccessible pointers dosen't
623 // seem to work for here. Perhaps make IAccessible2 available in views to make
624 // unique id comparison available.
625 bool found = false;
626 base::win::ScopedComPtr<IAccessible> parent = document_accessible;
627 while (parent.get()) {
628 base::win::ScopedComPtr<IDispatch> parent_dispatch;
629 hr = parent->get_accParent(parent_dispatch.Receive());
630 ASSERT_TRUE(SUCCEEDED(hr));
631 if (!parent_dispatch.get()) {
632 ASSERT_EQ(hr, S_FALSE);
633 break;
634 }
635
636 parent.Release();
637 hr = parent_dispatch.QueryInterface(parent.Receive());
638 ASSERT_EQ(S_OK, hr);
639
640 if (parent.get() == browser_accessible.get()) {
641 found = true;
642 break;
643 }
644 }
645
646 // If pointer comparison fails resort to the exhuasive search that can use
647 // IAccessible2::get_unique_id for equality comparison.
648 if (!found) {
649 AccessibleContainsAccessible(
650 browser_accessible, document_accessible2, &found);
651 }
652
653 ASSERT_EQ(found, true);
654 }
655
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,SupportsISimpleDOM)656 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
657 SupportsISimpleDOM) {
658 GURL tree_url("data:text/html,<body><input type='checkbox' /></body>");
659 browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
660 GetRendererAccessible();
661 ui_test_utils::WaitForNotification(
662 NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
663
664 // Get the IAccessible object for the document.
665 base::win::ScopedComPtr<IAccessible> document_accessible(
666 GetRendererAccessible());
667 ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
668
669 // Get the ISimpleDOM object for the document.
670 base::win::ScopedComPtr<IServiceProvider> service_provider;
671 HRESULT hr = static_cast<IAccessible*>(document_accessible)->QueryInterface(
672 service_provider.Receive());
673 ASSERT_EQ(S_OK, hr);
674 const GUID refguid = {0x0c539790, 0x12e4, 0x11cf,
675 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
676 base::win::ScopedComPtr<ISimpleDOMNode> document_isimpledomnode;
677 hr = static_cast<IServiceProvider *>(service_provider)->QueryService(
678 refguid, IID_ISimpleDOMNode,
679 reinterpret_cast<void**>(document_isimpledomnode.Receive()));
680 ASSERT_EQ(S_OK, hr);
681
682 BSTR node_name;
683 short name_space_id; // NOLINT
684 BSTR node_value;
685 unsigned int num_children;
686 unsigned int unique_id;
687 unsigned short node_type; // NOLINT
688 hr = document_isimpledomnode->get_nodeInfo(
689 &node_name, &name_space_id, &node_value, &num_children, &unique_id,
690 &node_type);
691 ASSERT_EQ(S_OK, hr);
692 EXPECT_EQ(NODETYPE_DOCUMENT, node_type);
693 EXPECT_EQ(1, num_children);
694
695 base::win::ScopedComPtr<ISimpleDOMNode> body_isimpledomnode;
696 hr = document_isimpledomnode->get_firstChild(
697 body_isimpledomnode.Receive());
698 ASSERT_EQ(S_OK, hr);
699 hr = body_isimpledomnode->get_nodeInfo(
700 &node_name, &name_space_id, &node_value, &num_children, &unique_id,
701 &node_type);
702 ASSERT_EQ(S_OK, hr);
703 EXPECT_STREQ(L"body", wstring(node_name, SysStringLen(node_name)).c_str());
704 EXPECT_EQ(NODETYPE_ELEMENT, node_type);
705 EXPECT_EQ(1, num_children);
706
707 base::win::ScopedComPtr<ISimpleDOMNode> checkbox_isimpledomnode;
708 hr = body_isimpledomnode->get_firstChild(
709 checkbox_isimpledomnode.Receive());
710 ASSERT_EQ(S_OK, hr);
711 hr = checkbox_isimpledomnode->get_nodeInfo(
712 &node_name, &name_space_id, &node_value, &num_children, &unique_id,
713 &node_type);
714 ASSERT_EQ(S_OK, hr);
715 EXPECT_STREQ(L"input", wstring(node_name, SysStringLen(node_name)).c_str());
716 EXPECT_EQ(NODETYPE_ELEMENT, node_type);
717 EXPECT_EQ(0, num_children);
718 }
719 } // namespace.
720