普通文本  |  720行  |  27.28 KB

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <atlbase.h>
#include <vector>

#include "base/win/scoped_comptr.h"
#include "chrome/browser/automation/ui_controls.h"
#include "chrome/browser/renderer_host/render_widget_host_view_win.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/common/notification_type.h"
#include "ia2_api_all.h"  // Generated    NOLINT
#include "ISimpleDOMNode.h"  // Generated   NOLINT

using std::auto_ptr;
using std::vector;
using std::wstring;

namespace {

class AccessibilityWinBrowserTest : public InProcessBrowserTest {
 public:
  AccessibilityWinBrowserTest() {}

  // InProcessBrowserTest
  void SetUpInProcessBrowserTestFixture();

 protected:
  IAccessible* GetRendererAccessible();
  void ExecuteScript(wstring script);
};

void AccessibilityWinBrowserTest::SetUpInProcessBrowserTestFixture() {
  // If the mouse happens to be on the document then it will have the unexpected
  // STATE_SYSTEM_HOTTRACKED state. Move it to a non-document location.
  ui_controls::SendMouseMove(0, 0);
}

class AccessibleChecker {
 public:
  AccessibleChecker(
      wstring expected_name,
      int32 expected_role,
      wstring expected_value);
  AccessibleChecker(
      wstring expected_name,
      wstring expected_role,
      wstring expected_value);

  // Append an AccessibleChecker that verifies accessibility information for
  // a child IAccessible. Order is important.
  void AppendExpectedChild(AccessibleChecker* expected_child);

  // Check that the name and role of the given IAccessible instance and its
  // descendants match the expected names and roles that this object was
  // initialized with.
  void CheckAccessible(IAccessible* accessible);

  // Set the expected value for this AccessibleChecker.
  void SetExpectedValue(wstring expected_value);

  // Set the expected state for this AccessibleChecker.
  void SetExpectedState(LONG expected_state);

 private:
  void CheckAccessibleName(IAccessible* accessible);
  void CheckAccessibleRole(IAccessible* accessible);
  void CheckAccessibleValue(IAccessible* accessible);
  void CheckAccessibleState(IAccessible* accessible);
  void CheckAccessibleChildren(IAccessible* accessible);

 private:
  typedef vector<AccessibleChecker*> AccessibleCheckerVector;

  // Expected accessible name. Checked against IAccessible::get_accName.
  wstring name_;

  // Expected accessible role. Checked against IAccessible::get_accRole.
  CComVariant role_;

  // Expected accessible value. Checked against IAccessible::get_accValue.
  wstring value_;

  // Expected accessible state. Checked against IAccessible::get_accState.
  LONG state_;

