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