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