  // Expected accessible children. Checked using IAccessible::get_accChildCount
  // and ::AccessibleChildren.
  AccessibleCheckerVector children_;
};

VARIANT CreateI4Variant(LONG value) {
  VARIANT variant = {0};

  V_VT(&variant) = VT_I4;
  V_I4(&variant) = value;

  return variant;
}

IAccessible* GetAccessibleFromResultVariant(IAccessible* parent, VARIANT *var) {
  switch (V_VT(var)) {
    case VT_DISPATCH:
      return CComQIPtr<IAccessible>(V_DISPATCH(var)).Detach();
      break;

    case VT_I4: {
      CComPtr<IDispatch> dispatch;
      HRESULT hr = parent->get_accChild(CreateI4Variant(V_I4(var)), &dispatch);
      EXPECT_TRUE(SUCCEEDED(hr));
      return CComQIPtr<IAccessible>(dispatch).Detach();
      break;
    }
  }

  return NULL;
}

HRESULT QueryIAccessible2(IAccessible* accessible, IAccessible2** accessible2) {
  // TODO(ctguil): For some reason querying the IAccessible2 interface from
  // IAccessible fails.
  base::win::ScopedComPtr<IServiceProvider> service_provider;
  HRESULT hr = accessible->QueryInterface(service_provider.Receive());
  if (FAILED(hr))
    return hr;

  hr = service_provider->QueryService(IID_IAccessible2, accessible2);
  return hr;
}

// Sets result to true if the child is located in the parent's tree. An
// exhustive search is perform here because we determine equality using
// IAccessible2::get_unique_id which is only supported by the child node.
void AccessibleContainsAccessible(
    IAccessible* parent, IAccessible2* child, bool* result) {
  vector<base::win::ScopedComPtr<IAccessible>> accessible_list;
  accessible_list.push_back(base::win::ScopedComPtr<IAccessible>(parent));

  LONG unique_id;
  HRESULT hr = child->get_uniqueID(&unique_id);
  ASSERT_EQ(S_OK, hr);
  *result = false;

  while (accessible_list.size()) {
    base::win::ScopedComPtr<IAccessible> accessible = accessible_list.back();
    accessible_list.pop_back();

    base::win::ScopedComPtr<IAccessible2> accessible2;
    hr = QueryIAccessible2(accessible, accessible2.Receive());
    if (SUCCEEDED(hr)) {
      LONG child_id;
      accessible2->get_uniqueID(&child_id);
      if (child_id == unique_id) {
        *result = true;
        break;
      }
    }

    LONG child_count;
    hr = accessible->get_accChildCount(&child_count);
    ASSERT_EQ(S_OK, hr);
    if (child_count == 0)
      continue;

    auto_ptr<VARIANT> child_array(new VARIANT[child_count]);
    LONG obtained_count = 0;
    hr = AccessibleChildren(
        accessible, 0, child_count, child_array.get(), &obtained_count);
    ASSERT_EQ(S_OK, hr);
    ASSERT_EQ(child_count, obtained_count);

    for (int index = 0; index < obtained_count; index++) {
      base::win::ScopedComPtr<IAccessible> child_accessible(
        GetAccessibleFromResultVariant(accessible, &child_array.get()[index]));
      if (child_accessible.get()) {
        accessible_list.push_back(
            base::win::ScopedComPtr<IAccessible>(child_accessible));
      }
    }
  }
}

// Retrieve the MSAA client accessibility object for the Render Widget Host View
// of the selected tab.
IAccessible*
AccessibilityWinBrowserTest::GetRendererAccessible() {
  HWND hwnd_render_widget_host_view =
      browser()->GetSelectedTabContents()->GetRenderWidgetHostView()->
          GetNativeView();

  // Invoke windows screen reader detection by sending the WM_GETOBJECT message
  // with kIdCustom as the LPARAM.
  const int32 kIdCustom = 1;
  SendMessage(
      hwnd_render_widget_host_view, WM_GETOBJECT, OBJID_CLIENT, kIdCustom);

  IAccessible* accessible;
  HRESULT hr = AccessibleObjectFromWindow(
      hwnd_render_widget_host_view, OBJID_CLIENT,
      IID_IAccessible, reinterpret_cast<void**>(&accessible));
  EXPECT_EQ(S_OK, hr);
  EXPECT_NE(accessible, reinterpret_cast<IAccessible*>(NULL));

  return accessible;
}

void AccessibilityWinBrowserTest::ExecuteScript(wstring script) {
  browser()->GetSelectedTabContents()->render_view_host()->
      ExecuteJavascriptInWebFrame(L"", script);
}

AccessibleChecker::AccessibleChecker(
    wstring expected_name, int32 expected_role, wstring expected_value) :
    name_(expected_name),
    role_(expected_role),
    value_(expected_value),
    state_(-1) {
}

AccessibleChecker::AccessibleChecker(
    wstring expected_name, wstring expected_role, wstring expected_value) :
    name_(expected_name),
    role_(expected_role.c_str()),
    value_(expected_value),
    state_(-1) {
}

void AccessibleChecker::AppendExpectedChild(
    AccessibleChecker* expected_child) {
  children_.push_back(expected_child);
}

void AccessibleChecker::CheckAccessible(IAccessible* accessible) {
  CheckAccessibleName(accessible);
  CheckAccessibleRole(accessible);
  CheckAccessibleValue(accessible);
  CheckAccessibleState(accessible);
  CheckAccessibleChildren(accessible);
}

void AccessibleChecker::SetExpectedValue(wstring expected_value) {
  value_ = expected_value;
}

void AccessibleChecker::SetExpectedState(LONG expected_state) {
  state_ = expected_state;
}

void AccessibleChecker::CheckAccessibleName(IAccessible* accessible) {
  CComBSTR name;
  HRESULT hr =
      accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name);

