Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / browser / accessibility / browser_accessibility_win_unittest.cc
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           root, NULL, new CountedBrowserAccessibilityFactory()));
145   manager->UpdateNodesForTesting(button, checkbox);
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       root, NULL, new CountedBrowserAccessibilityFactory()));
156   manager->UpdateNodesForTesting(button, checkbox);
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           root, NULL, new CountedBrowserAccessibilityFactory()));
206   manager->UpdateNodesForTesting(text);
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.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           root, NULL, new CountedBrowserAccessibilityFactory()));
302   manager->UpdateNodesForTesting(div, text3, text4);
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.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           root, NULL, new CountedBrowserAccessibilityFactory()));
346   manager->UpdateNodesForTesting(text1);
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           root, NULL, new CountedBrowserAccessibilityFactory()));
446   manager->UpdateNodesForTesting(root, text1, text2);
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           root, NULL, new CountedBrowserAccessibilityFactory()));
536   manager->UpdateNodesForTesting(root,
537                                  text1, button1, button1_text,
538                                  text2, link1, link1_text);
539
540   ASSERT_EQ(7, CountedBrowserAccessibility::num_instances());
541
542   BrowserAccessibilityWin* root_obj =
543       manager->GetRoot()->ToBrowserAccessibilityWin();
544
545   long text_len;
546   ASSERT_EQ(S_OK, root_obj->get_nCharacters(&text_len));
547
548   base::win::ScopedBstr text;
549   ASSERT_EQ(S_OK, root_obj->get_text(0, text_len, text.Receive()));
550   const std::string embed = base::UTF16ToUTF8(
551       BrowserAccessibilityWin::kEmbeddedCharacter);
552   EXPECT_EQ(text1_name + embed + text2_name + embed,
553             base::UTF16ToUTF8(base::string16(text)));
554   text.Reset();
555
556   long hyperlink_count;
557   ASSERT_EQ(S_OK, root_obj->get_nHyperlinks(&hyperlink_count));
558   EXPECT_EQ(2, hyperlink_count);
559
560   base::win::ScopedComPtr<IAccessibleHyperlink> hyperlink;
561   base::win::ScopedComPtr<IAccessibleText> hypertext;
562   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(-1, hyperlink.Receive()));
563   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(2, hyperlink.Receive()));
564   EXPECT_EQ(E_INVALIDARG, root_obj->get_hyperlink(28, hyperlink.Receive()));
565
566   EXPECT_EQ(S_OK, root_obj->get_hyperlink(0, hyperlink.Receive()));
567   EXPECT_EQ(S_OK,
568             hyperlink.QueryInterface<IAccessibleText>(hypertext.Receive()));
569   EXPECT_EQ(S_OK, hypertext->get_text(0, 3, text.Receive()));
570   EXPECT_STREQ(button1_text_name.c_str(),
571                base::UTF16ToUTF8(base::string16(text)).c_str());
572   text.Reset();
573   hyperlink.Release();
574   hypertext.Release();
575
576   EXPECT_EQ(S_OK, root_obj->get_hyperlink(1, hyperlink.Receive()));
577   EXPECT_EQ(S_OK,
578             hyperlink.QueryInterface<IAccessibleText>(hypertext.Receive()));
579   EXPECT_EQ(S_OK, hypertext->get_text(0, 4, text.Receive()));
580   EXPECT_STREQ(link1_text_name.c_str(),
581                base::UTF16ToUTF8(base::string16(text)).c_str());
582   text.Reset();
583   hyperlink.Release();
584   hypertext.Release();
585
586   long hyperlink_index;
587   EXPECT_EQ(E_FAIL, root_obj->get_hyperlinkIndex(0, &hyperlink_index));
588   EXPECT_EQ(-1, hyperlink_index);
589   EXPECT_EQ(E_FAIL, root_obj->get_hyperlinkIndex(28, &hyperlink_index));
590   EXPECT_EQ(-1, hyperlink_index);
591   EXPECT_EQ(S_OK, root_obj->get_hyperlinkIndex(14, &hyperlink_index));
592   EXPECT_EQ(0, hyperlink_index);
593   EXPECT_EQ(S_OK, root_obj->get_hyperlinkIndex(30, &hyperlink_index));
594   EXPECT_EQ(1, hyperlink_index);
595
596   // Delete the manager and test that all BrowserAccessibility instances are
597   // deleted.
598   manager.reset();
599   ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
600 }
601
602 TEST_F(BrowserAccessibilityTest, TestCreateEmptyDocument) {
603   // Try creating an empty document with busy state. Readonly is
604   // set automatically.
605   CountedBrowserAccessibility::reset();
606   const int32 busy_state = 1 << ui::AX_STATE_BUSY;
607   const int32 readonly_state = 1 << ui::AX_STATE_READ_ONLY;
608   const int32 enabled_state = 1 << blink::WebAXStateEnabled;
609   scoped_ptr<content::LegacyRenderWidgetHostHWND> accessible_hwnd(
610       content::LegacyRenderWidgetHostHWND::Create(GetDesktopWindow()));
611   scoped_ptr<BrowserAccessibilityManager> manager(
612       new BrowserAccessibilityManagerWin(
613           accessible_hwnd.get(),
614           NULL,
615           BrowserAccessibilityManagerWin::GetEmptyDocument(),
616           NULL,
617           new CountedBrowserAccessibilityFactory()));
618
619   // Verify the root is as we expect by default.
620   BrowserAccessibility* root = manager->GetRoot();
621   EXPECT_EQ(0, root->renderer_id());
622   EXPECT_EQ(ui::AX_ROLE_ROOT_WEB_AREA, root->role());
623   EXPECT_EQ(busy_state | readonly_state | enabled_state, root->state());
624
625   // Tree with a child textfield.
626   ui::AXNodeData tree1_1;
627   tree1_1.id = 1;
628   tree1_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
629   tree1_1.child_ids.push_back(2);
630
631   ui::AXNodeData tree1_2;
632   tree1_2.id = 2;
633   tree1_2.role = ui::AX_ROLE_TEXT_FIELD;
634
635   // Process a load complete.
636   std::vector<AccessibilityHostMsg_EventParams> params;
637   params.push_back(AccessibilityHostMsg_EventParams());
638   AccessibilityHostMsg_EventParams* msg = &params[0];
639   msg->event_type = ui::AX_EVENT_LOAD_COMPLETE;
640   msg->nodes.push_back(tree1_1);
641   msg->nodes.push_back(tree1_2);
642   msg->id = tree1_1.id;
643   manager->OnAccessibilityEvents(params);
644
645   // Save for later comparison.
646   BrowserAccessibility* acc1_2 = manager->GetFromRendererID(2);
647
648   // Verify the root has changed.
649   EXPECT_NE(root, manager->GetRoot());
650
651   // And the proper child remains.
652   EXPECT_EQ(ui::AX_ROLE_TEXT_FIELD, acc1_2->role());
653   EXPECT_EQ(2, acc1_2->renderer_id());
654
655   // Tree with a child button.
656   ui::AXNodeData tree2_1;
657   tree2_1.id = 1;
658   tree2_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
659   tree2_1.child_ids.push_back(3);
660
661   ui::AXNodeData tree2_2;
662   tree2_2.id = 3;
663   tree2_2.role = ui::AX_ROLE_BUTTON;
664
665   msg->nodes.clear();
666   msg->nodes.push_back(tree2_1);
667   msg->nodes.push_back(tree2_2);
668   msg->id = tree2_1.id;
669
670   // Fire another load complete.
671   manager->OnAccessibilityEvents(params);
672
673   BrowserAccessibility* acc2_2 = manager->GetFromRendererID(3);
674
675   // Verify the root has changed.
676   EXPECT_NE(root, manager->GetRoot());
677
678   // And the new child exists.
679   EXPECT_EQ(ui::AX_ROLE_BUTTON, acc2_2->role());
680   EXPECT_EQ(3, acc2_2->renderer_id());
681
682   // Ensure we properly cleaned up.
683   manager.reset();
684   ASSERT_EQ(0, CountedBrowserAccessibility::num_instances());
685 }
686
687 TEST(BrowserAccessibilityManagerWinTest, TestAccessibleHWND) {
688   HWND desktop_hwnd = GetDesktopWindow();
689   base::win::ScopedComPtr<IAccessible> desktop_hwnd_iaccessible;
690   ASSERT_EQ(S_OK, AccessibleObjectFromWindow(
691       desktop_hwnd, OBJID_CLIENT,
692       IID_IAccessible,
693       reinterpret_cast<void**>(desktop_hwnd_iaccessible.Receive())));
694
695   scoped_ptr<content::LegacyRenderWidgetHostHWND> accessible_hwnd(
696       content::LegacyRenderWidgetHostHWND::Create(GetDesktopWindow()));
697
698   scoped_ptr<BrowserAccessibilityManagerWin> manager(
699       new BrowserAccessibilityManagerWin(
700           accessible_hwnd.get(),
701           desktop_hwnd_iaccessible,
702           BrowserAccessibilityManagerWin::GetEmptyDocument(),
703           NULL));
704   ASSERT_EQ(desktop_hwnd, manager->parent_hwnd());
705
706   // Enabling screen reader support and calling MaybeCallNotifyWinEvent
707   // should trigger creating the AccessibleHWND, and we should now get a
708   // new parent_hwnd with the right window class to fool older screen
709   // readers.
710   BrowserAccessibilityStateImpl::GetInstance()->OnScreenReaderDetected();
711   manager->MaybeCallNotifyWinEvent(0, 0);
712   HWND new_parent_hwnd = manager->parent_hwnd();
713   ASSERT_NE(desktop_hwnd, new_parent_hwnd);
714   WCHAR hwnd_class_name[256];
715   ASSERT_NE(0, GetClassName(new_parent_hwnd, hwnd_class_name, 256));
716   ASSERT_STREQ(L"Chrome_RenderWidgetHostHWND", hwnd_class_name);
717
718   // Destroy the hwnd explicitly; that should trigger clearing parent_hwnd().
719   DestroyWindow(new_parent_hwnd);
720   ASSERT_EQ(NULL, manager->parent_hwnd());
721
722   // Now create it again.
723   accessible_hwnd = content::LegacyRenderWidgetHostHWND::Create(
724       GetDesktopWindow());
725   manager.reset(
726       new BrowserAccessibilityManagerWin(
727           accessible_hwnd.get(),
728           desktop_hwnd_iaccessible,
729           BrowserAccessibilityManagerWin::GetEmptyDocument(),
730           NULL));
731   manager->MaybeCallNotifyWinEvent(0, 0);
732   new_parent_hwnd = manager->parent_hwnd();
733   ASSERT_FALSE(NULL == new_parent_hwnd);
734
735   // This time, destroy the manager first, make sure the AccessibleHWND doesn't
736   // crash on destruction (to be caught by SyzyASAN or other tools).
737   manager.reset(NULL);
738 }
739
740 }  // namespace content