| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "base/scoped_ptr.h" | |
| 6 #include "base/scoped_comptr_win.h" | |
| 7 #include "chrome/browser/browser_accessibility_manager_win.h" | |
| 8 #include "chrome/browser/browser_accessibility_win.h" | |
| 9 #include "chrome/common/render_messages_params.h" | |
| 10 #include "testing/gtest/include/gtest/gtest.h" | |
| 11 | |
| 12 using webkit_glue::WebAccessibility; | |
| 13 | |
| 14 // Subclass of BrowserAccessibility that counts the number of instances. | |
| 15 class CountedBrowserAccessibility : public BrowserAccessibility { | |
| 16 public: | |
| 17 CountedBrowserAccessibility() { global_obj_count_++; } | |
| 18 virtual ~CountedBrowserAccessibility() { global_obj_count_--; } | |
| 19 static int global_obj_count_; | |
| 20 }; | |
| 21 | |
| 22 int CountedBrowserAccessibility::global_obj_count_ = 0; | |
| 23 | |
| 24 // Factory that creates a CountedBrowserAccessibility. | |
| 25 class CountedBrowserAccessibilityFactory : public BrowserAccessibilityFactory { | |
| 26 public: | |
| 27 virtual ~CountedBrowserAccessibilityFactory() {} | |
| 28 virtual BrowserAccessibility* Create() { | |
| 29 CComObject<CountedBrowserAccessibility>* instance; | |
| 30 HRESULT hr = CComObject<CountedBrowserAccessibility>::CreateInstance( | |
| 31 &instance); | |
| 32 DCHECK(SUCCEEDED(hr)); | |
| 33 return instance->NewReference(); | |
| 34 } | |
| 35 }; | |
| 36 | |
| 37 VARIANT CreateI4Variant(LONG value) { | |
| 38 VARIANT variant = {0}; | |
| 39 | |
| 40 V_VT(&variant) = VT_I4; | |
| 41 V_I4(&variant) = value; | |
| 42 | |
| 43 return variant; | |
| 44 } | |
| 45 | |
| 46 class BrowserAccessibilityTest : public testing::Test { | |
| 47 protected: | |
| 48 virtual void SetUp() { | |
| 49 // ATL needs a pointer to a COM module. | |
| 50 static CComModule module; | |
| 51 _pAtlModule = &module; | |
| 52 | |
| 53 // Make sure COM is initialized for this thread; it's safe to call twice. | |
| 54 ::CoInitialize(NULL); | |
| 55 } | |
| 56 | |
| 57 virtual void TearDown() | |
| 58 { | |
| 59 ::CoUninitialize(); | |
| 60 } | |
| 61 }; | |
| 62 | |
| 63 // Test that BrowserAccessibilityManager correctly releases the tree of | |
| 64 // BrowserAccessibility instances upon delete. | |
| 65 TEST_F(BrowserAccessibilityTest, TestNoLeaks) { | |
| 66 // Create WebAccessibility objects for a simple document tree, | |
| 67 // representing the accessibility information used to initialize | |
| 68 // BrowserAccessibilityManager. | |
| 69 WebAccessibility button; | |
| 70 button.id = 2; | |
| 71 button.name = L"Button"; | |
| 72 button.role = WebAccessibility::ROLE_BUTTON; | |
| 73 button.state = 0; | |
| 74 | |
| 75 WebAccessibility checkbox; | |
| 76 checkbox.id = 3; | |
| 77 checkbox.name = L"Checkbox"; | |
| 78 checkbox.role = WebAccessibility::ROLE_CHECKBOX; | |
| 79 checkbox.state = 0; | |
| 80 | |
| 81 WebAccessibility root; | |
| 82 root.id = 1; | |
| 83 root.name = L"Document"; | |
| 84 root.role = WebAccessibility::ROLE_DOCUMENT; | |
| 85 root.state = 0; | |
| 86 root.children.push_back(button); | |
| 87 root.children.push_back(checkbox); | |
| 88 | |
| 89 // Construct a BrowserAccessibilityManager with this WebAccessibility tree | |
| 90 // and a factory for an instance-counting BrowserAccessibility, and ensure | |
| 91 // that exactly 3 instances were created. Note that the manager takes | |
| 92 // ownership of the factory. | |
| 93 CountedBrowserAccessibility::global_obj_count_ = 0; | |
| 94 BrowserAccessibilityManager* manager = | |
| 95 new BrowserAccessibilityManager( | |
| 96 GetDesktopWindow(), root, NULL, | |
| 97 new CountedBrowserAccessibilityFactory()); | |
| 98 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_); | |
| 99 | |
| 100 // Delete the manager and test that all 3 instances are deleted. | |
| 101 delete manager; | |
| 102 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_); | |
| 103 | |
| 104 // Construct a manager again, and this time use the IAccessible interface | |
| 105 // to get new references to two of the three nodes in the tree. | |
| 106 manager = new BrowserAccessibilityManager( | |
| 107 GetDesktopWindow(), root, NULL, | |
| 108 new CountedBrowserAccessibilityFactory()); | |
| 109 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_); | |
| 110 BrowserAccessibility* root_accessible = manager->GetRoot(); | |
| 111 IDispatch* root_iaccessible = NULL; | |
| 112 IDispatch* child1_iaccessible = NULL; | |
| 113 VARIANT var_child; | |
| 114 var_child.vt = VT_I4; | |
| 115 var_child.lVal = CHILDID_SELF; | |
| 116 HRESULT hr = root_accessible->get_accChild(var_child, &root_iaccessible); | |
| 117 ASSERT_EQ(S_OK, hr); | |
| 118 var_child.lVal = 1; | |
| 119 hr = root_accessible->get_accChild(var_child, &child1_iaccessible); | |
| 120 ASSERT_EQ(S_OK, hr); | |
| 121 | |
| 122 // Now delete the manager, and only one of the three nodes in the tree | |
| 123 // should be released. | |
| 124 delete manager; | |
| 125 ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_); | |
| 126 | |
| 127 // Release each of our references and make sure that each one results in | |
| 128 // the instance being deleted as its reference count hits zero. | |
| 129 root_iaccessible->Release(); | |
| 130 ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_); | |
| 131 child1_iaccessible->Release(); | |
| 132 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_); | |
| 133 } | |
| 134 | |
| 135 TEST_F(BrowserAccessibilityTest, TestChildrenChange) { | |
| 136 // Create WebAccessibility objects for a simple document tree, | |
| 137 // representing the accessibility information used to initialize | |
| 138 // BrowserAccessibilityManager. | |
| 139 WebAccessibility text; | |
| 140 text.id = 2; | |
| 141 text.role = WebAccessibility::ROLE_STATIC_TEXT; | |
| 142 text.value = L"old text"; | |
| 143 text.state = 0; | |
| 144 | |
| 145 WebAccessibility root; | |
| 146 root.id = 1; | |
| 147 root.name = L"Document"; | |
| 148 root.role = WebAccessibility::ROLE_DOCUMENT; | |
| 149 root.state = 0; | |
| 150 root.children.push_back(text); | |
| 151 | |
| 152 // Construct a BrowserAccessibilityManager with this WebAccessibility tree | |
| 153 // and a factory for an instance-counting BrowserAccessibility. | |
| 154 CountedBrowserAccessibility::global_obj_count_ = 0; | |
| 155 BrowserAccessibilityManager* manager = | |
| 156 new BrowserAccessibilityManager( | |
| 157 GetDesktopWindow(), root, NULL, | |
| 158 new CountedBrowserAccessibilityFactory()); | |
| 159 | |
| 160 // Query for the text IAccessible and verify that it returns "old text" as its | |
| 161 // value. | |
| 162 ScopedComPtr<IDispatch> text_dispatch; | |
| 163 HRESULT hr = manager->GetRoot()->get_accChild(CreateI4Variant(1), | |
| 164 text_dispatch.Receive()); | |
| 165 ASSERT_EQ(S_OK, hr); | |
| 166 | |
| 167 ScopedComPtr<IAccessible> text_accessible; | |
| 168 hr = text_dispatch.QueryInterface(text_accessible.Receive()); | |
| 169 ASSERT_EQ(S_OK, hr); | |
| 170 | |
| 171 CComBSTR value; | |
| 172 hr = text_accessible->get_accValue(CreateI4Variant(CHILDID_SELF), &value); | |
| 173 ASSERT_EQ(S_OK, hr); | |
| 174 EXPECT_STREQ(L"old text", value.m_str); | |
| 175 | |
| 176 text_dispatch.Release(); | |
| 177 text_accessible.Release(); | |
| 178 | |
| 179 // Notify the BrowserAccessibilityManager that the text child has changed. | |
| 180 text.value = L"new text"; | |
| 181 ViewHostMsg_AccessibilityNotification_Params param; | |
| 182 param.notification_type = | |
| 183 ViewHostMsg_AccessibilityNotification_Params:: | |
| 184 NOTIFICATION_TYPE_CHILDREN_CHANGED; | |
| 185 param.acc_obj = text; | |
| 186 std::vector<ViewHostMsg_AccessibilityNotification_Params> notifications; | |
| 187 notifications.push_back(param); | |
| 188 manager->OnAccessibilityNotifications(notifications); | |
| 189 | |
| 190 // Query for the text IAccessible and verify that it now returns "new text" | |
| 191 // as its value. | |
| 192 hr = manager->GetRoot()->get_accChild( | |
| 193 CreateI4Variant(1), | |
| 194 text_dispatch.Receive()); | |
| 195 ASSERT_EQ(S_OK, hr); | |
| 196 | |
| 197 hr = text_dispatch.QueryInterface(text_accessible.Receive()); | |
| 198 ASSERT_EQ(S_OK, hr); | |
| 199 | |
| 200 hr = text_accessible->get_accValue(CreateI4Variant(CHILDID_SELF), &value); | |
| 201 ASSERT_EQ(S_OK, hr); | |
| 202 EXPECT_STREQ(L"new text", value.m_str); | |
| 203 | |
| 204 text_dispatch.Release(); | |
| 205 text_accessible.Release(); | |
| 206 | |
| 207 // Delete the manager and test that all BrowserAccessibility instances are | |
| 208 // deleted. | |
| 209 delete manager; | |
| 210 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_); | |
| 211 } | |
| 212 | |
| 213 TEST_F(BrowserAccessibilityTest, TestChildrenChangeNoLeaks) { | |
| 214 // Create WebAccessibility objects for a simple document tree, | |
| 215 // representing the accessibility information used to initialize | |
| 216 // BrowserAccessibilityManager. | |
| 217 WebAccessibility text; | |
| 218 text.id = 3; | |
| 219 text.role = WebAccessibility::ROLE_STATIC_TEXT; | |
| 220 text.state = 0; | |
| 221 | |
| 222 WebAccessibility div; | |
| 223 div.id = 2; | |
| 224 div.role = WebAccessibility::ROLE_GROUP; | |
| 225 div.state = 0; | |
| 226 | |
| 227 div.children.push_back(text); | |
| 228 text.id = 4; | |
| 229 div.children.push_back(text); | |
| 230 | |
| 231 WebAccessibility root; | |
| 232 root.id = 1; | |
| 233 root.role = WebAccessibility::ROLE_DOCUMENT; | |
| 234 root.state = 0; | |
| 235 root.children.push_back(div); | |
| 236 | |
| 237 // Construct a BrowserAccessibilityManager with this WebAccessibility tree | |
| 238 // and a factory for an instance-counting BrowserAccessibility and ensure | |
| 239 // that exactly 4 instances were created. Note that the manager takes | |
| 240 // ownership of the factory. | |
| 241 CountedBrowserAccessibility::global_obj_count_ = 0; | |
| 242 BrowserAccessibilityManager* manager = | |
| 243 new BrowserAccessibilityManager( | |
| 244 GetDesktopWindow(), root, NULL, | |
| 245 new CountedBrowserAccessibilityFactory()); | |
| 246 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_); | |
| 247 | |
| 248 // Notify the BrowserAccessibilityManager that the div node and its children | |
| 249 // were removed and ensure that only one BrowserAccessibility instance exists. | |
| 250 root.children.clear(); | |
| 251 ViewHostMsg_AccessibilityNotification_Params param; | |
| 252 param.notification_type = | |
| 253 ViewHostMsg_AccessibilityNotification_Params:: | |
| 254 NOTIFICATION_TYPE_CHILDREN_CHANGED; | |
| 255 param.acc_obj = root; | |
| 256 std::vector<ViewHostMsg_AccessibilityNotification_Params> notifications; | |
| 257 notifications.push_back(param); | |
| 258 manager->OnAccessibilityNotifications(notifications); | |
| 259 ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_); | |
| 260 | |
| 261 // Delete the manager and test that all BrowserAccessibility instances are | |
| 262 // deleted. | |
| 263 delete manager; | |
| 264 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_); | |
| 265 } | |
| OLD | NEW |