  if (name_.empty()) {
    // If the object doesn't have name S_FALSE should be returned.
    EXPECT_EQ(hr, S_FALSE);
  } else {
    // Test that the correct string was returned.
    EXPECT_EQ(S_OK, hr);
    EXPECT_STREQ(name_.c_str(),
                 wstring(name.m_str, SysStringLen(name)).c_str());
  }
}

void AccessibleChecker::CheckAccessibleRole(IAccessible* accessible) {
  VARIANT var_role = {0};
  HRESULT hr =
      accessible->get_accRole(CreateI4Variant(CHILDID_SELF), &var_role);
  ASSERT_EQ(S_OK, hr);
  EXPECT_TRUE(role_ == var_role);
}

void AccessibleChecker::CheckAccessibleValue(IAccessible* accessible) {
  CComBSTR value;
  HRESULT hr =
      accessible->get_accValue(CreateI4Variant(CHILDID_SELF), &value);
  EXPECT_EQ(S_OK, hr);

  // Test that the correct string was returned.
  EXPECT_STREQ(value_.c_str(),
               wstring(value.m_str, SysStringLen(value)).c_str());
}

void AccessibleChecker::CheckAccessibleState(IAccessible* accessible) {
  if (state_ < 0)
    return;

  VARIANT var_state = {0};
  HRESULT hr =
      accessible->get_accState(CreateI4Variant(CHILDID_SELF), &var_state);
  EXPECT_EQ(S_OK, hr);
  ASSERT_EQ(VT_I4, V_VT(&var_state));
  EXPECT_EQ(state_, V_I4(&var_state));
}

void AccessibleChecker::CheckAccessibleChildren(IAccessible* parent) {
  LONG child_count = 0;
  HRESULT hr = parent->get_accChildCount(&child_count);
  EXPECT_EQ(S_OK, hr);
  ASSERT_EQ(child_count, children_.size());

  auto_ptr<VARIANT> child_array(new VARIANT[child_count]);
  LONG obtained_count = 0;
  hr = AccessibleChildren(parent, 0, child_count,
                          child_array.get(), &obtained_count);
  ASSERT_EQ(S_OK, hr);
  ASSERT_EQ(child_count, obtained_count);

  VARIANT* child = child_array.get();
  for (AccessibleCheckerVector::iterator child_checker = children_.begin();
       child_checker != children_.end();
       ++child_checker, ++child) {
    base::win::ScopedComPtr<IAccessible> child_accessible;
    child_accessible.Attach(GetAccessibleFromResultVariant(parent, child));
    ASSERT_TRUE(child_accessible.get());
    (*child_checker)->CheckAccessible(child_accessible);
  }
}

IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
                       TestRendererAccessibilityTree) {
  // The initial accessible returned should have state STATE_SYSTEM_BUSY while
  // the accessibility tree is being requested from the renderer.
  AccessibleChecker document1_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
  document1_checker.SetExpectedState(
      STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED |
      STATE_SYSTEM_BUSY);
  document1_checker.CheckAccessible(GetRendererAccessible());

  // Wait for the initial accessibility tree to load. Busy state should clear.
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);
  document1_checker.SetExpectedState(
      STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_FOCUSED);
  document1_checker.CheckAccessible(GetRendererAccessible());

  GURL tree_url(
      "data:text/html,<html><head><title>Accessibility Win Test</title></head>"
      "<body><input type='button' value='push' /><input type='checkbox' />"
      "</body></html>");
  browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check the browser's copy of the renderer accessibility tree.
  AccessibleChecker button_checker(L"push", ROLE_SYSTEM_PUSHBUTTON, L"push");
  AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON, L"");
  AccessibleChecker body_checker(L"", L"body", L"");
  AccessibleChecker document2_checker(
    L"Accessibility Win Test", ROLE_SYSTEM_DOCUMENT, L"");
  body_checker.AppendExpectedChild(&button_checker);
  body_checker.AppendExpectedChild(&checkbox_checker);
  document2_checker.AppendExpectedChild(&body_checker);
  document2_checker.CheckAccessible(GetRendererAccessible());

  // Check that document accessible has a parent accessible.
  base::win::ScopedComPtr<IAccessible> document_accessible(
      GetRendererAccessible());
  ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
  base::win::ScopedComPtr<IDispatch> parent_dispatch;
  HRESULT hr = document_accessible->get_accParent(parent_dispatch.Receive());
  EXPECT_EQ(S_OK, hr);
  EXPECT_NE(parent_dispatch, reinterpret_cast<IDispatch*>(NULL));

  // Navigate to another page.
  GURL about_url("about:");
  ui_test_utils::NavigateToURL(browser(), about_url);

  // Verify that the IAccessible reference still points to a valid object and
  // that calls to its methods fail since the tree is no longer valid after
  // the page navagation.
  CComBSTR name;
  hr = document_accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name);
  ASSERT_EQ(E_FAIL, hr);
}

IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
                       TestNotificationActiveDescendantChanged) {
  GURL tree_url("data:text/html,<ul tabindex='-1' role='radiogroup'><li id='li'"
      ">li</li></ul>");
  browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
  GetRendererAccessible();
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check the browser's copy of the renderer accessibility tree.
  AccessibleChecker list_marker_checker(L"", ROLE_SYSTEM_LISTITEM, L"\x2022");
  AccessibleChecker static_text_checker(L"li", ROLE_SYSTEM_TEXT, L"");
  AccessibleChecker list_item_checker(L"", ROLE_SYSTEM_LISTITEM, L"");
  list_item_checker.SetExpectedState(
      STATE_SYSTEM_READONLY);
  AccessibleChecker radio_group_checker(L"", ROLE_SYSTEM_GROUPING, L"");
  radio_group_checker.SetExpectedState(
      STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
  AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
  list_item_checker.AppendExpectedChild(&list_marker_checker);
  list_item_checker.AppendExpectedChild(&static_text_checker);
  radio_group_checker.AppendExpectedChild(&list_item_checker);
  document_checker.AppendExpectedChild(&radio_group_checker);
  document_checker.CheckAccessible(GetRendererAccessible());

  // Set focus to the radio group.
  ExecuteScript(L"document.body.children[0].focus()");
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check that the accessibility tree of the browser has been updated.
  radio_group_checker.SetExpectedState(
      STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED);
  document_checker.CheckAccessible(GetRendererAccessible());

  // Set the active descendant of the radio group
  ExecuteScript(
      L"document.body.children[0].setAttribute('aria-activedescendant', 'li')");
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check that the accessibility tree of the browser has been updated.
  list_item_checker.SetExpectedState(
      STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED);
  radio_group_checker.SetExpectedState(
      STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
  document_checker.CheckAccessible(GetRendererAccessible());
}

IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
                       TestNotificationCheckedStateChanged) {
  GURL tree_url("data:text/html,<body><input type='checkbox' /></body>");
  browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
  GetRendererAccessible();
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check the browser's copy of the renderer accessibility tree.
  AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON, L"");
  checkbox_checker.SetExpectedState(
      STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
  AccessibleChecker body_checker(L"", L"body", L"");
  AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
  body_checker.AppendExpectedChild(&checkbox_checker);
  document_checker.AppendExpectedChild(&body_checker);
  document_checker.CheckAccessible(GetRendererAccessible());

  // Check the checkbox.
  ExecuteScript(L"document.body.children[0].checked=true");
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check that the accessibility tree of the browser has been updated.
  checkbox_checker.SetExpectedState(
      STATE_SYSTEM_CHECKED | STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
  document_checker.CheckAccessible(GetRendererAccessible());
}

IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
                       TestNotificationChildrenChanged) {
  // The role attribute causes the node to be in the accessibility tree.
  GURL tree_url(
      "data:text/html,<body role=group></body>");
  browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
  GetRendererAccessible();
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check the browser's copy of the renderer accessibility tree.
  AccessibleChecker body_checker(L"", L"body", L"");
  AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
  document_checker.AppendExpectedChild(&body_checker);
  document_checker.CheckAccessible(GetRendererAccessible());

  // Change the children of the document body.
  ExecuteScript(L"document.body.innerHTML='<b>new text</b>'");
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check that the accessibility tree of the browser has been updated.
  AccessibleChecker text_checker(L"new text", ROLE_SYSTEM_TEXT, L"");
  body_checker.AppendExpectedChild(&text_checker);
  document_checker.CheckAccessible(GetRendererAccessible());
}

IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
                       TestNotificationChildrenChanged2) {
  // The role attribute causes the node to be in the accessibility tree.
  GURL tree_url(
      "data:text/html,<div role=group style='visibility: hidden'>text"
      "</div>");
  browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
  GetRendererAccessible();
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check the accessible tree of the browser.
  AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
  document_checker.CheckAccessible(GetRendererAccessible());

  // Change the children of the document body.
  ExecuteScript(L"document.body.children[0].style.visibility='visible'");
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check that the accessibility tree of the browser has been updated.
  AccessibleChecker static_text_checker(L"text", ROLE_SYSTEM_TEXT, L"");
  AccessibleChecker div_checker(L"", L"div", L"");
  document_checker.AppendExpectedChild(&div_checker);
  div_checker.AppendExpectedChild(&static_text_checker);
  document_checker.CheckAccessible(GetRendererAccessible());
}

IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
                       TestNotificationFocusChanged) {
  // The role attribute causes the node to be in the accessibility tree.
  GURL tree_url(
      "data:text/html,<div role=group tabindex='-1'></div>");
  browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
  GetRendererAccessible();
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check the browser's copy of the renderer accessibility tree.
  AccessibleChecker div_checker(L"", L"div", L"");
  div_checker.SetExpectedState(
      STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_READONLY);
  AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
  document_checker.AppendExpectedChild(&div_checker);
  document_checker.CheckAccessible(GetRendererAccessible());

  // Focus the div in the document
  ExecuteScript(L"document.body.children[0].focus()");
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check that the accessibility tree of the browser has been updated.
  div_checker.SetExpectedState(
      STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY | STATE_SYSTEM_FOCUSED);
  document_checker.CheckAccessible(GetRendererAccessible());

  // Focus the document accessible. This will un-focus the current node.
  base::win::ScopedComPtr<IAccessible> document_accessible(
      GetRendererAccessible());
  ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
  HRESULT hr = document_accessible->accSelect(
    SELFLAG_TAKEFOCUS, CreateI4Variant(CHILDID_SELF));
  ASSERT_EQ(S_OK, hr);
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check that the accessibility tree of the browser has been updated.
  div_checker.SetExpectedState(
      STATE_SYSTEM_FOCUSABLE | STATE_SYSTEM_READONLY);
  document_checker.CheckAccessible(GetRendererAccessible());
}

IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
                       TestNotificationValueChanged) {
  GURL tree_url("data:text/html,<body><input type='text' value='old value'/>"
      "</body>");
  browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
  GetRendererAccessible();
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check the browser's copy of the renderer accessibility tree.

  AccessibleChecker text_field_div_checker(L"", L"div", L"");
  AccessibleChecker text_field_checker(L"", ROLE_SYSTEM_TEXT, L"old value");
  text_field_checker.SetExpectedState(STATE_SYSTEM_FOCUSABLE);
  AccessibleChecker body_checker(L"", L"body", L"");
  AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT, L"");
  text_field_checker.AppendExpectedChild(&text_field_div_checker);
  body_checker.AppendExpectedChild(&text_field_checker);
  document_checker.AppendExpectedChild(&body_checker);
  document_checker.CheckAccessible(GetRendererAccessible());

  // Set the value of the text control
  ExecuteScript(L"document.body.children[0].value='new value'");
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Check that the accessibility tree of the browser has been updated.
  text_field_checker.SetExpectedValue(L"new value");
  document_checker.CheckAccessible(GetRendererAccessible());
}

