• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/memory/scoped_ptr.h"
6 #include "base/win/scoped_comptr.h"
7 #include "chrome/browser/accessibility/browser_accessibility_manager.h"
8 #include "chrome/browser/accessibility/browser_accessibility_win.h"
9 #include "content/common/view_messages.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 
12 using webkit_glue::WebAccessibility;
13 
14 namespace {
15 
16 // Subclass of BrowserAccessibilityWin that counts the number of instances.
17 class CountedBrowserAccessibility : public BrowserAccessibilityWin {
18  public:
CountedBrowserAccessibility()19   CountedBrowserAccessibility() { global_obj_count_++; }
~CountedBrowserAccessibility()20   virtual ~CountedBrowserAccessibility() { global_obj_count_--; }
21   static int global_obj_count_;
22 };
23 
24 int CountedBrowserAccessibility::global_obj_count_ = 0;
25 
26 // Factory that creates a CountedBrowserAccessibility.
27 class CountedBrowserAccessibilityFactory
28     : public BrowserAccessibilityFactory {
29  public:
~CountedBrowserAccessibilityFactory()30   virtual ~CountedBrowserAccessibilityFactory() {}
Create()31   virtual BrowserAccessibility* Create() {
32     CComObject<CountedBrowserAccessibility>* instance;
33     HRESULT hr = CComObject<CountedBrowserAccessibility>::CreateInstance(
34         &instance);
35     DCHECK(SUCCEEDED(hr));
36     instance->AddRef();
37     return instance;
38   }
39 };
40 
41 }  // anonymous namespace
42 
CreateI4Variant(LONG value)43 VARIANT CreateI4Variant(LONG value) {
44   VARIANT variant = {0};
45 
46   V_VT(&variant) = VT_I4;
47   V_I4(&variant) = value;
48 
49   return variant;
50 }
51 
52 class BrowserAccessibilityTest : public testing::Test {
53  protected:
SetUp()54   virtual void SetUp() {
55     // ATL needs a pointer to a COM module.
56     static CComModule module;
57     _pAtlModule = &module;
58 
59     // Make sure COM is initialized for this thread; it's safe to call twice.
60     ::CoInitialize(NULL);
61   }
62 
TearDown()63   virtual void TearDown() {
64     ::CoUninitialize();
65   }
66 };
67 
68 // Test that BrowserAccessibilityManager correctly releases the tree of
69 // BrowserAccessibility instances upon delete.
TEST_F(BrowserAccessibilityTest,TestNoLeaks)70 TEST_F(BrowserAccessibilityTest, TestNoLeaks) {
71   // Create WebAccessibility objects for a simple document tree,
72   // representing the accessibility information used to initialize
73   // BrowserAccessibilityManager.
74   WebAccessibility button;
75   button.id = 2;
76   button.name = L"Button";
77   button.role = WebAccessibility::ROLE_BUTTON;
78   button.state = 0;
79 
80   WebAccessibility checkbox;
81   checkbox.id = 3;
82   checkbox.name = L"Checkbox";
83   checkbox.role = WebAccessibility::ROLE_CHECKBOX;
84   checkbox.state = 0;
85 
86   WebAccessibility root;
87   root.id = 1;
88   root.name = L"Document";
89   root.role = WebAccessibility::ROLE_DOCUMENT;
90   root.state = 0;
91   root.children.push_back(button);
92   root.children.push_back(checkbox);
93 
94   // Construct a BrowserAccessibilityManager with this WebAccessibility tree
95   // and a factory for an instance-counting BrowserAccessibility, and ensure
96   // that exactly 3 instances were created. Note that the manager takes
97   // ownership of the factory.
98   CountedBrowserAccessibility::global_obj_count_ = 0;
99   BrowserAccessibilityManager* manager =
100       BrowserAccessibilityManager::Create(
101           GetDesktopWindow(),
102           root,
103           NULL,
104           new CountedBrowserAccessibilityFactory());
105   ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
106 
107   // Delete the manager and test that all 3 instances are deleted.
108   delete manager;
109   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
110 
111   // Construct a manager again, and this time use the IAccessible interface
112   // to get new references to two of the three nodes in the tree.
113   manager =
114       BrowserAccessibilityManager::Create(
115           GetDesktopWindow(),
116           root,
117           NULL,
118           new CountedBrowserAccessibilityFactory());
119   ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
120   IAccessible* root_accessible =
121       manager->GetRoot()->toBrowserAccessibilityWin();
122   IDispatch* root_iaccessible = NULL;
123   IDispatch* child1_iaccessible = NULL;
124   VARIANT var_child;
125   var_child.vt = VT_I4;
126   var_child.lVal = CHILDID_SELF;
127   HRESULT hr = root_accessible->get_accChild(var_child, &root_iaccessible);
128   ASSERT_EQ(S_OK, hr);
129   var_child.lVal = 1;
130   hr = root_accessible->get_accChild(var_child, &child1_iaccessible);
131   ASSERT_EQ(S_OK, hr);
132 
133   // Now delete the manager, and only one of the three nodes in the tree
134   // should be released.
135   delete manager;
136   ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_);
137 
138   // Release each of our references and make sure that each one results in
139   // the instance being deleted as its reference count hits zero.
140   root_iaccessible->Release();
141   ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_);
142   child1_iaccessible->Release();
143   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
144 }
145 
TEST_F(BrowserAccessibilityTest,TestChildrenChange)146 TEST_F(BrowserAccessibilityTest, TestChildrenChange) {
147   // Create WebAccessibility objects for a simple document tree,
148   // representing the accessibility information used to initialize
149   // BrowserAccessibilityManager.
150   WebAccessibility text;
151   text.id = 2;
152   text.role = WebAccessibility::ROLE_STATIC_TEXT;
153   text.name = L"old text";
154   text.state = 0;
155 
156   WebAccessibility root;
157   root.id = 1;
158   root.name = L"Document";
159   root.role = WebAccessibility::ROLE_DOCUMENT;
160   root.state = 0;
161   root.children.push_back(text);
162 
163   // Construct a BrowserAccessibilityManager with this WebAccessibility tree
164   // and a factory for an instance-counting BrowserAccessibility.
165   CountedBrowserAccessibility::global_obj_count_ = 0;
166   BrowserAccessibilityManager* manager =
167       BrowserAccessibilityManager::Create(
168           GetDesktopWindow(),
169           root,
170           NULL,
171           new CountedBrowserAccessibilityFactory());
172 
173   // Query for the text IAccessible and verify that it returns "old text" as its
174   // value.
175   base::win::ScopedComPtr<IDispatch> text_dispatch;
176   HRESULT hr = manager->GetRoot()->toBrowserAccessibilityWin()->get_accChild(
177       CreateI4Variant(1), text_dispatch.Receive());
178   ASSERT_EQ(S_OK, hr);
179 
180   base::win::ScopedComPtr<IAccessible> text_accessible;
181   hr = text_dispatch.QueryInterface(text_accessible.Receive());
182   ASSERT_EQ(S_OK, hr);
183 
184   CComBSTR name;
185   hr = text_accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name);
186   ASSERT_EQ(S_OK, hr);
187   EXPECT_STREQ(L"old text", name.m_str);
188 
189   text_dispatch.Release();
190   text_accessible.Release();
191 
192   // Notify the BrowserAccessibilityManager that the text child has changed.
193   text.name = L"new text";
194   ViewHostMsg_AccessibilityNotification_Params param;
195   param.notification_type =
196       ViewHostMsg_AccessibilityNotification_Type::
197         NOTIFICATION_TYPE_CHILDREN_CHANGED;
198   param.acc_obj = text;
199   std::vector<ViewHostMsg_AccessibilityNotification_Params> notifications;
200   notifications.push_back(param);
201   manager->OnAccessibilityNotifications(notifications);
202 
203   // Query for the text IAccessible and verify that it now returns "new text"
204   // as its value.
205   hr = manager->GetRoot()->toBrowserAccessibilityWin()->get_accChild(
206       CreateI4Variant(1),
207       text_dispatch.Receive());
208   ASSERT_EQ(S_OK, hr);
209 
210   hr = text_dispatch.QueryInterface(text_accessible.Receive());
211   ASSERT_EQ(S_OK, hr);
212 
213   hr = text_accessible->get_accName(CreateI4Variant(CHILDID_SELF), &name);
214   ASSERT_EQ(S_OK, hr);
215   EXPECT_STREQ(L"new text", name.m_str);
216 
217   text_dispatch.Release();
218   text_accessible.Release();
219 
220   // Delete the manager and test that all BrowserAccessibility instances are
221   // deleted.
222   delete manager;
223   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
224 }
225 
TEST_F(BrowserAccessibilityTest,TestChildrenChangeNoLeaks)226 TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) {
227   // Create WebAccessibility objects for a simple document tree,
228   // representing the accessibility information used to initialize
229   // BrowserAccessibilityManager.
230   WebAccessibility text;
231   text.id = 3;
232   text.role = WebAccessibility::ROLE_STATIC_TEXT;
233   text.state = 0;
234 
235   WebAccessibility div;
236   div.id = 2;
237   div.role = WebAccessibility::ROLE_GROUP;
238   div.state = 0;
239 
240   div.children.push_back(text);
241   text.id = 4;
242   div.children.push_back(text);
243 
244   WebAccessibility root;
245   root.id = 1;
246   root.role = WebAccessibility::ROLE_DOCUMENT;
247   root.state = 0;
248   root.children.push_back(div);
249 
250   // Construct a BrowserAccessibilityManager with this WebAccessibility tree
251   // and a factory for an instance-counting BrowserAccessibility and ensure
252   // that exactly 4 instances were created. Note that the manager takes
253   // ownership of the factory.
254   CountedBrowserAccessibility::global_obj_count_ = 0;
255   BrowserAccessibilityManager* manager =
256       BrowserAccessibilityManager::Create(
257           GetDesktopWindow(),
258           root,
259           NULL,
260           new CountedBrowserAccessibilityFactory());
261   ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
262 
263   // Notify the BrowserAccessibilityManager that the div node and its children
264   // were removed and ensure that only one BrowserAccessibility instance exists.
265   root.children.clear();
266   ViewHostMsg_AccessibilityNotification_Params param;
267   param.notification_type =
268       ViewHostMsg_AccessibilityNotification_Type::
269         NOTIFICATION_TYPE_CHILDREN_CHANGED;
270   param.acc_obj = root;
271   std::vector<ViewHostMsg_AccessibilityNotification_Params> notifications;
272   notifications.push_back(param);
273   manager->OnAccessibilityNotifications(notifications);
274   ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_);
275 
276   // Delete the manager and test that all BrowserAccessibility instances are
277   // deleted.
278   delete manager;
279   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
280 }
281 
TEST_F(BrowserAccessibilityTest,TestTextBoundaries)282 TEST_F(BrowserAccessibilityTest, TestTextBoundaries) {
283   WebAccessibility text1;
284   text1.id = 11;
285   text1.role = WebAccessibility::ROLE_TEXT_FIELD;
286   text1.state = 0;
287   text1.value = L"One two three.\nFour five six.";
288 
289   WebAccessibility root;
290   root.id = 1;
291   root.role = WebAccessibility::ROLE_DOCUMENT;
292   root.state = 0;
293   root.children.push_back(text1);
294 
295   CountedBrowserAccessibility::global_obj_count_ = 0;
296   BrowserAccessibilityManager* manager = BrowserAccessibilityManager::Create(
297       GetDesktopWindow(), root, NULL,
298       new CountedBrowserAccessibilityFactory());
299   ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_);
300 
301   BrowserAccessibilityWin* root_obj =
302       manager->GetRoot()->toBrowserAccessibilityWin();
303   BrowserAccessibilityWin* text1_obj =
304       root_obj->GetChild(0)->toBrowserAccessibilityWin();
305 
306   BSTR text;
307   long start;
308   long end;
309 
310   long text1_len;
311   ASSERT_EQ(S_OK, text1_obj->get_nCharacters(&text1_len));
312 
313   ASSERT_EQ(S_OK, text1_obj->get_text(0, text1_len, &text));
314   ASSERT_EQ(text, text1.value);
315   SysFreeString(text);
316 
317   ASSERT_EQ(S_OK, text1_obj->get_text(0, 4, &text));
318   ASSERT_EQ(text, string16(L"One "));
319   SysFreeString(text);
320 
321   ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
322       1, IA2_TEXT_BOUNDARY_CHAR, &start, &end, &text));
323   ASSERT_EQ(start, 1);
324   ASSERT_EQ(end, 2);
325   ASSERT_EQ(text, string16(L"n"));
326   SysFreeString(text);
327 
328   ASSERT_EQ(S_FALSE, text1_obj->get_textAtOffset(
329       text1_len, IA2_TEXT_BOUNDARY_CHAR, &start, &end, &text));
330   ASSERT_EQ(start, text1_len);
331   ASSERT_EQ(end, text1_len);
332 
333   ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
334       1, IA2_TEXT_BOUNDARY_WORD, &start, &end, &text));
335   ASSERT_EQ(start, 0);
336   ASSERT_EQ(end, 3);
337   ASSERT_EQ(text, string16(L"One"));
338   SysFreeString(text);
339 
340   ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
341       6, IA2_TEXT_BOUNDARY_WORD, &start, &end, &text));
342   ASSERT_EQ(start, 4);
343   ASSERT_EQ(end, 7);
344   ASSERT_EQ(text, string16(L"two"));
345   SysFreeString(text);
346 
347   ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
348       text1_len, IA2_TEXT_BOUNDARY_WORD, &start, &end, &text));
349   ASSERT_EQ(start, 25);
350   ASSERT_EQ(end, 29);
351   ASSERT_EQ(text, string16(L"six."));
352   SysFreeString(text);
353 
354   ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
355       1, IA2_TEXT_BOUNDARY_LINE, &start, &end, &text));
356   ASSERT_EQ(start, 0);
357   ASSERT_EQ(end, 13);
358   ASSERT_EQ(text, string16(L"One two three"));
359   SysFreeString(text);
360 
361   // Delete the manager and test that all BrowserAccessibility instances are
362   // deleted.
363   delete manager;
364   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
365 }
366