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/memory/scoped_ptr.h"
      6 #include "base/win/scoped_bstr.h"
      7 #include "base/win/scoped_comptr.h"
      8 #include "base/win/scoped_variant.h"
      9 #include "content/browser/accessibility/browser_accessibility_manager.h"
     10 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
     11 #include "content/browser/accessibility/browser_accessibility_win.h"
     12 #include "content/common/accessibility_messages.h"
     13 #include "testing/gtest/include/gtest/gtest.h"
     14 #include "ui/base/win/atl_module.h"
     15 
     16 namespace content {
     17 namespace {
     18 
     19 
     20 // CountedBrowserAccessibility ------------------------------------------------
     21 
     22 // Subclass of BrowserAccessibilityWin that counts the number of instances.
     23 class CountedBrowserAccessibility : public BrowserAccessibilityWin {
     24  public:
     25   CountedBrowserAccessibility();
     26   virtual ~CountedBrowserAccessibility();
     27 
     28   static void reset() { num_instances_ = 0; }
     29   static int num_instances() { return num_instances_; }
     30 
     31  private:
     32   static int num_instances_;
     33 
     34   DISALLOW_COPY_AND_ASSIGN(CountedBrowserAccessibility);
     35 };
     36 
     37 // static
     38 int CountedBrowserAccessibility::num_instances_ = 0;
     39 
     40 CountedBrowserAccessibility::CountedBrowserAccessibility() {
     41   ++num_instances_;
     42 }
     43 
     44 CountedBrowserAccessibility::~CountedBrowserAccessibility() {
     45   --num_instances_;
     46 }
     47 
     48 
     49 // CountedBrowserAccessibilityFactory -----------------------------------------
     50 
     51 // Factory that creates a CountedBrowserAccessibility.
     52 class CountedBrowserAccessibilityFactory : public BrowserAccessibilityFactory {
     53  public:
     54   CountedBrowserAccessibilityFactory();
     55 
     56  private:
     57   virtual ~CountedBrowserAccessibilityFactory();
     58 
     59   virtual BrowserAccessibility* Create() OVERRIDE;
     60 
     61   DISALLOW_COPY_AND_ASSIGN(CountedBrowserAccessibilityFactory);
     62 };
     63 
     64 CountedBrowserAccessibilityFactory::CountedBrowserAccessibilityFactory() {
     65 }
     66 
     67 CountedBrowserAccessibilityFactory::~CountedBrowserAccessibilityFactory() {
     68 }
     69 
     70 BrowserAccessibility* CountedBrowserAccessibilityFactory::Create() {
     71   CComObject<CountedBrowserAccessibility>* instance;
     72   HRESULT hr = CComObject<CountedBrowserAccessibility>::CreateInstance(
     73       &instance);
     74   DCHECK(SUCCEEDED(hr));
     75   instance->AddRef();
     76   return instance;
     77 }
     78 
     79 }  // namespace
     80 
     81 
     82 // BrowserAccessibilityTest ---------------------------------------------------
     83 
     84 class BrowserAccessibilityTest : public testing::Test {
     85  public:
     86   BrowserAccessibilityTest();
     87   virtual ~BrowserAccessibilityTest();
     88 
     89  private:
     90   virtual void SetUp() OVERRIDE;
     91 
     92   DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityTest);
     93 };
     94 
     95 BrowserAccessibilityTest::BrowserAccessibilityTest() {
     96 }
     97 
     98 BrowserAccessibilityTest::~BrowserAccessibilityTest() {
     99 }
    100 
    101 void BrowserAccessibilityTest::SetUp() {
    102   ui::win::CreateATLModuleIfNeeded();
    103 }
    104 
    105 
    106 // Actual tests ---------------------------------------------------------------
    107 
    108 // Test that BrowserAccessibilityManager correctly releases the tree of
    109 // BrowserAccessibility instances upon delete.
    110 TEST_F(BrowserAccessibilityTest, TestNoLeaks) {
    111   // Create AccessibilityNodeData objects for a simple document tree,
    112   // representing the accessibility information used to initialize
    113   // BrowserAccessibilityManager.
    114   AccessibilityNodeData button;
    115   button.id = 2;
    116   button.name = L"Button";
    117   button.role = AccessibilityNodeData::ROLE_BUTTON;
    118   button.state = 0;
    119 
    120   AccessibilityNodeData checkbox;
    121   checkbox.id = 3;
    122   checkbox.name = L"Checkbox";
    123   checkbox.role = AccessibilityNodeData::ROLE_CHECKBOX;
    124   checkbox.state = 0;
    125 
    126   AccessibilityNodeData root;
    127   root.id = 1;
    128   root.name = L"Document";
    129   root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
    130   root.state = 0;
    131   root.child_ids.push_back(2);
    132   root.child_ids.push_back(3);
    133 
    134   // Construct a BrowserAccessibilityManager with this
    135   // AccessibilityNodeData tree and a factory for an instance-counting
    136   // BrowserAccessibility, and ensure that exactly 3 instances were
    137   // created. Note that the manager takes ownership of the factory.
    138   CountedBrowserAccessibility::reset();
    139   scoped_ptr<BrowserAccessibilityManager> manager(
    140       BrowserAccessibilityManager::Create(
    141           root, NULL, new CountedBrowserAccessibilityFactory()));
    142   manager->UpdateNodesForTesting(button, checkbox);
    143   ASSERT_EQ(3, CountedBrowserAccessibility::num_instances());
    144 
    145   // Delete the manager and test that all 3 instances are deleted.
    146   manager.reset();
    147   ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
    148 
    149   // Construct a manager again, and this time use the IAccessible interface
    150   // to get new references to two of the three nodes in the tree.
    151   manager.reset(BrowserAccessibilityManager::Create(
    152       root, NULL, new CountedBrowserAccessibilityFactory()));
    153   manager->UpdateNodesForTesting(button, checkbox);
    154   ASSERT_EQ(3, CountedBrowserAccessibility::num_instances());
    155   IAccessible* root_accessible =
    156       manager->GetRoot()->ToBrowserAccessibilityWin();
    157   IDispatch* root_iaccessible = NULL;
    158   IDispatch* child1_iaccessible = NULL;
    159   base::win::ScopedVariant childid_self(CHILDID_SELF);
    160   HRESULT hr = root_accessible->get_accChild(childid_self, &root_iaccessible);
    161   ASSERT_EQ(S_OK, hr);
    162   base::win::ScopedVariant one(1);
    163   hr = root_accessible->get_accChild(one, &child1_iaccessible);
    164   ASSERT_EQ(S_OK, hr);
    165 
    166   // Now delete the manager, and only one of the three nodes in the tree
    167   // should be released.
    168   manager.reset();
    169   ASSERT_EQ(2, CountedBrowserAccessibility::num_instances());
    170 
    171   // Release each of our references and make sure that each one results in
    172   // the instance being deleted as its reference count hits zero.
    173   root_iaccessible->Release();
    174   ASSERT_EQ(1, CountedBrowserAccessibility::num_instances());
    175   child1_iaccessible->Release();
    176   ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
    177 }
    178 
    179 TEST_F(BrowserAccessibilityTest, TestChildrenChange) {
    180   // Create AccessibilityNodeData objects for a simple document tree,
    181   // representing the accessibility information used to initialize
    182   // BrowserAccessibilityManager.
    183   AccessibilityNodeData text;
    184   text.id = 2;
    185   text.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
    186   text.name = L"old text";
    187   text.state = 0;
    188 
    189   AccessibilityNodeData root;
    190   root.id = 1;
    191   root.name = L"Document";
    192   root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
    193   root.state = 0;
    194   root.child_ids.push_back(2);
    195 
    196   // Construct a BrowserAccessibilityManager with this
    197   // AccessibilityNodeData tree and a factory for an instance-counting
    198   // BrowserAccessibility.
    199   CountedBrowserAccessibility::reset();
    200   scoped_ptr<BrowserAccessibilityManager> manager(
    201       BrowserAccessibilityManager::Create(
    202           root, NULL, new CountedBrowserAccessibilityFactory()));
    203   manager->UpdateNodesForTesting(text);
    204 
    205   // Query for the text IAccessible and verify that it returns "old text" as its
    206   // value.
    207   base::win::ScopedVariant one(1);
    208   base::win::ScopedComPtr<IDispatch> text_dispatch;
    209   HRESULT hr = manager->GetRoot()->ToBrowserAccessibilityWin()->get_accChild(
    210       one, text_dispatch.Receive());
    211   ASSERT_EQ(S_OK, hr);
    212 
    213   base::win::ScopedComPtr<IAccessible> text_accessible;
    214   hr = text_dispatch.QueryInterface(text_accessible.Receive());
    215   ASSERT_EQ(S_OK, hr);
    216 
    217   base::win::ScopedVariant childid_self(CHILDID_SELF);
    218   base::win::ScopedBstr name;
    219   hr = text_accessible->get_accName(childid_self, name.Receive());
    220   ASSERT_EQ(S_OK, hr);
    221   EXPECT_EQ(L"old text", string16(name));
    222   name.Reset();
    223 
    224   text_dispatch.Release();
    225   text_accessible.Release();
    226 
    227   // Notify the BrowserAccessibilityManager that the text child has changed.
    228   text.name = L"new text";
    229   AccessibilityHostMsg_NotificationParams param;
    230   param.notification_type = AccessibilityNotificationChildrenChanged;
    231   param.nodes.push_back(text);
    232   param.id = text.id;
    233   std::vector<AccessibilityHostMsg_NotificationParams> notifications;
    234   notifications.push_back(param);
    235   manager->OnAccessibilityNotifications(notifications);
    236 
    237   // Query for the text IAccessible and verify that it now returns "new text"
    238   // as its value.
    239   hr = manager->GetRoot()->ToBrowserAccessibilityWin()->get_accChild(
    240       one, text_dispatch.Receive());
    241   ASSERT_EQ(S_OK, hr);
    242 
    243   hr = text_dispatch.QueryInterface(text_accessible.Receive());
    244   ASSERT_EQ(S_OK, hr);
    245 
    246   hr = text_accessible->get_accName(childid_self, name.Receive());
    247   ASSERT_EQ(S_OK, hr);
    248   EXPECT_EQ(L"new text", string16(name));
    249 
    250   text_dispatch.Release();
    251   text_accessible.Release();
    252 
    253   // Delete the manager and test that all BrowserAccessibility instances are
    254   // deleted.
    255   manager.reset();
    256   ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
    257 }
    258 
    259 TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) {
    260   // Create AccessibilityNodeData objects for a simple document tree,
    261   // representing the accessibility information used to initialize
    262   // BrowserAccessibilityManager.
    263   AccessibilityNodeData div;
    264   div.id = 2;
    265   div.role = AccessibilityNodeData::ROLE_GROUP;
    266   div.state = 0;
    267 
    268   AccessibilityNodeData text3;
    269   text3.id = 3;
    270   text3.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
    271   text3.state = 0;
    272 
    273   AccessibilityNodeData text4;
    274   text4.id = 4;
    275   text4.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
    276   text4.state = 0;
    277 
    278   div.child_ids.push_back(3);
    279   div.child_ids.push_back(4);
    280 
    281   AccessibilityNodeData root;
    282   root.id = 1;
    283   root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
    284   root.state = 0;
    285   root.child_ids.push_back(2);
    286 
    287   // Construct a BrowserAccessibilityManager with this
    288   // AccessibilityNodeData tree and a factory for an instance-counting
    289   // BrowserAccessibility and ensure that exactly 4 instances were
    290   // created. Note that the manager takes ownership of the factory.
    291   CountedBrowserAccessibility::reset();
    292   scoped_ptr<BrowserAccessibilityManager> manager(
    293       BrowserAccessibilityManager::Create(
    294           root, NULL, new CountedBrowserAccessibilityFactory()));
    295   manager->UpdateNodesForTesting(div, text3, text4);
    296   ASSERT_EQ(4, CountedBrowserAccessibility::num_instances());
    297 
    298   // Notify the BrowserAccessibilityManager that the div node and its children
    299   // were removed and ensure that only one BrowserAccessibility instance exists.
    300   root.child_ids.clear();
    301   AccessibilityHostMsg_NotificationParams param;
    302   param.notification_type = AccessibilityNotificationChildrenChanged;
    303   param.nodes.push_back(root);
    304   param.id = root.id;
    305   std::vector<AccessibilityHostMsg_NotificationParams> notifications;
    306   notifications.push_back(param);
    307   manager->OnAccessibilityNotifications(notifications);
    308   ASSERT_EQ(1, CountedBrowserAccessibility::num_instances());
    309 
    310   // Delete the manager and test that all BrowserAccessibility instances are
    311   // deleted.
    312   manager.reset();
    313   ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
    314 }
    315 
    316 TEST_F(BrowserAccessibilityTest, TestTextBoundaries) {
    317   AccessibilityNodeData text1;
    318   text1.id = 11;
    319   text1.role = AccessibilityNodeData::ROLE_TEXT_FIELD;
    320   text1.state = 0;
    321   text1.value = L"One two three.\nFour five six.";
    322   text1.line_breaks.push_back(15);
    323 
    324   AccessibilityNodeData root;
    325   root.id = 1;
    326   root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
    327   root.state = 0;
    328   root.child_ids.push_back(11);
    329 
    330   CountedBrowserAccessibility::reset();
    331   scoped_ptr<BrowserAccessibilityManager> manager(
    332       BrowserAccessibilityManager::Create(
    333           root, NULL, new CountedBrowserAccessibilityFactory()));
    334   manager->UpdateNodesForTesting(text1);
    335   ASSERT_EQ(2, CountedBrowserAccessibility::num_instances());
    336 
    337   BrowserAccessibilityWin* root_obj =
    338       manager->GetRoot()->ToBrowserAccessibilityWin();
    339   BrowserAccessibilityWin* text1_obj =
    340       root_obj->GetChild(0)->ToBrowserAccessibilityWin();
    341 
    342   long text1_len;
    343   ASSERT_EQ(S_OK, text1_obj->get_nCharacters(&text1_len));
    344 
    345   base::win::ScopedBstr text;
    346   ASSERT_EQ(S_OK, text1_obj->get_text(0, text1_len, text.Receive()));
    347   ASSERT_EQ(text1.value, string16(text));
    348   text.Reset();
    349 
    350   ASSERT_EQ(S_OK, text1_obj->get_text(0, 4, text.Receive()));
    351   ASSERT_STREQ(L"One ", text);
    352   text.Reset();
    353 
    354   long start;
    355   long end;
    356   ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
    357       1, IA2_TEXT_BOUNDARY_CHAR, &start, &end, text.Receive()));
    358   ASSERT_EQ(1, start);
    359   ASSERT_EQ(2, end);
    360   ASSERT_STREQ(L"n", text);
    361   text.Reset();
    362 
    363   ASSERT_EQ(S_FALSE, text1_obj->get_textAtOffset(
    364       text1_len, IA2_TEXT_BOUNDARY_CHAR, &start, &end, text.Receive()));
    365   ASSERT_EQ(text1_len, start);
    366   ASSERT_EQ(text1_len, end);
    367   text.Reset();
    368 
    369   ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
    370       1, IA2_TEXT_BOUNDARY_WORD, &start, &end, text.Receive()));
    371   ASSERT_EQ(0, start);
    372   ASSERT_EQ(3, end);
    373   ASSERT_STREQ(L"One", text);
    374   text.Reset();
    375 
    376   ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
    377       6, IA2_TEXT_BOUNDARY_WORD, &start, &end, text.Receive()));
    378   ASSERT_EQ(4, start);
    379   ASSERT_EQ(7, end);
    380   ASSERT_STREQ(L"two", text);
    381   text.Reset();
    382 
    383   ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
    384       text1_len, IA2_TEXT_BOUNDARY_WORD, &start, &end, text.Receive()));
    385   ASSERT_EQ(25, start);
    386   ASSERT_EQ(29, end);
    387   ASSERT_STREQ(L"six.", text);
    388   text.Reset();
    389 
    390   ASSERT_EQ(S_OK, text1_obj->get_textAtOffset(
    391       1, IA2_TEXT_BOUNDARY_LINE, &start, &end, text.Receive()));
    392   ASSERT_EQ(0, start);
    393   ASSERT_EQ(15, end);
    394   ASSERT_STREQ(L"One two three.\n", text);
    395   text.Reset();
    396 
    397   ASSERT_EQ(S_OK,
    398             text1_obj->get_text(0, IA2_TEXT_OFFSET_LENGTH, text.Receive()));
    399   ASSERT_STREQ(L"One two three.\nFour five six.", text);
    400 
    401   // Delete the manager and test that all BrowserAccessibility instances are
    402   // deleted.
    403   manager.reset();
    404   ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
    405 }
    406 
    407 TEST_F(BrowserAccessibilityTest, TestSimpleHypertext) {
    408   AccessibilityNodeData text1;
    409   text1.id = 11;
    410   text1.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
    411   text1.state = 1 << AccessibilityNodeData::STATE_READONLY;
    412   text1.name = L"One two three.";
    413 
    414   AccessibilityNodeData text2;
    415   text2.id = 12;
    416   text2.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
    417   text2.state = 1 << AccessibilityNodeData::STATE_READONLY;
    418   text2.name = L" Four five six.";
    419 
    420   AccessibilityNodeData root;
    421   root.id = 1;
    422   root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
    423   root.state = 1 << AccessibilityNodeData::STATE_READONLY;
    424   root.child_ids.push_back(11);
    425   root.child_ids.push_back(12);
    426 
    427   CountedBrowserAccessibility::reset();
    428   scoped_ptr<BrowserAccessibilityManager> manager(
    429       BrowserAccessibilityManager::Create(
    430           root, NULL, new CountedBrowserAccessibilityFactory()));
    431   manager->UpdateNodesForTesting(root, text1, text2);
    432   ASSERT_EQ(3, CountedBrowserAccessibility::num_instances());
    433 
    434   BrowserAccessibilityWin* root_obj =
    435       manager->GetRoot()->ToBrowserAccessibilityWin();
    436 
    437   long text_len;
    438   ASSERT_EQ(S_OK, root_obj->get_nCharacters(&text_len));
    439 
    440   base::win::ScopedBstr text;
    441   ASSERT_EQ(S_OK, root_obj->get_text(0, text_len, text.Receive()));
    442   EXPECT_EQ(text1.name + text2.name, string16(text));
    443 
    444   long hyperlink_count;
    445   ASSERT_EQ(S_OK, root_obj->get_nHyperlinks(&hyperlink_count));
    446   EXPECT_EQ(0, hyperlink_count);
    447 
    448   base::win::ScopedComPtr<IAccessibleHyperlink> hyperlink;
    449   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(-1, hyperlink.Receive()));
    450   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(0, hyperlink.Receive()));
    451   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(28, hyperlink.Receive()));
    452   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(29, hyperlink.Receive()));
    453 
    454   long hyperlink_index;
    455   EXPECT_EQ(E_FAIL, root_obj->get_hyperlinkIndex(0, &hyperlink_index));
    456   EXPECT_EQ(-1, hyperlink_index);
    457   EXPECT_EQ(E_FAIL, root_obj->get_hyperlinkIndex(28, &hyperlink_index));
    458   EXPECT_EQ(-1, hyperlink_index);
    459   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlinkIndex(-1, &hyperlink_index));
    460   EXPECT_EQ(-1, hyperlink_index);
    461   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlinkIndex(29, &hyperlink_index));
    462   EXPECT_EQ(-1, hyperlink_index);
    463 
    464   // Delete the manager and test that all BrowserAccessibility instances are
    465   // deleted.
    466   manager.reset();
    467   ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
    468 }
    469 
    470 TEST_F(BrowserAccessibilityTest, TestComplexHypertext) {
    471   AccessibilityNodeData text1;
    472   text1.id = 11;
    473   text1.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
    474   text1.state = 1 << AccessibilityNodeData::STATE_READONLY;
    475   text1.name = L"One two three.";
    476 
    477   AccessibilityNodeData text2;
    478   text2.id = 12;
    479   text2.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
    480   text2.state = 1 << AccessibilityNodeData::STATE_READONLY;
    481   text2.name = L" Four five six.";
    482 
    483   AccessibilityNodeData button1, button1_text;
    484   button1.id = 13;
    485   button1_text.id = 15;
    486   button1_text.name = L"red";
    487   button1.role = AccessibilityNodeData::ROLE_BUTTON;
    488   button1_text.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
    489   button1.state = 1 << AccessibilityNodeData::STATE_READONLY;
    490   button1_text.state = 1 << AccessibilityNodeData::STATE_READONLY;
    491   button1.child_ids.push_back(15);
    492 
    493   AccessibilityNodeData link1, link1_text;
    494   link1.id = 14;
    495   link1_text.id = 16;
    496   link1_text.name = L"blue";
    497   link1.role = AccessibilityNodeData::ROLE_LINK;
    498   link1_text.role = AccessibilityNodeData::ROLE_STATIC_TEXT;
    499   link1.state = 1 << AccessibilityNodeData::STATE_READONLY;
    500   link1_text.state = 1 << AccessibilityNodeData::STATE_READONLY;
    501   link1.child_ids.push_back(16);
    502 
    503   AccessibilityNodeData root;
    504   root.id = 1;
    505   root.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
    506   root.state = 1 << AccessibilityNodeData::STATE_READONLY;
    507   root.child_ids.push_back(11);
    508   root.child_ids.push_back(13);
    509   root.child_ids.push_back(12);
    510   root.child_ids.push_back(14);
    511 
    512   CountedBrowserAccessibility::reset();
    513   scoped_ptr<BrowserAccessibilityManager> manager(
    514       BrowserAccessibilityManager::Create(
    515           root, NULL, new CountedBrowserAccessibilityFactory()));
    516   manager->UpdateNodesForTesting(root,
    517                                  text1, button1, button1_text,
    518                                  text2, link1, link1_text);
    519 
    520   ASSERT_EQ(7, CountedBrowserAccessibility::num_instances());
    521 
    522   BrowserAccessibilityWin* root_obj =
    523       manager->GetRoot()->ToBrowserAccessibilityWin();
    524 
    525   long text_len;
    526   ASSERT_EQ(S_OK, root_obj->get_nCharacters(&text_len));
    527 
    528   base::win::ScopedBstr text;
    529   ASSERT_EQ(S_OK, root_obj->get_text(0, text_len, text.Receive()));
    530   const string16 embed = BrowserAccessibilityWin::kEmbeddedCharacter;
    531   EXPECT_EQ(text1.name + embed + text2.name + embed, string16(text));
    532   text.Reset();
    533 
    534   long hyperlink_count;
    535   ASSERT_EQ(S_OK, root_obj->get_nHyperlinks(&hyperlink_count));
    536   EXPECT_EQ(2, hyperlink_count);
    537 
    538   base::win::ScopedComPtr<IAccessibleHyperlink> hyperlink;
    539   base::win::ScopedComPtr<IAccessibleText> hypertext;
    540   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(-1, hyperlink.Receive()));
    541   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(2, hyperlink.Receive()));
    542   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(28, hyperlink.Receive()));
    543 
    544   EXPECT_EQ(S_OK, root_obj->get_hyperlink(0, hyperlink.Receive()));
    545   EXPECT_EQ(S_OK,
    546             hyperlink.QueryInterface<IAccessibleText>(hypertext.Receive()));
    547   EXPECT_EQ(S_OK, hypertext->get_text(0, 3, text.Receive()));
    548   EXPECT_STREQ(L"red", text);
    549   text.Reset();
    550   hyperlink.Release();
    551   hypertext.Release();
    552 
    553   EXPECT_EQ(S_OK, root_obj->get_hyperlink(1, hyperlink.Receive()));
    554   EXPECT_EQ(S_OK,
    555             hyperlink.QueryInterface<IAccessibleText>(hypertext.Receive()));
    556   EXPECT_EQ(S_OK, hypertext->get_text(0, 4, text.Receive()));
    557   EXPECT_STREQ(L"blue", text);
    558   text.Reset();
    559   hyperlink.Release();
    560   hypertext.Release();
    561 
    562   long hyperlink_index;
    563   EXPECT_EQ(E_FAIL, root_obj->get_hyperlinkIndex(0, &hyperlink_index));
    564   EXPECT_EQ(-1, hyperlink_index);
    565   EXPECT_EQ(E_FAIL, root_obj->get_hyperlinkIndex(28, &hyperlink_index));
    566   EXPECT_EQ(-1, hyperlink_index);
    567   EXPECT_EQ(S_OK, root_obj->get_hyperlinkIndex(14, &hyperlink_index));
    568   EXPECT_EQ(0, hyperlink_index);
    569   EXPECT_EQ(S_OK, root_obj->get_hyperlinkIndex(30, &hyperlink_index));
    570   EXPECT_EQ(1, hyperlink_index);
    571 
    572   // Delete the manager and test that all BrowserAccessibility instances are
    573   // deleted.
    574   manager.reset();
    575   ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
    576 }
    577 
    578 TEST_F(BrowserAccessibilityTest, TestCreateEmptyDocument) {
    579   // Try creating an empty document with busy state. Readonly is
    580   // set automatically.
    581   CountedBrowserAccessibility::reset();
    582   const int32 busy_state = 1 << AccessibilityNodeData::STATE_BUSY;
    583   const int32 readonly_state = 1 << AccessibilityNodeData::STATE_READONLY;
    584   scoped_ptr<BrowserAccessibilityManager> manager(
    585       new BrowserAccessibilityManagerWin(
    586           GetDesktopWindow(),
    587           NULL,
    588           BrowserAccessibilityManagerWin::GetEmptyDocument(),
    589           NULL,
    590           new CountedBrowserAccessibilityFactory()));
    591 
    592   // Verify the root is as we expect by default.
    593   BrowserAccessibility* root = manager->GetRoot();
    594   EXPECT_EQ(0, root->renderer_id());
    595   EXPECT_EQ(AccessibilityNodeData::ROLE_ROOT_WEB_AREA, root->role());
    596   EXPECT_EQ(busy_state | readonly_state, root->state());
    597 
    598   // Tree with a child textfield.
    599   AccessibilityNodeData tree1_1;
    600   tree1_1.id = 1;
    601   tree1_1.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
    602   tree1_1.child_ids.push_back(2);
    603 
    604   AccessibilityNodeData tree1_2;
    605   tree1_2.id = 2;
    606   tree1_2.role = AccessibilityNodeData::ROLE_TEXT_FIELD;
    607 
    608   // Process a load complete.
    609   std::vector<AccessibilityHostMsg_NotificationParams> params;
    610   params.push_back(AccessibilityHostMsg_NotificationParams());
    611   AccessibilityHostMsg_NotificationParams* msg = &params[0];
    612   msg->notification_type = AccessibilityNotificationLoadComplete;
    613   msg->nodes.push_back(tree1_1);
    614   msg->nodes.push_back(tree1_2);
    615   msg->id = tree1_1.id;
    616   manager->OnAccessibilityNotifications(params);
    617 
    618   // Save for later comparison.
    619   BrowserAccessibility* acc1_2 = manager->GetFromRendererID(2);
    620 
    621   // Verify the root has changed.
    622   EXPECT_NE(root, manager->GetRoot());
    623 
    624   // And the proper child remains.
    625   EXPECT_EQ(AccessibilityNodeData::ROLE_TEXT_FIELD, acc1_2->role());
    626   EXPECT_EQ(2, acc1_2->renderer_id());
    627 
    628   // Tree with a child button.
    629   AccessibilityNodeData tree2_1;
    630   tree2_1.id = 1;
    631   tree2_1.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
    632   tree2_1.child_ids.push_back(3);
    633 
    634   AccessibilityNodeData tree2_2;
    635   tree2_2.id = 3;
    636   tree2_2.role = AccessibilityNodeData::ROLE_BUTTON;
    637 
    638   msg->nodes.clear();
    639   msg->nodes.push_back(tree2_1);
    640   msg->nodes.push_back(tree2_2);
    641   msg->id = tree2_1.id;
    642 
    643   // Fire another load complete.
    644   manager->OnAccessibilityNotifications(params);
    645 
    646   BrowserAccessibility* acc2_2 = manager->GetFromRendererID(3);
    647 
    648   // Verify the root has changed.
    649   EXPECT_NE(root, manager->GetRoot());
    650 
    651   // And the new child exists.
    652   EXPECT_EQ(AccessibilityNodeData::ROLE_BUTTON, acc2_2->role());
    653   EXPECT_EQ(3, acc2_2->renderer_id());
    654 
    655   // Ensure we properly cleaned up.
    656   manager.reset();
    657   ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
    658 }
    659 
    660 }  // namespace content
    661