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
21using std::auto_ptr;
22using std::vector;
23using std::wstring;
24
25namespace {
26
27class AccessibilityWinBrowserTest : public InProcessBrowserTest {
28 public:
29  AccessibilityWinBrowserTest() {}
30
31  // InProcessBrowserTest
32  void SetUpInProcessBrowserTestFixture();
33
34 protected:
35  IAccessible* GetRendererAccessible();
36  void ExecuteScript(wstring script);
37};
38
39void 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
45class 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
98VARIANT 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
107IAccessible* 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
125HRESULT 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.
140void 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.
191IAccessible*
192AccessibilityWinBrowserTest::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
213void AccessibilityWinBrowserTest::ExecuteScript(wstring script) {
214  browser()->GetSelectedTabContents()->render_view_host()->
215      ExecuteJavascriptInWebFrame(L"", script);
216}
217
218AccessibleChecker::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
226AccessibleChecker::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
234void AccessibleChecker::AppendExpectedChild(
235    AccessibleChecker* expected_child) {
236  children_.push_back(expected_child);
237}
238
239void AccessibleChecker::CheckAccessible(IAccessible* accessible) {
240  CheckAccessibleName(accessible);
241  CheckAccessibleRole(accessible);
242  CheckAccessibleValue(accessible);
243  CheckAccessibleState(accessible);
244  CheckAccessibleChildren(accessible);
245}
246
247void AccessibleChecker::SetExpectedValue(wstring expected_value) {
248  value_ = expected_value;
249}
250
251void AccessibleChecker::SetExpectedState(LONG expected_state) {
252  state_ = expected_state;
253}
254
255void 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
271void 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
279void 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
290void 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
302void 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
326IN_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
383IN_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
432IN_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
461IN_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
488IN_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
516IN_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
560IN_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.
596IN_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
656IN_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