// FAILS crbug.com/54220
// This test verifies that browser-side cache of the renderer accessibility
// tree is reachable from the browser's tree. Tools that analyze windows
// accessibility trees like AccExplorer32 should be able to drill into the
// cached renderer accessibility tree.
IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
                       DISABLED_ContainsRendererAccessibilityTree) {
  GURL tree_url("data:text/html,<body><input type='checkbox' /></body>");
  browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
  GetRendererAccessible();
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Get the accessibility object for the browser window.
  HWND browser_hwnd = browser()->window()->GetNativeHandle();
  base::win::ScopedComPtr<IAccessible> browser_accessible;
  HRESULT hr = AccessibleObjectFromWindow(
      browser_hwnd,
      OBJID_WINDOW,
      IID_IAccessible,
      reinterpret_cast<void**>(browser_accessible.Receive()));
  ASSERT_EQ(S_OK, hr);

  // Get the accessibility object for the renderer client document.
  base::win::ScopedComPtr<IAccessible> document_accessible(
      GetRendererAccessible());
  ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));
  base::win::ScopedComPtr<IAccessible2> document_accessible2;
  hr = QueryIAccessible2(document_accessible, document_accessible2.Receive());
  ASSERT_EQ(S_OK, hr);

  // TODO(ctguil): Pointer comparison of retrieved IAccessible pointers dosen't
  // seem to work for here. Perhaps make IAccessible2 available in views to make
  // unique id comparison available.
  bool found = false;
  base::win::ScopedComPtr<IAccessible> parent = document_accessible;
  while (parent.get()) {
    base::win::ScopedComPtr<IDispatch> parent_dispatch;
    hr = parent->get_accParent(parent_dispatch.Receive());
    ASSERT_TRUE(SUCCEEDED(hr));
    if (!parent_dispatch.get()) {
      ASSERT_EQ(hr, S_FALSE);
      break;
    }

    parent.Release();
    hr = parent_dispatch.QueryInterface(parent.Receive());
    ASSERT_EQ(S_OK, hr);

    if (parent.get() == browser_accessible.get()) {
      found = true;
      break;
    }
  }

  // If pointer comparison fails resort to the exhuasive search that can use
  // IAccessible2::get_unique_id for equality comparison.
  if (!found) {
    AccessibleContainsAccessible(
        browser_accessible, document_accessible2, &found);
  }

  ASSERT_EQ(found, true);
}

IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
                       SupportsISimpleDOM) {
  GURL tree_url("data:text/html,<body><input type='checkbox' /></body>");
  browser()->OpenURL(tree_url, GURL(), CURRENT_TAB, PageTransition::TYPED);
  GetRendererAccessible();
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  // Get the IAccessible object for the document.
  base::win::ScopedComPtr<IAccessible> document_accessible(
      GetRendererAccessible());
  ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));

  // Get the ISimpleDOM object for the document.
  base::win::ScopedComPtr<IServiceProvider> service_provider;
  HRESULT hr = static_cast<IAccessible*>(document_accessible)->QueryInterface(
      service_provider.Receive());
  ASSERT_EQ(S_OK, hr);
  const GUID refguid = {0x0c539790, 0x12e4, 0x11cf,
                        0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8};
  base::win::ScopedComPtr<ISimpleDOMNode> document_isimpledomnode;
  hr = static_cast<IServiceProvider *>(service_provider)->QueryService(
      refguid, IID_ISimpleDOMNode,
      reinterpret_cast<void**>(document_isimpledomnode.Receive()));
  ASSERT_EQ(S_OK, hr);

  BSTR node_name;
  short name_space_id;  // NOLINT
  BSTR node_value;
  unsigned int num_children;
  unsigned int unique_id;
  unsigned short node_type;  // NOLINT
  hr = document_isimpledomnode->get_nodeInfo(
      &node_name, &name_space_id, &node_value, &num_children, &unique_id,
      &node_type);
  ASSERT_EQ(S_OK, hr);
  EXPECT_EQ(NODETYPE_DOCUMENT, node_type);
  EXPECT_EQ(1, num_children);

  base::win::ScopedComPtr<ISimpleDOMNode> body_isimpledomnode;
  hr = document_isimpledomnode->get_firstChild(
      body_isimpledomnode.Receive());
  ASSERT_EQ(S_OK, hr);
  hr = body_isimpledomnode->get_nodeInfo(
      &node_name, &name_space_id, &node_value, &num_children, &unique_id,
      &node_type);
  ASSERT_EQ(S_OK, hr);
  EXPECT_STREQ(L"body", wstring(node_name, SysStringLen(node_name)).c_str());
  EXPECT_EQ(NODETYPE_ELEMENT, node_type);
  EXPECT_EQ(1, num_children);

  base::win::ScopedComPtr<ISimpleDOMNode> checkbox_isimpledomnode;
  hr = body_isimpledomnode->get_firstChild(
      checkbox_isimpledomnode.Receive());
  ASSERT_EQ(S_OK, hr);
  hr = checkbox_isimpledomnode->get_nodeInfo(
      &node_name, &name_space_id, &node_value, &num_children, &unique_id,
      &node_type);
  ASSERT_EQ(S_OK, hr);
  EXPECT_STREQ(L"input", wstring(node_name, SysStringLen(node_name)).c_str());
  EXPECT_EQ(NODETYPE_ELEMENT, node_type);
  EXPECT_EQ(0, num_children);
}
}  // namespace.