Home | History | Annotate | Download | only in accessibility
      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:
     20   CountedBrowserAccessibility() {
     21     global_obj_count_++;
     22     native_ref_count_ = 1;
     23   }
     24   virtual ~CountedBrowserAccessibility() {
     25     global_obj_count_--;
     26   }
     27 
     28   virtual void NativeAddReference() OVERRIDE {
     29     native_ref_count_++;
     30   }
     31 
     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:
     48   virtual ~CountedBrowserAccessibilityFactory() {}
     49   virtual BrowserAccessibility* Create() {
     50     return new CountedBrowserAccessibility();
     51   }
     52 };
     53 
     54 }  // anonymous namespace
     55 
     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 
    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 
    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 
    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