• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2010 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/string16.h"
6 #include "base/utf_string_conversions.h"
7 #include "chrome/browser/accessibility/browser_accessibility.h"
8 #include "chrome/browser/accessibility/browser_accessibility_manager.h"
9 #include "content/common/view_messages.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "webkit/glue/webaccessibility.h"
12 
13 using webkit_glue::WebAccessibility;
14 
15 namespace {
16 
17 // Subclass of BrowserAccessibility that counts the number of instances.
18 class CountedBrowserAccessibility : public BrowserAccessibility {
19  public:
CountedBrowserAccessibility()20   CountedBrowserAccessibility() {
21     global_obj_count_++;
22     native_ref_count_ = 1;
23   }
~CountedBrowserAccessibility()24   virtual ~CountedBrowserAccessibility() {
25     global_obj_count_--;
26   }
27 
NativeAddReference()28   virtual void NativeAddReference() OVERRIDE {
29     native_ref_count_++;
30   }
31 
NativeReleaseReference()32   virtual void NativeReleaseReference() OVERRIDE {
33     native_ref_count_--;
34     if (native_ref_count_ == 0)
35       delete this;
36   }
37 
38   int native_ref_count_;
39   static int global_obj_count_;
40 };
41 
42 int CountedBrowserAccessibility::global_obj_count_ = 0;
43 
44 // Factory that creates a CountedBrowserAccessibility.
45 class CountedBrowserAccessibilityFactory
46     : public BrowserAccessibilityFactory {
47  public:
~CountedBrowserAccessibilityFactory()48   virtual ~CountedBrowserAccessibilityFactory() {}
Create()49   virtual BrowserAccessibility* Create() {
50     return new CountedBrowserAccessibility();
51   }
52 };
53 
54 }  // anonymous namespace
55 
TEST(BrowserAccessibilityManagerTest,TestNoLeaks)56 TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
57   // Create WebAccessibility objects for a simple document tree,
58   // representing the accessibility information used to initialize
59   // BrowserAccessibilityManager.
60   WebAccessibility button;
61   button.id = 2;
62   button.name = UTF8ToUTF16("Button");
63   button.role = WebAccessibility::ROLE_BUTTON;
64   button.state = 0;
65 
66   WebAccessibility checkbox;
67   checkbox.id = 3;
68   checkbox.name = UTF8ToUTF16("Checkbox");
69   checkbox.role = WebAccessibility::ROLE_CHECKBOX;
70   checkbox.state = 0;
71 
72   WebAccessibility root;
73   root.id = 1;
74   root.name = UTF8ToUTF16("Document");
75   root.role = WebAccessibility::ROLE_DOCUMENT;
76   root.state = 0;
77   root.children.push_back(button);
78   root.children.push_back(checkbox);
79 
80   // Construct a BrowserAccessibilityManager with this WebAccessibility tree
81   // and a factory for an instance-counting BrowserAccessibility, and ensure
82   // that exactly 3 instances were created. Note that the manager takes
83   // ownership of the factory.
84   CountedBrowserAccessibility::global_obj_count_ = 0;
85   BrowserAccessibilityManager* manager =
86       BrowserAccessibilityManager::Create(
87           NULL,
88           root,
89           NULL,
90           new CountedBrowserAccessibilityFactory());
91 
92   ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
93 
94   // Delete the manager and test that all 3 instances are deleted.
95   delete manager;
96   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
97 
98   // Construct a manager again, and this time save references to two of
99   // the three nodes in the tree.
100   manager =
101       BrowserAccessibilityManager::Create(
102           NULL,
103           root,
104           NULL,
105           new CountedBrowserAccessibilityFactory());
106   ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
107 
108   CountedBrowserAccessibility* root_accessible =
109       static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
110   root_accessible->NativeAddReference();
111   CountedBrowserAccessibility* child1_accessible =
112       static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(1));
113   child1_accessible->NativeAddReference();
114 
115   // Now delete the manager, and only one of the three nodes in the tree
116   // should be released.
117   delete manager;
118   ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_);
119 
120   // Release each of our references and make sure that each one results in
121   // the instance being deleted as its reference count hits zero.
122   root_accessible->NativeReleaseReference();
123   ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_);
124   child1_accessible->NativeReleaseReference();
125   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
126 }
127 
TEST(BrowserAccessibilityManagerTest,TestReuseBrowserAccessibilityObjects)128 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
129   // Make sure that changes to a subtree reuse as many objects as possible.
130 
131   // Tree 1:
132   //
133   // root
134   //   child1
135   //   child2
136   //   child3
137 
138   WebAccessibility tree1_child1;
139   tree1_child1.id = 2;
140   tree1_child1.name = UTF8ToUTF16("Child1");
141   tree1_child1.role = WebAccessibility::ROLE_BUTTON;
142   tree1_child1.state = 0;
143 
144   WebAccessibility tree1_child2;
145   tree1_child2.id = 3;
146   tree1_child2.name = UTF8ToUTF16("Child2");
147   tree1_child2.role = WebAccessibility::ROLE_BUTTON;
148   tree1_child2.state = 0;
149 
150   WebAccessibility tree1_child3;
151   tree1_child3.id = 4;
152   tree1_child3.name = UTF8ToUTF16("Child3");
153   tree1_child3.role = WebAccessibility::ROLE_BUTTON;
154   tree1_child3.state = 0;
155 
156   WebAccessibility tree1_root;
157   tree1_root.id = 1;
158   tree1_root.name = UTF8ToUTF16("Document");
159   tree1_root.role = WebAccessibility::ROLE_DOCUMENT;
160   tree1_root.state = 0;
161   tree1_root.children.push_back(tree1_child1);
162   tree1_root.children.push_back(tree1_child2);
163   tree1_root.children.push_back(tree1_child3);
164 
165   // Tree 2:
166   //
167   // root
168   //   child0  <-- inserted
169   //   child1
170   //   child2
171   //           <-- child3 deleted
172 
173   WebAccessibility tree2_child0;
174   tree2_child0.id = 5;
175   tree2_child0.name = UTF8ToUTF16("Child0");
176   tree2_child0.role = WebAccessibility::ROLE_BUTTON;
177   tree2_child0.state = 0;
178 
179   WebAccessibility tree2_child1;
180   tree2_child1.id = 2;
181   tree2_child1.name = UTF8ToUTF16("Child1");
182   tree2_child1.role = WebAccessibility::ROLE_BUTTON;
183   tree2_child1.state = 0;
184 
185   WebAccessibility tree2_child2;
186   tree2_child2.id = 3;
187   tree2_child2.name = UTF8ToUTF16("Child2");
188   tree2_child2.role = WebAccessibility::ROLE_BUTTON;
189   tree2_child2.state = 0;
190 
191   WebAccessibility tree2_root;
192   tree2_root.id = 1;
193   tree2_root.name = UTF8ToUTF16("DocumentChanged");
194   tree2_root.role = WebAccessibility::ROLE_DOCUMENT;
195   tree2_root.state = 0;
196   tree2_root.children.push_back(tree2_child0);
197   tree2_root.children.push_back(tree2_child1);
198   tree2_root.children.push_back(tree2_child2);
199 
200   // Construct a BrowserAccessibilityManager with tree1.
201   CountedBrowserAccessibility::global_obj_count_ = 0;
202   BrowserAccessibilityManager* manager =
203       BrowserAccessibilityManager::Create(
204           NULL,
205           tree1_root,
206           NULL,
207           new CountedBrowserAccessibilityFactory());
208   ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
209 
210   // Save references to all of the objects.
211   CountedBrowserAccessibility* root_accessible =
212       static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
213   root_accessible->NativeAddReference();
214   CountedBrowserAccessibility* child1_accessible =
215       static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(0));
216   child1_accessible->NativeAddReference();
217   CountedBrowserAccessibility* child2_accessible =
218       static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(1));
219   child2_accessible->NativeAddReference();
220   CountedBrowserAccessibility* child3_accessible =
221       static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(2));
222   child3_accessible->NativeAddReference();
223 
224   // Check the index in parent.
225   EXPECT_EQ(0, child1_accessible->index_in_parent());
226   EXPECT_EQ(1, child2_accessible->index_in_parent());
227   EXPECT_EQ(2, child3_accessible->index_in_parent());
228 
229   // Process a notification containing the changed subtree.
230   std::vector<ViewHostMsg_AccessibilityNotification_Params> params;
231   params.push_back(ViewHostMsg_AccessibilityNotification_Params());
232   ViewHostMsg_AccessibilityNotification_Params* msg = &params[0];
233   msg->notification_type = ViewHostMsg_AccessibilityNotification_Type::
234       NOTIFICATION_TYPE_CHILDREN_CHANGED;
235   msg->acc_obj = tree2_root;
236   manager->OnAccessibilityNotifications(params);
237 
238   // There should be 5 objects now: the 4 from the new tree, plus the
239   // reference to child3 we kept.
240   EXPECT_EQ(5, CountedBrowserAccessibility::global_obj_count_);
241 
242   // Check that our references to the root, child1, and child2 are still valid,
243   // but that the reference to child3 is now invalid.
244   EXPECT_TRUE(root_accessible->instance_active());
245   EXPECT_TRUE(child1_accessible->instance_active());
246   EXPECT_TRUE(child2_accessible->instance_active());
247   EXPECT_FALSE(child3_accessible->instance_active());
248 
249   // Check that the index in parent has been updated.
250   EXPECT_EQ(1, child1_accessible->index_in_parent());
251   EXPECT_EQ(2, child2_accessible->index_in_parent());
252 
253   // Release our references. The object count should only decrease by 1
254   // for child3.
255   root_accessible->NativeReleaseReference();
256   child1_accessible->NativeReleaseReference();
257   child2_accessible->NativeReleaseReference();
258   child3_accessible->NativeReleaseReference();
259 
260   EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
261 
262   // Delete the manager and make sure all memory is cleaned up.
263   delete manager;
264   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
265 }
266 
TEST(BrowserAccessibilityManagerTest,TestReuseBrowserAccessibilityObjects2)267 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
268   // Similar to the test above, but with a more complicated tree.
269 
270   // Tree 1:
271   //
272   // root
273   //   container
274   //     child1
275   //       grandchild1
276   //     child2
277   //       grandchild2
278   //     child3
279   //       grandchild3
280 
281   WebAccessibility tree1_grandchild1;
282   tree1_grandchild1.id = 4;
283   tree1_grandchild1.name = UTF8ToUTF16("GrandChild1");
284   tree1_grandchild1.role = WebAccessibility::ROLE_BUTTON;
285   tree1_grandchild1.state = 0;
286 
287   WebAccessibility tree1_child1;
288   tree1_child1.id = 3;
289   tree1_child1.name = UTF8ToUTF16("Child1");
290   tree1_child1.role = WebAccessibility::ROLE_BUTTON;
291   tree1_child1.state = 0;
292   tree1_child1.children.push_back(tree1_grandchild1);
293 
294   WebAccessibility tree1_grandchild2;
295   tree1_grandchild2.id = 6;
296   tree1_grandchild2.name = UTF8ToUTF16("GrandChild1");
297   tree1_grandchild2.role = WebAccessibility::ROLE_BUTTON;
298   tree1_grandchild2.state = 0;
299 
300   WebAccessibility tree1_child2;
301   tree1_child2.id = 5;
302   tree1_child2.name = UTF8ToUTF16("Child2");
303   tree1_child2.role = WebAccessibility::ROLE_BUTTON;
304   tree1_child2.state = 0;
305   tree1_child2.children.push_back(tree1_grandchild2);
306 
307   WebAccessibility tree1_grandchild3;
308   tree1_grandchild3.id = 8;
309   tree1_grandchild3.name = UTF8ToUTF16("GrandChild3");
310   tree1_grandchild3.role = WebAccessibility::ROLE_BUTTON;
311   tree1_grandchild3.state = 0;
312 
313   WebAccessibility tree1_child3;
314   tree1_child3.id = 7;
315   tree1_child3.name = UTF8ToUTF16("Child3");
316   tree1_child3.role = WebAccessibility::ROLE_BUTTON;
317   tree1_child3.state = 0;
318   tree1_child3.children.push_back(tree1_grandchild3);
319 
320   WebAccessibility tree1_container;
321   tree1_container.id = 2;
322   tree1_container.name = UTF8ToUTF16("Container");
323   tree1_container.role = WebAccessibility::ROLE_GROUP;
324   tree1_container.state = 0;
325   tree1_container.children.push_back(tree1_child1);
326   tree1_container.children.push_back(tree1_child2);
327   tree1_container.children.push_back(tree1_child3);
328 
329   WebAccessibility tree1_root;
330   tree1_root.id = 1;
331   tree1_root.name = UTF8ToUTF16("Document");
332   tree1_root.role = WebAccessibility::ROLE_DOCUMENT;
333   tree1_root.state = 0;
334   tree1_root.children.push_back(tree1_container);
335 
336   // Tree 2:
337   //
338   // root
339   //   container
340   //     child0         <-- inserted
341   //       grandchild0  <--
342   //     child1
343   //       grandchild1
344   //     child2
345   //       grandchild2
346   //                    <-- child3 (and grandchild3) deleted
347 
348   WebAccessibility tree2_grandchild0;
349   tree2_grandchild0.id = 9;
350   tree2_grandchild0.name = UTF8ToUTF16("GrandChild0");
351   tree2_grandchild0.role = WebAccessibility::ROLE_BUTTON;
352   tree2_grandchild0.state = 0;
353 
354   WebAccessibility tree2_child0;
355   tree2_child0.id = 10;
356   tree2_child0.name = UTF8ToUTF16("Child0");
357   tree2_child0.role = WebAccessibility::ROLE_BUTTON;
358   tree2_child0.state = 0;
359   tree2_child0.children.push_back(tree2_grandchild0);
360 
361   WebAccessibility tree2_grandchild1;
362   tree2_grandchild1.id = 4;
363   tree2_grandchild1.name = UTF8ToUTF16("GrandChild1");
364   tree2_grandchild1.role = WebAccessibility::ROLE_BUTTON;
365   tree2_grandchild1.state = 0;
366 
367   WebAccessibility tree2_child1;
368   tree2_child1.id = 3;
369   tree2_child1.name = UTF8ToUTF16("Child1");
370   tree2_child1.role = WebAccessibility::ROLE_BUTTON;
371   tree2_child1.state = 0;
372   tree2_child1.children.push_back(tree2_grandchild1);
373 
374   WebAccessibility tree2_grandchild2;
375   tree2_grandchild2.id = 6;
376   tree2_grandchild2.name = UTF8ToUTF16("GrandChild1");
377   tree2_grandchild2.role = WebAccessibility::ROLE_BUTTON;
378   tree2_grandchild2.state = 0;
379 
380   WebAccessibility tree2_child2;
381   tree2_child2.id = 5;
382   tree2_child2.name = UTF8ToUTF16("Child2");
383   tree2_child2.role = WebAccessibility::ROLE_BUTTON;
384   tree2_child2.state = 0;
385   tree2_child2.children.push_back(tree2_grandchild2);
386 
387   WebAccessibility tree2_container;
388   tree2_container.id = 2;
389   tree2_container.name = UTF8ToUTF16("Container");
390   tree2_container.role = WebAccessibility::ROLE_GROUP;
391   tree2_container.state = 0;
392   tree2_container.children.push_back(tree2_child0);
393   tree2_container.children.push_back(tree2_child1);
394   tree2_container.children.push_back(tree2_child2);
395 
396   WebAccessibility tree2_root;
397   tree2_root.id = 1;
398   tree2_root.name = UTF8ToUTF16("Document");
399   tree2_root.role = WebAccessibility::ROLE_DOCUMENT;
400   tree2_root.state = 0;
401   tree2_root.children.push_back(tree2_container);
402 
403   // Construct a BrowserAccessibilityManager with tree1.
404   CountedBrowserAccessibility::global_obj_count_ = 0;
405   BrowserAccessibilityManager* manager =
406       BrowserAccessibilityManager::Create(
407           NULL,
408           tree1_root,
409           NULL,
410           new CountedBrowserAccessibilityFactory());
411   ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
412 
413   // Save references to some objects.
414   CountedBrowserAccessibility* root_accessible =
415       static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
416   root_accessible->NativeAddReference();
417   CountedBrowserAccessibility* container_accessible =
418       static_cast<CountedBrowserAccessibility*>(root_accessible->GetChild(0));
419   container_accessible->NativeAddReference();
420   CountedBrowserAccessibility* child2_accessible =
421       static_cast<CountedBrowserAccessibility*>(
422           container_accessible->GetChild(1));
423   child2_accessible->NativeAddReference();
424   CountedBrowserAccessibility* child3_accessible =
425       static_cast<CountedBrowserAccessibility*>(
426           container_accessible->GetChild(2));
427   child3_accessible->NativeAddReference();
428 
429   // Check the index in parent.
430   EXPECT_EQ(1, child2_accessible->index_in_parent());
431   EXPECT_EQ(2, child3_accessible->index_in_parent());
432 
433   // Process a notification containing the changed subtree rooted at
434   // the container.
435   std::vector<ViewHostMsg_AccessibilityNotification_Params> params;
436   params.push_back(ViewHostMsg_AccessibilityNotification_Params());
437   ViewHostMsg_AccessibilityNotification_Params* msg = &params[0];
438   msg->notification_type = ViewHostMsg_AccessibilityNotification_Type::
439       NOTIFICATION_TYPE_CHILDREN_CHANGED;
440   msg->acc_obj = tree2_container;
441   manager->OnAccessibilityNotifications(params);
442 
443   // There should be 9 objects now: the 8 from the new tree, plus the
444   // reference to child3 we kept.
445   EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_);
446 
447   // Check that our references to the root and container and child2 are
448   // still valid, but that the reference to child3 is now invalid.
449   EXPECT_TRUE(root_accessible->instance_active());
450   EXPECT_TRUE(container_accessible->instance_active());
451   EXPECT_TRUE(child2_accessible->instance_active());
452   EXPECT_FALSE(child3_accessible->instance_active());
453 
454   // Check that the index in parent has been updated.
455   EXPECT_EQ(2, child2_accessible->index_in_parent());
456 
457   // Release our references. The object count should only decrease by 1
458   // for child3.
459   root_accessible->NativeReleaseReference();
460   container_accessible->NativeReleaseReference();
461   child2_accessible->NativeReleaseReference();
462   child3_accessible->NativeReleaseReference();
463 
464   EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
465 
466   // Delete the manager and make sure all memory is cleaned up.
467   delete manager;
468   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
469 }
470 
TEST(BrowserAccessibilityManagerTest,TestMoveChildUp)471 TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
472   // Tree 1:
473   //
474   // 1
475   //   2
476   //   3
477   //     4
478 
479   WebAccessibility tree1_4;
480   tree1_4.id = 4;
481   tree1_4.state = 0;
482 
483   WebAccessibility tree1_3;
484   tree1_3.id = 3;
485   tree1_3.state = 0;
486   tree1_3.children.push_back(tree1_4);
487 
488   WebAccessibility tree1_2;
489   tree1_2.id = 2;
490   tree1_2.state = 0;
491 
492   WebAccessibility tree1_1;
493   tree1_1.id = 1;
494   tree1_1.state = 0;
495   tree1_1.children.push_back(tree1_2);
496   tree1_1.children.push_back(tree1_3);
497 
498   // Tree 2:
499   //
500   // 1
501   //   4    <-- moves up a level and gains child
502   //     6  <-- new
503   //   5    <-- new
504 
505   WebAccessibility tree2_6;
506   tree2_6.id = 6;
507   tree2_6.state = 0;
508 
509   WebAccessibility tree2_5;
510   tree2_5.id = 5;
511   tree2_5.state = 0;
512 
513   WebAccessibility tree2_4;
514   tree2_4.id = 4;
515   tree2_4.state = 0;
516   tree2_4.children.push_back(tree2_6);
517 
518   WebAccessibility tree2_1;
519   tree2_1.id = 1;
520   tree2_1.state = 0;
521   tree2_1.children.push_back(tree2_4);
522   tree2_1.children.push_back(tree2_5);
523 
524   // Construct a BrowserAccessibilityManager with tree1.
525   CountedBrowserAccessibility::global_obj_count_ = 0;
526   BrowserAccessibilityManager* manager =
527       BrowserAccessibilityManager::Create(
528           NULL,
529           tree1_1,
530           NULL,
531           new CountedBrowserAccessibilityFactory());
532   ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
533 
534   // Process a notification containing the changed subtree.
535   std::vector<ViewHostMsg_AccessibilityNotification_Params> params;
536   params.push_back(ViewHostMsg_AccessibilityNotification_Params());
537   ViewHostMsg_AccessibilityNotification_Params* msg = &params[0];
538   msg->notification_type = ViewHostMsg_AccessibilityNotification_Type::
539       NOTIFICATION_TYPE_CHILDREN_CHANGED;
540   msg->acc_obj = tree2_1;
541   manager->OnAccessibilityNotifications(params);
542 
543   // There should be 4 objects now.
544   EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
545 
546   // Delete the manager and make sure all memory is cleaned up.
547   delete manager;
548   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
549 }
550