Home | History | Annotate | Download | only in accessibility
      1 // Copyright (c) 2012 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/strings/string16.h"
      6 #include "base/strings/utf_string_conversions.h"
      7 #include "content/browser/accessibility/browser_accessibility.h"
      8 #include "content/browser/accessibility/browser_accessibility_manager.h"
      9 #if defined(OS_WIN)
     10 #include "content/browser/accessibility/browser_accessibility_win.h"
     11 #endif
     12 #include "content/common/accessibility_messages.h"
     13 #include "testing/gtest/include/gtest/gtest.h"
     14 
     15 namespace content {
     16 namespace {
     17 
     18 // Subclass of BrowserAccessibility that counts the number of instances.
     19 class CountedBrowserAccessibility : public BrowserAccessibility {
     20  public:
     21   CountedBrowserAccessibility() {
     22     global_obj_count_++;
     23     native_ref_count_ = 1;
     24   }
     25   virtual ~CountedBrowserAccessibility() {
     26     global_obj_count_--;
     27   }
     28 
     29   virtual void NativeAddReference() OVERRIDE {
     30     native_ref_count_++;
     31   }
     32 
     33   virtual void NativeReleaseReference() OVERRIDE {
     34     native_ref_count_--;
     35     if (native_ref_count_ == 0)
     36       delete this;
     37   }
     38 
     39   int native_ref_count_;
     40   static int global_obj_count_;
     41 
     42 #if defined(OS_WIN)
     43   // Adds some padding to prevent a heap-buffer-overflow when an instance of
     44   // this class is casted into a BrowserAccessibilityWin pointer.
     45   // http://crbug.com/235508
     46   // TODO(dmazzoni): Fix this properly.
     47   static const size_t kDataSize = sizeof(int) + sizeof(BrowserAccessibility);
     48   uint8 padding_[sizeof(BrowserAccessibilityWin) - kDataSize];
     49 #endif
     50 };
     51 
     52 int CountedBrowserAccessibility::global_obj_count_ = 0;
     53 
     54 // Factory that creates a CountedBrowserAccessibility.
     55 class CountedBrowserAccessibilityFactory
     56     : public BrowserAccessibilityFactory {
     57  public:
     58   virtual ~CountedBrowserAccessibilityFactory() {}
     59   virtual BrowserAccessibility* Create() OVERRIDE {
     60     return new CountedBrowserAccessibility();
     61   }
     62 };
     63 
     64 class TestBrowserAccessibilityDelegate
     65     : public BrowserAccessibilityDelegate {
     66  public:
     67   TestBrowserAccessibilityDelegate()
     68       : got_fatal_error_(false) {}
     69 
     70   virtual void AccessibilitySetFocus(int acc_obj_id) OVERRIDE {}
     71   virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE {}
     72   virtual void AccessibilityShowMenu(const gfx::Point& point) OVERRIDE {}
     73   virtual void AccessibilityScrollToMakeVisible(
     74       int acc_obj_id, const gfx::Rect& subfocus) OVERRIDE {}
     75   virtual void AccessibilityScrollToPoint(
     76       int acc_obj_id, const gfx::Point& point) OVERRIDE {}
     77   virtual void AccessibilitySetTextSelection(
     78       int acc_obj_id, int start_offset, int end_offset) OVERRIDE {}
     79   virtual bool AccessibilityViewHasFocus() const OVERRIDE {
     80     return false;
     81   }
     82   virtual gfx::Rect AccessibilityGetViewBounds() const OVERRIDE {
     83     return gfx::Rect();
     84   }
     85   virtual gfx::Point AccessibilityOriginInScreen(
     86       const gfx::Rect& bounds) const OVERRIDE {
     87     return gfx::Point();
     88   }
     89   virtual void AccessibilityHitTest(const gfx::Point& point) OVERRIDE {}
     90   virtual void AccessibilityFatalError() OVERRIDE {
     91     got_fatal_error_ = true;
     92   }
     93   virtual gfx::AcceleratedWidget AccessibilityGetAcceleratedWidget() OVERRIDE {
     94     return gfx::kNullAcceleratedWidget;
     95   }
     96   virtual gfx::NativeViewAccessible AccessibilityGetNativeViewAccessible()
     97       OVERRIDE {
     98     return NULL;
     99   }
    100   virtual BrowserAccessibilityManager* AccessibilityGetChildFrame(
    101       int accessibility_node_id) OVERRIDE {
    102     return NULL;
    103   }
    104   virtual BrowserAccessibility* AccessibilityGetParentFrame() OVERRIDE {
    105     return NULL;
    106   }
    107 
    108   bool got_fatal_error() const { return got_fatal_error_; }
    109   void reset_got_fatal_error() { got_fatal_error_ = false; }
    110 
    111 private:
    112   bool got_fatal_error_;
    113 };
    114 
    115 }  // anonymous namespace
    116 
    117 TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
    118   // Create ui::AXNodeData objects for a simple document tree,
    119   // representing the accessibility information used to initialize
    120   // BrowserAccessibilityManager.
    121   ui::AXNodeData button;
    122   button.id = 2;
    123   button.SetName("Button");
    124   button.role = ui::AX_ROLE_BUTTON;
    125   button.state = 0;
    126 
    127   ui::AXNodeData checkbox;
    128   checkbox.id = 3;
    129   checkbox.SetName("Checkbox");
    130   checkbox.role = ui::AX_ROLE_CHECK_BOX;
    131   checkbox.state = 0;
    132 
    133   ui::AXNodeData root;
    134   root.id = 1;
    135   root.SetName("Document");
    136   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
    137   root.state = 0;
    138   root.child_ids.push_back(2);
    139   root.child_ids.push_back(3);
    140 
    141   // Construct a BrowserAccessibilityManager with this
    142   // ui::AXNodeData tree and a factory for an instance-counting
    143   // BrowserAccessibility, and ensure that exactly 3 instances were
    144   // created. Note that the manager takes ownership of the factory.
    145   CountedBrowserAccessibility::global_obj_count_ = 0;
    146   BrowserAccessibilityManager* manager =
    147       BrowserAccessibilityManager::Create(
    148           MakeAXTreeUpdate(root, button, checkbox),
    149           NULL,
    150           new CountedBrowserAccessibilityFactory());
    151 
    152   ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
    153 
    154   // Delete the manager and test that all 3 instances are deleted.
    155   delete manager;
    156   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
    157 
    158   // Construct a manager again, and this time save references to two of
    159   // the three nodes in the tree.
    160   manager =
    161       BrowserAccessibilityManager::Create(
    162           MakeAXTreeUpdate(root, button, checkbox),
    163           NULL,
    164           new CountedBrowserAccessibilityFactory());
    165   ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
    166 
    167   CountedBrowserAccessibility* root_accessible =
    168       static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
    169   root_accessible->NativeAddReference();
    170   CountedBrowserAccessibility* child1_accessible =
    171       static_cast<CountedBrowserAccessibility*>(
    172           root_accessible->PlatformGetChild(1));
    173   child1_accessible->NativeAddReference();
    174 
    175   // Now delete the manager, and only one of the three nodes in the tree
    176   // should be released.
    177   delete manager;
    178   ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_);
    179 
    180   // Release each of our references and make sure that each one results in
    181   // the instance being deleted as its reference count hits zero.
    182   root_accessible->NativeReleaseReference();
    183   ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_);
    184   child1_accessible->NativeReleaseReference();
    185   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
    186 }
    187 
    188 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
    189   // Make sure that changes to a subtree reuse as many objects as possible.
    190 
    191   // Tree 1:
    192   //
    193   // root
    194   //   child1
    195   //   child2
    196   //   child3
    197 
    198   ui::AXNodeData tree1_child1;
    199   tree1_child1.id = 2;
    200   tree1_child1.SetName("Child1");
    201   tree1_child1.role = ui::AX_ROLE_BUTTON;
    202   tree1_child1.state = 0;
    203 
    204   ui::AXNodeData tree1_child2;
    205   tree1_child2.id = 3;
    206   tree1_child2.SetName("Child2");
    207   tree1_child2.role = ui::AX_ROLE_BUTTON;
    208   tree1_child2.state = 0;
    209 
    210   ui::AXNodeData tree1_child3;
    211   tree1_child3.id = 4;
    212   tree1_child3.SetName("Child3");
    213   tree1_child3.role = ui::AX_ROLE_BUTTON;
    214   tree1_child3.state = 0;
    215 
    216   ui::AXNodeData tree1_root;
    217   tree1_root.id = 1;
    218   tree1_root.SetName("Document");
    219   tree1_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
    220   tree1_root.state = 0;
    221   tree1_root.child_ids.push_back(2);
    222   tree1_root.child_ids.push_back(3);
    223   tree1_root.child_ids.push_back(4);
    224 
    225   // Tree 2:
    226   //
    227   // root
    228   //   child0  <-- inserted
    229   //   child1
    230   //   child2
    231   //           <-- child3 deleted
    232 
    233   ui::AXNodeData tree2_child0;
    234   tree2_child0.id = 5;
    235   tree2_child0.SetName("Child0");
    236   tree2_child0.role = ui::AX_ROLE_BUTTON;
    237   tree2_child0.state = 0;
    238 
    239   ui::AXNodeData tree2_root;
    240   tree2_root.id = 1;
    241   tree2_root.SetName("DocumentChanged");
    242   tree2_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
    243   tree2_root.state = 0;
    244   tree2_root.child_ids.push_back(5);
    245   tree2_root.child_ids.push_back(2);
    246   tree2_root.child_ids.push_back(3);
    247 
    248   // Construct a BrowserAccessibilityManager with tree1.
    249   CountedBrowserAccessibility::global_obj_count_ = 0;
    250   BrowserAccessibilityManager* manager =
    251       BrowserAccessibilityManager::Create(
    252           MakeAXTreeUpdate(tree1_root,
    253                            tree1_child1, tree1_child2, tree1_child3),
    254           NULL,
    255           new CountedBrowserAccessibilityFactory());
    256   ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
    257 
    258   // Save references to all of the objects.
    259   CountedBrowserAccessibility* root_accessible =
    260       static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
    261   root_accessible->NativeAddReference();
    262   CountedBrowserAccessibility* child1_accessible =
    263       static_cast<CountedBrowserAccessibility*>(
    264           root_accessible->PlatformGetChild(0));
    265   child1_accessible->NativeAddReference();
    266   CountedBrowserAccessibility* child2_accessible =
    267       static_cast<CountedBrowserAccessibility*>(
    268           root_accessible->PlatformGetChild(1));
    269   child2_accessible->NativeAddReference();
    270   CountedBrowserAccessibility* child3_accessible =
    271       static_cast<CountedBrowserAccessibility*>(
    272           root_accessible->PlatformGetChild(2));
    273   child3_accessible->NativeAddReference();
    274 
    275   // Check the index in parent.
    276   EXPECT_EQ(0, child1_accessible->GetIndexInParent());
    277   EXPECT_EQ(1, child2_accessible->GetIndexInParent());
    278   EXPECT_EQ(2, child3_accessible->GetIndexInParent());
    279 
    280   // Process a notification containing the changed subtree.
    281   std::vector<AccessibilityHostMsg_EventParams> params;
    282   params.push_back(AccessibilityHostMsg_EventParams());
    283   AccessibilityHostMsg_EventParams* msg = &params[0];
    284   msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
    285   msg->update.nodes.push_back(tree2_root);
    286   msg->update.nodes.push_back(tree2_child0);
    287   msg->id = tree2_root.id;
    288   manager->OnAccessibilityEvents(params);
    289 
    290   // There should be 5 objects now: the 4 from the new tree, plus the
    291   // reference to child3 we kept.
    292   EXPECT_EQ(5, CountedBrowserAccessibility::global_obj_count_);
    293 
    294   // Check that our references to the root, child1, and child2 are still valid,
    295   // but that the reference to child3 is now invalid.
    296   EXPECT_TRUE(root_accessible->instance_active());
    297   EXPECT_TRUE(child1_accessible->instance_active());
    298   EXPECT_TRUE(child2_accessible->instance_active());
    299   EXPECT_FALSE(child3_accessible->instance_active());
    300 
    301   // Check that the index in parent has been updated.
    302   EXPECT_EQ(1, child1_accessible->GetIndexInParent());
    303   EXPECT_EQ(2, child2_accessible->GetIndexInParent());
    304 
    305   // Release our references. The object count should only decrease by 1
    306   // for child3.
    307   root_accessible->NativeReleaseReference();
    308   child1_accessible->NativeReleaseReference();
    309   child2_accessible->NativeReleaseReference();
    310   child3_accessible->NativeReleaseReference();
    311 
    312   EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
    313 
    314   // Delete the manager and make sure all memory is cleaned up.
    315   delete manager;
    316   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
    317 }
    318 
    319 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
    320   // Similar to the test above, but with a more complicated tree.
    321 
    322   // Tree 1:
    323   //
    324   // root
    325   //   container
    326   //     child1
    327   //       grandchild1
    328   //     child2
    329   //       grandchild2
    330   //     child3
    331   //       grandchild3
    332 
    333   ui::AXNodeData tree1_grandchild1;
    334   tree1_grandchild1.id = 4;
    335   tree1_grandchild1.SetName("GrandChild1");
    336   tree1_grandchild1.role = ui::AX_ROLE_BUTTON;
    337   tree1_grandchild1.state = 0;
    338 
    339   ui::AXNodeData tree1_child1;
    340   tree1_child1.id = 3;
    341   tree1_child1.SetName("Child1");
    342   tree1_child1.role = ui::AX_ROLE_BUTTON;
    343   tree1_child1.state = 0;
    344   tree1_child1.child_ids.push_back(4);
    345 
    346   ui::AXNodeData tree1_grandchild2;
    347   tree1_grandchild2.id = 6;
    348   tree1_grandchild2.SetName("GrandChild1");
    349   tree1_grandchild2.role = ui::AX_ROLE_BUTTON;
    350   tree1_grandchild2.state = 0;
    351 
    352   ui::AXNodeData tree1_child2;
    353   tree1_child2.id = 5;
    354   tree1_child2.SetName("Child2");
    355   tree1_child2.role = ui::AX_ROLE_BUTTON;
    356   tree1_child2.state = 0;
    357   tree1_child2.child_ids.push_back(6);
    358 
    359   ui::AXNodeData tree1_grandchild3;
    360   tree1_grandchild3.id = 8;
    361   tree1_grandchild3.SetName("GrandChild3");
    362   tree1_grandchild3.role = ui::AX_ROLE_BUTTON;
    363   tree1_grandchild3.state = 0;
    364 
    365   ui::AXNodeData tree1_child3;
    366   tree1_child3.id = 7;
    367   tree1_child3.SetName("Child3");
    368   tree1_child3.role = ui::AX_ROLE_BUTTON;
    369   tree1_child3.state = 0;
    370   tree1_child3.child_ids.push_back(8);
    371 
    372   ui::AXNodeData tree1_container;
    373   tree1_container.id = 2;
    374   tree1_container.SetName("Container");
    375   tree1_container.role = ui::AX_ROLE_GROUP;
    376   tree1_container.state = 0;
    377   tree1_container.child_ids.push_back(3);
    378   tree1_container.child_ids.push_back(5);
    379   tree1_container.child_ids.push_back(7);
    380 
    381   ui::AXNodeData tree1_root;
    382   tree1_root.id = 1;
    383   tree1_root.SetName("Document");
    384   tree1_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
    385   tree1_root.state = 0;
    386   tree1_root.child_ids.push_back(2);
    387 
    388   // Tree 2:
    389   //
    390   // root
    391   //   container
    392   //     child0         <-- inserted
    393   //       grandchild0  <--
    394   //     child1
    395   //       grandchild1
    396   //     child2
    397   //       grandchild2
    398   //                    <-- child3 (and grandchild3) deleted
    399 
    400   ui::AXNodeData tree2_grandchild0;
    401   tree2_grandchild0.id = 9;
    402   tree2_grandchild0.SetName("GrandChild0");
    403   tree2_grandchild0.role = ui::AX_ROLE_BUTTON;
    404   tree2_grandchild0.state = 0;
    405 
    406   ui::AXNodeData tree2_child0;
    407   tree2_child0.id = 10;
    408   tree2_child0.SetName("Child0");
    409   tree2_child0.role = ui::AX_ROLE_BUTTON;
    410   tree2_child0.state = 0;
    411   tree2_child0.child_ids.push_back(9);
    412 
    413   ui::AXNodeData tree2_container;
    414   tree2_container.id = 2;
    415   tree2_container.SetName("Container");
    416   tree2_container.role = ui::AX_ROLE_GROUP;
    417   tree2_container.state = 0;
    418   tree2_container.child_ids.push_back(10);
    419   tree2_container.child_ids.push_back(3);
    420   tree2_container.child_ids.push_back(5);
    421 
    422   // Construct a BrowserAccessibilityManager with tree1.
    423   CountedBrowserAccessibility::global_obj_count_ = 0;
    424   BrowserAccessibilityManager* manager =
    425       BrowserAccessibilityManager::Create(
    426           MakeAXTreeUpdate(tree1_root, tree1_container,
    427                            tree1_child1, tree1_grandchild1,
    428                            tree1_child2, tree1_grandchild2,
    429                            tree1_child3, tree1_grandchild3),
    430           NULL,
    431           new CountedBrowserAccessibilityFactory());
    432   ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
    433 
    434   // Save references to some objects.
    435   CountedBrowserAccessibility* root_accessible =
    436       static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
    437   root_accessible->NativeAddReference();
    438   CountedBrowserAccessibility* container_accessible =
    439       static_cast<CountedBrowserAccessibility*>(
    440           root_accessible->PlatformGetChild(0));
    441   container_accessible->NativeAddReference();
    442   CountedBrowserAccessibility* child2_accessible =
    443       static_cast<CountedBrowserAccessibility*>(
    444           container_accessible->PlatformGetChild(1));
    445   child2_accessible->NativeAddReference();
    446   CountedBrowserAccessibility* child3_accessible =
    447       static_cast<CountedBrowserAccessibility*>(
    448           container_accessible->PlatformGetChild(2));
    449   child3_accessible->NativeAddReference();
    450 
    451   // Check the index in parent.
    452   EXPECT_EQ(1, child2_accessible->GetIndexInParent());
    453   EXPECT_EQ(2, child3_accessible->GetIndexInParent());
    454 
    455   // Process a notification containing the changed subtree rooted at
    456   // the container.
    457   std::vector<AccessibilityHostMsg_EventParams> params;
    458   params.push_back(AccessibilityHostMsg_EventParams());
    459   AccessibilityHostMsg_EventParams* msg = &params[0];
    460   msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
    461   msg->update.nodes.push_back(tree2_container);
    462   msg->update.nodes.push_back(tree2_child0);
    463   msg->update.nodes.push_back(tree2_grandchild0);
    464   msg->id = tree2_container.id;
    465   manager->OnAccessibilityEvents(params);
    466 
    467   // There should be 9 objects now: the 8 from the new tree, plus the
    468   // reference to child3 we kept.
    469   EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_);
    470 
    471   // Check that our references to the root and container and child2 are
    472   // still valid, but that the reference to child3 is now invalid.
    473   EXPECT_TRUE(root_accessible->instance_active());
    474   EXPECT_TRUE(container_accessible->instance_active());
    475   EXPECT_TRUE(child2_accessible->instance_active());
    476   EXPECT_FALSE(child3_accessible->instance_active());
    477 
    478   // Ensure that we retain the parent of the detached subtree.
    479   EXPECT_EQ(root_accessible, container_accessible->GetParent());
    480   EXPECT_EQ(0, container_accessible->GetIndexInParent());
    481 
    482   // Check that the index in parent has been updated.
    483   EXPECT_EQ(2, child2_accessible->GetIndexInParent());
    484 
    485   // Release our references. The object count should only decrease by 1
    486   // for child3.
    487   root_accessible->NativeReleaseReference();
    488   container_accessible->NativeReleaseReference();
    489   child2_accessible->NativeReleaseReference();
    490   child3_accessible->NativeReleaseReference();
    491 
    492   EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
    493 
    494   // Delete the manager and make sure all memory is cleaned up.
    495   delete manager;
    496   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
    497 }
    498 
    499 TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
    500   // Tree 1:
    501   //
    502   // 1
    503   //   2
    504   //   3
    505   //     4
    506 
    507   ui::AXNodeData tree1_4;
    508   tree1_4.id = 4;
    509   tree1_4.state = 0;
    510 
    511   ui::AXNodeData tree1_3;
    512   tree1_3.id = 3;
    513   tree1_3.state = 0;
    514   tree1_3.child_ids.push_back(4);
    515 
    516   ui::AXNodeData tree1_2;
    517   tree1_2.id = 2;
    518   tree1_2.state = 0;
    519 
    520   ui::AXNodeData tree1_1;
    521   tree1_1.id = 1;
    522   tree1_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
    523   tree1_1.state = 0;
    524   tree1_1.child_ids.push_back(2);
    525   tree1_1.child_ids.push_back(3);
    526 
    527   // Tree 2:
    528   //
    529   // 1
    530   //   4    <-- moves up a level and gains child
    531   //     6  <-- new
    532   //   5    <-- new
    533 
    534   ui::AXNodeData tree2_6;
    535   tree2_6.id = 6;
    536   tree2_6.state = 0;
    537 
    538   ui::AXNodeData tree2_5;
    539   tree2_5.id = 5;
    540   tree2_5.state = 0;
    541 
    542   ui::AXNodeData tree2_4;
    543   tree2_4.id = 4;
    544   tree2_4.state = 0;
    545   tree2_4.child_ids.push_back(6);
    546 
    547   ui::AXNodeData tree2_1;
    548   tree2_1.id = 1;
    549   tree2_1.state = 0;
    550   tree2_1.child_ids.push_back(4);
    551   tree2_1.child_ids.push_back(5);
    552 
    553   // Construct a BrowserAccessibilityManager with tree1.
    554   CountedBrowserAccessibility::global_obj_count_ = 0;
    555   BrowserAccessibilityManager* manager =
    556       BrowserAccessibilityManager::Create(
    557           MakeAXTreeUpdate(tree1_1, tree1_2, tree1_3, tree1_4),
    558           NULL,
    559           new CountedBrowserAccessibilityFactory());
    560   ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
    561 
    562   // Process a notification containing the changed subtree.
    563   std::vector<AccessibilityHostMsg_EventParams> params;
    564   params.push_back(AccessibilityHostMsg_EventParams());
    565   AccessibilityHostMsg_EventParams* msg = &params[0];
    566   msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
    567   msg->update.nodes.push_back(tree2_1);
    568   msg->update.nodes.push_back(tree2_4);
    569   msg->update.nodes.push_back(tree2_5);
    570   msg->update.nodes.push_back(tree2_6);
    571   msg->id = tree2_1.id;
    572   manager->OnAccessibilityEvents(params);
    573 
    574   // There should be 4 objects now.
    575   EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
    576 
    577   // Delete the manager and make sure all memory is cleaned up.
    578   delete manager;
    579   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
    580 }
    581 
    582 TEST(BrowserAccessibilityManagerTest, TestFatalError) {
    583   // Test that BrowserAccessibilityManager raises a fatal error
    584   // (which will crash the renderer) if the same id is used in
    585   // two places in the tree.
    586 
    587   ui::AXNodeData root;
    588   root.id = 1;
    589   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
    590   root.child_ids.push_back(2);
    591   root.child_ids.push_back(2);
    592 
    593   CountedBrowserAccessibilityFactory* factory =
    594       new CountedBrowserAccessibilityFactory();
    595   scoped_ptr<TestBrowserAccessibilityDelegate> delegate(
    596       new TestBrowserAccessibilityDelegate());
    597   scoped_ptr<BrowserAccessibilityManager> manager;
    598   ASSERT_FALSE(delegate->got_fatal_error());
    599   manager.reset(BrowserAccessibilityManager::Create(
    600       MakeAXTreeUpdate(root),
    601       delegate.get(),
    602       factory));
    603   ASSERT_TRUE(delegate->got_fatal_error());
    604 
    605   ui::AXNodeData root2;
    606   root2.id = 1;
    607   root2.role = ui::AX_ROLE_ROOT_WEB_AREA;
    608   root2.child_ids.push_back(2);
    609   root2.child_ids.push_back(3);
    610 
    611   ui::AXNodeData child1;
    612   child1.id = 2;
    613   child1.child_ids.push_back(4);
    614   child1.child_ids.push_back(5);
    615 
    616   ui::AXNodeData child2;
    617   child2.id = 3;
    618   child2.child_ids.push_back(6);
    619   child2.child_ids.push_back(5);  // Duplicate
    620 
    621   ui::AXNodeData grandchild4;
    622   grandchild4.id = 4;
    623 
    624   ui::AXNodeData grandchild5;
    625   grandchild5.id = 5;
    626 
    627   ui::AXNodeData grandchild6;
    628   grandchild6.id = 6;
    629 
    630   delegate->reset_got_fatal_error();
    631   factory = new CountedBrowserAccessibilityFactory();
    632   manager.reset(BrowserAccessibilityManager::Create(
    633       MakeAXTreeUpdate(root2, child1, child2,
    634                        grandchild4, grandchild5, grandchild6),
    635       delegate.get(),
    636       factory));
    637   ASSERT_TRUE(delegate->got_fatal_error());
    638 }
    639 
    640 TEST(BrowserAccessibilityManagerTest, BoundsForRange) {
    641   ui::AXNodeData root;
    642   root.id = 1;
    643   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
    644 
    645   ui::AXNodeData static_text;
    646   static_text.id = 2;
    647   static_text.SetValue("Hello, world.");
    648   static_text.role = ui::AX_ROLE_STATIC_TEXT;
    649   static_text.location = gfx::Rect(100, 100, 29, 18);
    650   root.child_ids.push_back(2);
    651 
    652   ui::AXNodeData inline_text1;
    653   inline_text1.id = 3;
    654   inline_text1.SetValue("Hello, ");
    655   inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
    656   inline_text1.location = gfx::Rect(100, 100, 29, 9);
    657   inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
    658                                ui::AX_TEXT_DIRECTION_LR);
    659   std::vector<int32> character_offsets1;
    660   character_offsets1.push_back(6);   // 0
    661   character_offsets1.push_back(11);  // 1
    662   character_offsets1.push_back(16);  // 2
    663   character_offsets1.push_back(21);  // 3
    664   character_offsets1.push_back(26);  // 4
    665   character_offsets1.push_back(29);  // 5
    666   character_offsets1.push_back(29);  // 6 (note that the space has no width)
    667   inline_text1.AddIntListAttribute(
    668       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
    669   static_text.child_ids.push_back(3);
    670 
    671   ui::AXNodeData inline_text2;
    672   inline_text2.id = 4;
    673   inline_text2.SetValue("world.");
    674   inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
    675   inline_text2.location = gfx::Rect(100, 109, 28, 9);
    676   inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
    677                                ui::AX_TEXT_DIRECTION_LR);
    678   std::vector<int32> character_offsets2;
    679   character_offsets2.push_back(5);
    680   character_offsets2.push_back(10);
    681   character_offsets2.push_back(15);
    682   character_offsets2.push_back(20);
    683   character_offsets2.push_back(25);
    684   character_offsets2.push_back(28);
    685   inline_text2.AddIntListAttribute(
    686       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
    687   static_text.child_ids.push_back(4);
    688 
    689   scoped_ptr<BrowserAccessibilityManager> manager(
    690       BrowserAccessibilityManager::Create(
    691           MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
    692           NULL,
    693           new CountedBrowserAccessibilityFactory()));
    694 
    695   BrowserAccessibility* root_accessible = manager->GetRoot();
    696   BrowserAccessibility* static_text_accessible =
    697       root_accessible->PlatformGetChild(0);
    698 
    699   EXPECT_EQ(gfx::Rect(100, 100, 6, 9).ToString(),
    700             static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
    701 
    702   EXPECT_EQ(gfx::Rect(100, 100, 26, 9).ToString(),
    703             static_text_accessible->GetLocalBoundsForRange(0, 5).ToString());
    704 
    705   EXPECT_EQ(gfx::Rect(100, 109, 5, 9).ToString(),
    706             static_text_accessible->GetLocalBoundsForRange(7, 1).ToString());
    707 
    708   EXPECT_EQ(gfx::Rect(100, 109, 25, 9).ToString(),
    709             static_text_accessible->GetLocalBoundsForRange(7, 5).ToString());
    710 
    711   EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
    712             static_text_accessible->GetLocalBoundsForRange(5, 3).ToString());
    713 
    714   EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
    715             static_text_accessible->GetLocalBoundsForRange(0, 13).ToString());
    716 
    717   // Test range that's beyond the text.
    718   EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
    719             static_text_accessible->GetLocalBoundsForRange(-1, 999).ToString());
    720 
    721   // Test that we can call bounds for range on the parent element, too,
    722   // and it still works.
    723   EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
    724             root_accessible->GetLocalBoundsForRange(0, 13).ToString());
    725 }
    726 
    727 TEST(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) {
    728   // In this example, we assume that the string "123abc" is rendered with
    729   // "123" going left-to-right and "abc" going right-to-left. In other
    730   // words, on-screen it would look like "123cba". This is possible to
    731   // acheive if the source string had unicode control characters
    732   // to switch directions. This test doesn't worry about how, though - it just
    733   // tests that if something like that were to occur, GetLocalBoundsForRange
    734   // returns the correct bounds for different ranges.
    735 
    736   ui::AXNodeData root;
    737   root.id = 1;
    738   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
    739 
    740   ui::AXNodeData static_text;
    741   static_text.id = 2;
    742   static_text.SetValue("123abc");
    743   static_text.role = ui::AX_ROLE_STATIC_TEXT;
    744   static_text.location = gfx::Rect(100, 100, 60, 20);
    745   root.child_ids.push_back(2);
    746 
    747   ui::AXNodeData inline_text1;
    748   inline_text1.id = 3;
    749   inline_text1.SetValue("123");
    750   inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
    751   inline_text1.location = gfx::Rect(100, 100, 30, 20);
    752   inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
    753                                ui::AX_TEXT_DIRECTION_LR);
    754   std::vector<int32> character_offsets1;
    755   character_offsets1.push_back(10);  // 0
    756   character_offsets1.push_back(20);  // 1
    757   character_offsets1.push_back(30);  // 2
    758   inline_text1.AddIntListAttribute(
    759       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
    760   static_text.child_ids.push_back(3);
    761 
    762   ui::AXNodeData inline_text2;
    763   inline_text2.id = 4;
    764   inline_text2.SetValue("abc");
    765   inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
    766   inline_text2.location = gfx::Rect(130, 100, 30, 20);
    767   inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
    768                                ui::AX_TEXT_DIRECTION_RL);
    769   std::vector<int32> character_offsets2;
    770   character_offsets2.push_back(10);
    771   character_offsets2.push_back(20);
    772   character_offsets2.push_back(30);
    773   inline_text2.AddIntListAttribute(
    774       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
    775   static_text.child_ids.push_back(4);
    776 
    777   scoped_ptr<BrowserAccessibilityManager> manager(
    778       BrowserAccessibilityManager::Create(
    779           MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
    780           NULL,
    781           new CountedBrowserAccessibilityFactory()));
    782 
    783   BrowserAccessibility* root_accessible = manager->GetRoot();
    784   BrowserAccessibility* static_text_accessible =
    785       root_accessible->PlatformGetChild(0);
    786 
    787   EXPECT_EQ(gfx::Rect(100, 100, 60, 20).ToString(),
    788             static_text_accessible->GetLocalBoundsForRange(0, 6).ToString());
    789 
    790   EXPECT_EQ(gfx::Rect(100, 100, 10, 20).ToString(),
    791             static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
    792 
    793   EXPECT_EQ(gfx::Rect(100, 100, 30, 20).ToString(),
    794             static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
    795 
    796   EXPECT_EQ(gfx::Rect(150, 100, 10, 20).ToString(),
    797             static_text_accessible->GetLocalBoundsForRange(3, 1).ToString());
    798 
    799   EXPECT_EQ(gfx::Rect(130, 100, 30, 20).ToString(),
    800             static_text_accessible->GetLocalBoundsForRange(3, 3).ToString());
    801 
    802   // This range is only two characters, but because of the direction switch
    803   // the bounds are as wide as four characters.
    804   EXPECT_EQ(gfx::Rect(120, 100, 40, 20).ToString(),
    805             static_text_accessible->GetLocalBoundsForRange(2, 2).ToString());
    806 }
    807 
    808 #if defined(OS_WIN)
    809 #define MAYBE_BoundsForRangeOnParentElement \
    810   DISABLED_BoundsForRangeOnParentElement
    811 #else
    812 #define MAYBE_BoundsForRangeOnParentElement BoundsForRangeOnParentElement
    813 #endif
    814 TEST(BrowserAccessibilityManagerTest, MAYBE_BoundsForRangeOnParentElement) {
    815   ui::AXNodeData root;
    816   root.id = 1;
    817   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
    818   root.child_ids.push_back(2);
    819 
    820   ui::AXNodeData div;
    821   div.id = 2;
    822   div.role = ui::AX_ROLE_DIV;
    823   div.location = gfx::Rect(100, 100, 100, 20);
    824   div.child_ids.push_back(3);
    825   div.child_ids.push_back(4);
    826   div.child_ids.push_back(5);
    827 
    828   ui::AXNodeData static_text1;
    829   static_text1.id = 3;
    830   static_text1.SetValue("AB");
    831   static_text1.role = ui::AX_ROLE_STATIC_TEXT;
    832   static_text1.location = gfx::Rect(100, 100, 40, 20);
    833   static_text1.child_ids.push_back(6);
    834 
    835   ui::AXNodeData img;
    836   img.id = 4;
    837   img.role = ui::AX_ROLE_IMAGE;
    838   img.location = gfx::Rect(140, 100, 20, 20);
    839 
    840   ui::AXNodeData static_text2;
    841   static_text2.id = 5;
    842   static_text2.SetValue("CD");
    843   static_text2.role = ui::AX_ROLE_STATIC_TEXT;
    844   static_text2.location = gfx::Rect(160, 100, 40, 20);
    845   static_text2.child_ids.push_back(7);
    846 
    847   ui::AXNodeData inline_text1;
    848   inline_text1.id = 6;
    849   inline_text1.SetValue("AB");
    850   inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
    851   inline_text1.location = gfx::Rect(100, 100, 40, 20);
    852   inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
    853                                ui::AX_TEXT_DIRECTION_LR);
    854   std::vector<int32> character_offsets1;
    855   character_offsets1.push_back(20);  // 0
    856   character_offsets1.push_back(40);  // 1
    857   inline_text1.AddIntListAttribute(
    858       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
    859 
    860   ui::AXNodeData inline_text2;
    861   inline_text2.id = 7;
    862   inline_text2.SetValue("CD");
    863   inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
    864   inline_text2.location = gfx::Rect(160, 100, 40, 20);
    865   inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
    866                                ui::AX_TEXT_DIRECTION_LR);
    867   std::vector<int32> character_offsets2;
    868   character_offsets2.push_back(20);  // 0
    869   character_offsets2.push_back(40);  // 1
    870   inline_text2.AddIntListAttribute(
    871       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
    872 
    873   scoped_ptr<BrowserAccessibilityManager> manager(
    874       BrowserAccessibilityManager::Create(
    875           MakeAXTreeUpdate(
    876               root, div, static_text1, img,
    877               static_text2, inline_text1, inline_text2),
    878           NULL,
    879           new CountedBrowserAccessibilityFactory()));
    880   BrowserAccessibility* root_accessible = manager->GetRoot();
    881 
    882   EXPECT_EQ(gfx::Rect(100, 100, 20, 20).ToString(),
    883             root_accessible->GetLocalBoundsForRange(0, 1).ToString());
    884 
    885   EXPECT_EQ(gfx::Rect(100, 100, 40, 20).ToString(),
    886             root_accessible->GetLocalBoundsForRange(0, 2).ToString());
    887 
    888   EXPECT_EQ(gfx::Rect(100, 100, 80, 20).ToString(),
    889             root_accessible->GetLocalBoundsForRange(0, 3).ToString());
    890 
    891   EXPECT_EQ(gfx::Rect(120, 100, 60, 20).ToString(),
    892             root_accessible->GetLocalBoundsForRange(1, 2).ToString());
    893 
    894   EXPECT_EQ(gfx::Rect(120, 100, 80, 20).ToString(),
    895             root_accessible->GetLocalBoundsForRange(1, 3).ToString());
    896 
    897   EXPECT_EQ(gfx::Rect(100, 100, 100, 20).ToString(),
    898             root_accessible->GetLocalBoundsForRange(0, 4).ToString());
    899 }
    900 
    901 TEST(BrowserAccessibilityManagerTest, NextPreviousInTreeOrder) {
    902   ui::AXNodeData root;
    903   root.id = 1;
    904   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
    905 
    906   ui::AXNodeData node2;
    907   node2.id = 2;
    908   root.child_ids.push_back(2);
    909 
    910   ui::AXNodeData node3;
    911   node3.id = 3;
    912   root.child_ids.push_back(3);
    913 
    914   ui::AXNodeData node4;
    915   node4.id = 4;
    916   node3.child_ids.push_back(4);
    917 
    918   ui::AXNodeData node5;
    919   node5.id = 5;
    920   root.child_ids.push_back(5);
    921 
    922   scoped_ptr<BrowserAccessibilityManager> manager(
    923       BrowserAccessibilityManager::Create(
    924           MakeAXTreeUpdate(root, node2, node3, node4, node5),
    925           NULL,
    926           new CountedBrowserAccessibilityFactory()));
    927 
    928   BrowserAccessibility* root_accessible = manager->GetRoot();
    929   BrowserAccessibility* node2_accessible = root_accessible->PlatformGetChild(0);
    930   BrowserAccessibility* node3_accessible = root_accessible->PlatformGetChild(1);
    931   BrowserAccessibility* node4_accessible =
    932       node3_accessible->PlatformGetChild(0);
    933   BrowserAccessibility* node5_accessible = root_accessible->PlatformGetChild(2);
    934 
    935   ASSERT_EQ(NULL, manager->NextInTreeOrder(NULL));
    936   ASSERT_EQ(node2_accessible, manager->NextInTreeOrder(root_accessible));
    937   ASSERT_EQ(node3_accessible, manager->NextInTreeOrder(node2_accessible));
    938   ASSERT_EQ(node4_accessible, manager->NextInTreeOrder(node3_accessible));
    939   ASSERT_EQ(node5_accessible, manager->NextInTreeOrder(node4_accessible));
    940   ASSERT_EQ(NULL, manager->NextInTreeOrder(node5_accessible));
    941 
    942   ASSERT_EQ(NULL, manager->PreviousInTreeOrder(NULL));
    943   ASSERT_EQ(node4_accessible, manager->PreviousInTreeOrder(node5_accessible));
    944   ASSERT_EQ(node3_accessible, manager->PreviousInTreeOrder(node4_accessible));
    945   ASSERT_EQ(node2_accessible, manager->PreviousInTreeOrder(node3_accessible));
    946   ASSERT_EQ(root_accessible, manager->PreviousInTreeOrder(node2_accessible));
    947 }
    948 
    949 }  // namespace content
    950