Logo Search packages:      
Sourcecode: chromium-browser version File versions  Download package

accessibility_win_browsertest.cc

// Copyright (c) 2010 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/file_path.h"
#include "base/scoped_comptr_win.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/renderer_host/render_widget_host_view_win.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_type.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"

namespace {

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

  // InProcessBrowserTest
  void SetUpInProcessBrowserTestFixture();
  void TearDownInProcessBrowserTestFixture();

 protected:
  IAccessible* GetRenderWidgetHostViewClientAccessible();

 private:
  BOOL screenreader_running_;
};

void AccessibilityWinBrowserTest::SetUpInProcessBrowserTestFixture() {
  // This test assumes the windows system-wide SPI_SETSCREENREADER flag is
  // cleared.
  if (SystemParametersInfo(SPI_GETSCREENREADER, 0, &screenreader_running_, 0) &&
      screenreader_running_) {
    // Clear the SPI_SETSCREENREADER flag and notify active applications about
    // the setting change.
    ::SystemParametersInfo(SPI_SETSCREENREADER, FALSE, NULL, 0);
    ::SendNotifyMessage(
        HWND_BROADCAST, WM_SETTINGCHANGE, SPI_GETSCREENREADER, 0);
  }
}

void AccessibilityWinBrowserTest::TearDownInProcessBrowserTestFixture() {
  if (screenreader_running_) {
    // Restore the SPI_SETSCREENREADER flag and notify active applications about
    // the setting change.
    ::SystemParametersInfo(SPI_SETSCREENREADER, TRUE, NULL, 0);
    ::SendNotifyMessage(
        HWND_BROADCAST, WM_SETTINGCHANGE, SPI_GETSCREENREADER, 0);
  }
}

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

  // 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);

  typedef std::vector<AccessibleChecker*> AccessibleCheckerVector;

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

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

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

  // 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_EQ(hr, S_OK);
      return CComQIPtr<IAccessible>(dispatch).Detach();
      break;
    }
  }

  return NULL;
}

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

  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;
}

AccessibleChecker::AccessibleChecker(
    std::wstring expected_name, int32 expected_role) :
    name_(expected_name),
    role_(expected_role) {
}

AccessibleChecker::AccessibleChecker(
    std::wstring expected_name, std::wstring expected_role) :
    name_(expected_name),
    role_(expected_role.c_str()) {
}

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

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

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(hr, S_OK);
    EXPECT_EQ(CompareString(LOCALE_NEUTRAL, 0, name, SysStringLen(name),
                  name_.c_str(), name_.length()),
              CSTR_EQUAL);
  }
}

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

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

  std::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(hr, S_OK);
  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) {
    ScopedComPtr<IAccessible> child_accessible;
    child_accessible.Attach(GetAccessibleFromResultVariant(parent, child));
    (*child_checker)->CheckAccessible(child_accessible);
  }
}

IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest,
                       TestRendererAccessibilityTree) {
  // By requesting an accessible chrome will believe a screen reader has been
  // detected.
  ScopedComPtr<IAccessible> document_accessible(
      GetRenderWidgetHostViewClientAccessible());

  // The initial accessible returned should have state STATE_SYSTEM_BUSY while
  // the accessibility tree is being requested from the renderer.
  VARIANT var_state;
  HRESULT hr = document_accessible->
      get_accState(CreateI4Variant(CHILDID_SELF), &var_state);
  EXPECT_EQ(hr, S_OK);
  EXPECT_EQ(V_VT(&var_state), VT_I4);
  EXPECT_EQ(V_I4(&var_state), STATE_SYSTEM_BUSY);

  // Wait for the initial accessibility tree to load.
  ui_test_utils::WaitForNotification(
      NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED);

  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);

  document_accessible = GetRenderWidgetHostViewClientAccessible();
  ASSERT_NE(document_accessible.get(), reinterpret_cast<IAccessible*>(NULL));

  AccessibleChecker button_checker(L"push", ROLE_SYSTEM_PUSHBUTTON);
  AccessibleChecker checkbox_checker(L"", ROLE_SYSTEM_CHECKBUTTON);

  AccessibleChecker grouping_checker(L"", L"div");
  grouping_checker.AppendExpectedChild(&button_checker);
  grouping_checker.AppendExpectedChild(&checkbox_checker);

  AccessibleChecker document_checker(L"", ROLE_SYSTEM_DOCUMENT);
  document_checker.AppendExpectedChild(&grouping_checker);

  // Check the accessible tree of the renderer.
  document_checker.CheckAccessible(document_accessible);

  // Check that document accessible has a parent accessible.
  ScopedComPtr<IDispatch> parent_dispatch;
  hr = document_accessible->get_accParent(parent_dispatch.Receive());
  EXPECT_EQ(hr, S_OK);
  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.
  // Todo(ctguil): Currently this is giving a false positive because E_FAIL is
  // returned when BrowserAccessibilityManager::RequestAccessibilityInfo fails
  // since the previous render view host connection is lost. Verify that
  // instances are actually marked as invalid once the browse side cache is
  // checked in.
  CComBSTR name;
  hr = document_accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name);
  ASSERT_EQ(E_FAIL, hr);
}
}  // namespace.

Generated by  Doxygen 1.6.0   Back to index