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