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