1// Copyright 2013 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 <oleacc.h>
6
7#include "base/win/scoped_bstr.h"
8#include "base/win/scoped_comptr.h"
9#include "base/win/scoped_variant.h"
10#include "third_party/iaccessible2/ia2_api_all.h"
11#include "ui/views/accessibility/native_view_accessibility.h"
12#include "ui/views/controls/textfield/textfield.h"
13#include "ui/views/test/views_test_base.h"
14
15using base::win::ScopedBstr;
16using base::win::ScopedComPtr;
17using base::win::ScopedVariant;
18
19namespace views {
20namespace test {
21
22class NativeViewAcccessibilityWinTest : public ViewsTestBase {
23 public:
24  NativeViewAcccessibilityWinTest() {}
25  virtual ~NativeViewAcccessibilityWinTest() {}
26
27 protected:
28  void GetIAccessible2InterfaceForView(View* view, IAccessible2_2** result) {
29    ScopedComPtr<IAccessible> view_accessible(
30        view->GetNativeViewAccessible());
31    ScopedComPtr<IServiceProvider> service_provider;
32    ASSERT_EQ(S_OK, view_accessible.QueryInterface(service_provider.Receive()));
33    ASSERT_EQ(S_OK,
34        service_provider->QueryService(IID_IAccessible2_2, result));
35  }
36};
37
38TEST_F(NativeViewAcccessibilityWinTest, TextfieldAccessibility) {
39  Widget widget;
40  Widget::InitParams init_params =
41      CreateParams(Widget::InitParams::TYPE_POPUP);
42  init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
43  widget.Init(init_params);
44
45  View* content = new View;
46  widget.SetContentsView(content);
47
48  Textfield* textfield = new Textfield;
49  textfield->SetAccessibleName(L"Name");
50  textfield->SetText(L"Value");
51  content->AddChildView(textfield);
52
53  ScopedComPtr<IAccessible> content_accessible(
54      content->GetNativeViewAccessible());
55  LONG child_count = 0;
56  ASSERT_EQ(S_OK, content_accessible->get_accChildCount(&child_count));
57  ASSERT_EQ(1L, child_count);
58
59  ScopedComPtr<IDispatch> textfield_dispatch;
60  ScopedComPtr<IAccessible> textfield_accessible;
61  ScopedVariant child_index(1);
62  ASSERT_EQ(S_OK, content_accessible->get_accChild(
63      child_index, textfield_dispatch.Receive()));
64  ASSERT_EQ(S_OK, textfield_dispatch.QueryInterface(
65      textfield_accessible.Receive()));
66
67  ScopedBstr name;
68  ScopedVariant childid_self(CHILDID_SELF);
69  ASSERT_EQ(S_OK, textfield_accessible->get_accName(
70      childid_self, name.Receive()));
71  ASSERT_STREQ(L"Name", name);
72
73  ScopedBstr value;
74  ASSERT_EQ(S_OK, textfield_accessible->get_accValue(
75      childid_self, value.Receive()));
76  ASSERT_STREQ(L"Value", value);
77
78  ScopedBstr new_value(L"New value");
79  ASSERT_EQ(S_OK, textfield_accessible->put_accValue(childid_self, new_value));
80
81  ASSERT_STREQ(L"New value", textfield->text().c_str());
82}
83
84TEST_F(NativeViewAcccessibilityWinTest, UnattachedWebView) {
85  // This is a regression test. Calling get_accChild on the native accessible
86  // object for a WebView with no attached WebContents was causing an
87  // infinite loop and crash. This test simulates that with an ordinary
88  // View that registers itself as a web view with NativeViewAcccessibility.
89
90  Widget widget;
91  Widget::InitParams init_params =
92      CreateParams(Widget::InitParams::TYPE_POPUP);
93  init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
94  widget.Init(init_params);
95
96  View* content = new View;
97  widget.SetContentsView(content);
98
99  View* web_view = new View;
100  content->AddChildView(web_view);
101  NativeViewAccessibility::RegisterWebView(web_view);
102
103  ScopedComPtr<IAccessible> web_view_accessible(
104      web_view->GetNativeViewAccessible());
105  ScopedComPtr<IDispatch> result_dispatch;
106  ScopedVariant child_index(-999);
107  ASSERT_EQ(E_FAIL, web_view_accessible->get_accChild(
108      child_index, result_dispatch.Receive()));
109
110  NativeViewAccessibility::UnregisterWebView(web_view);
111}
112
113TEST_F(NativeViewAcccessibilityWinTest, AuraOwnedWidgets) {
114  Widget widget;
115  Widget::InitParams init_params =
116      CreateParams(Widget::InitParams::TYPE_WINDOW);
117  init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
118  widget.Init(init_params);
119
120  ScopedComPtr<IAccessible> root_view_accessible(
121      widget.GetRootView()->GetNativeViewAccessible());
122
123  LONG child_count = 0;
124  ASSERT_EQ(S_OK, root_view_accessible->get_accChildCount(&child_count));
125  ASSERT_EQ(1L, child_count);
126
127  Widget owned_widget;
128  Widget::InitParams owned_init_params =
129      CreateParams(Widget::InitParams::TYPE_POPUP);
130  owned_init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
131  owned_init_params.parent = widget.GetNativeView();
132  owned_widget.Init(owned_init_params);
133  owned_widget.Show();
134
135  ASSERT_EQ(S_OK, root_view_accessible->get_accChildCount(&child_count));
136  ASSERT_EQ(2L, child_count);
137}
138
139TEST_F(NativeViewAcccessibilityWinTest, RetrieveAllAlerts) {
140  Widget widget;
141  Widget::InitParams init_params =
142      CreateParams(Widget::InitParams::TYPE_POPUP);
143  init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
144  widget.Init(init_params);
145
146  View* content = new View;
147  widget.SetContentsView(content);
148
149  View* infobar = new View;
150  content->AddChildView(infobar);
151
152  View* infobar2 = new View;
153  content->AddChildView(infobar2);
154
155  View* root_view = content->parent();
156  ASSERT_EQ(NULL, root_view->parent());
157
158  ScopedComPtr<IAccessible2_2> root_view_accessible;
159  GetIAccessible2InterfaceForView(root_view, root_view_accessible.Receive());
160
161  ScopedComPtr<IAccessible2_2> infobar_accessible;
162  GetIAccessible2InterfaceForView(infobar, infobar_accessible.Receive());
163
164  ScopedComPtr<IAccessible2_2> infobar2_accessible;
165  GetIAccessible2InterfaceForView(infobar2, infobar2_accessible.Receive());
166
167  // Initially, there are no alerts
168  ScopedBstr alerts_bstr(L"alerts");
169  IUnknown** targets;
170  long n_targets;
171  ASSERT_EQ(S_FALSE, root_view_accessible->get_relationTargetsOfType(
172      alerts_bstr, 0, &targets, &n_targets));
173  ASSERT_EQ(0, n_targets);
174
175  // Fire alert events on the infobars.
176  infobar->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
177  infobar2->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
178
179  // Now calling get_relationTargetsOfType should retrieve the alerts.
180  ASSERT_EQ(S_OK, root_view_accessible->get_relationTargetsOfType(
181      alerts_bstr, 0, &targets, &n_targets));
182  ASSERT_EQ(2, n_targets);
183  ASSERT_TRUE(infobar_accessible.IsSameObject(targets[0]));
184  ASSERT_TRUE(infobar2_accessible.IsSameObject(targets[1]));
185  CoTaskMemFree(targets);
186
187  // If we set max_targets to 1, we should only get the first one.
188  ASSERT_EQ(S_OK, root_view_accessible->get_relationTargetsOfType(
189      alerts_bstr, 1, &targets, &n_targets));
190  ASSERT_EQ(1, n_targets);
191  ASSERT_TRUE(infobar_accessible.IsSameObject(targets[0]));
192  CoTaskMemFree(targets);
193
194  // If we delete the first view, we should only get the second one now.
195  delete infobar;
196  ASSERT_EQ(S_OK, root_view_accessible->get_relationTargetsOfType(
197      alerts_bstr, 0, &targets, &n_targets));
198  ASSERT_EQ(1, n_targets);
199  ASSERT_TRUE(infobar2_accessible.IsSameObject(targets[0]));
200  CoTaskMemFree(targets);
201}
202
203}  // namespace test
204}  // namespace views
205