Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / browser / accessibility / browser_accessibility_manager_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/strings/string16.h"
6 #include "base/strings/utf_string_conversions.h"
7 #include "content/browser/accessibility/browser_accessibility.h"
8 #include "content/browser/accessibility/browser_accessibility_manager.h"
9 #if defined(OS_WIN)
10 #include "content/browser/accessibility/browser_accessibility_win.h"
11 #endif
12 #include "content/common/accessibility_messages.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 namespace content {
16 namespace {
17
18 // Subclass of BrowserAccessibility that counts the number of instances.
19 class CountedBrowserAccessibility : public BrowserAccessibility {
20  public:
21   CountedBrowserAccessibility() {
22     global_obj_count_++;
23     native_ref_count_ = 1;
24   }
25   virtual ~CountedBrowserAccessibility() {
26     global_obj_count_--;
27   }
28
29   virtual void NativeAddReference() OVERRIDE {
30     native_ref_count_++;
31   }
32
33   virtual void NativeReleaseReference() OVERRIDE {
34     native_ref_count_--;
35     if (native_ref_count_ == 0)
36       delete this;
37   }
38
39   int native_ref_count_;
40   static int global_obj_count_;
41
42 #if defined(OS_WIN)
43   // Adds some padding to prevent a heap-buffer-overflow when an instance of
44   // this class is casted into a BrowserAccessibilityWin pointer.
45   // http://crbug.com/235508
46   // TODO(dmazzoni): Fix this properly.
47   static const size_t kDataSize = sizeof(int) + sizeof(BrowserAccessibility);
48   uint8 padding_[sizeof(BrowserAccessibilityWin) - kDataSize];
49 #endif
50 };
51
52 int CountedBrowserAccessibility::global_obj_count_ = 0;
53
54 // Factory that creates a CountedBrowserAccessibility.
55 class CountedBrowserAccessibilityFactory
56     : public BrowserAccessibilityFactory {
57  public:
58   virtual ~CountedBrowserAccessibilityFactory() {}
59   virtual BrowserAccessibility* Create() OVERRIDE {
60     return new CountedBrowserAccessibility();
61   }
62 };
63
64 class TestBrowserAccessibilityDelegate
65     : public BrowserAccessibilityDelegate {
66  public:
67   TestBrowserAccessibilityDelegate()
68       : got_fatal_error_(false) {}
69
70   virtual void AccessibilitySetFocus(int acc_obj_id) OVERRIDE {}
71   virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE {}
72   virtual void AccessibilityShowMenu(int acc_obj_id) OVERRIDE {}
73   virtual void AccessibilityScrollToMakeVisible(
74       int acc_obj_id, gfx::Rect subfocus) OVERRIDE {}
75   virtual void AccessibilityScrollToPoint(
76       int acc_obj_id, gfx::Point point) OVERRIDE {}
77   virtual void AccessibilitySetTextSelection(
78       int acc_obj_id, int start_offset, int end_offset) OVERRIDE {}
79   virtual bool AccessibilityViewHasFocus() const OVERRIDE {
80     return false;
81   }
82   virtual gfx::Rect AccessibilityGetViewBounds() const OVERRIDE {
83     return gfx::Rect();
84   }
85   virtual gfx::Point AccessibilityOriginInScreen(
86       const gfx::Rect& bounds) const OVERRIDE {
87     return gfx::Point();
88   }
89   virtual void AccessibilityFatalError() OVERRIDE {
90     got_fatal_error_ = true;
91   }
92
93   bool got_fatal_error() const { return got_fatal_error_; }
94   void reset_got_fatal_error() { got_fatal_error_ = false; }
95
96 private:
97   bool got_fatal_error_;
98 };
99
100 }  // anonymous namespace
101
102 TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
103   // Create ui::AXNodeData objects for a simple document tree,
104   // representing the accessibility information used to initialize
105   // BrowserAccessibilityManager.
106   ui::AXNodeData button;
107   button.id = 2;
108   button.SetName("Button");
109   button.role = ui::AX_ROLE_BUTTON;
110   button.state = 0;
111
112   ui::AXNodeData checkbox;
113   checkbox.id = 3;
114   checkbox.SetName("Checkbox");
115   checkbox.role = ui::AX_ROLE_CHECK_BOX;
116   checkbox.state = 0;
117
118   ui::AXNodeData root;
119   root.id = 1;
120   root.SetName("Document");
121   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
122   root.state = 0;
123   root.child_ids.push_back(2);
124   root.child_ids.push_back(3);
125
126   // Construct a BrowserAccessibilityManager with this
127   // ui::AXNodeData tree and a factory for an instance-counting
128   // BrowserAccessibility, and ensure that exactly 3 instances were
129   // created. Note that the manager takes ownership of the factory.
130   CountedBrowserAccessibility::global_obj_count_ = 0;
131   BrowserAccessibilityManager* manager =
132       BrowserAccessibilityManager::Create(
133           MakeAXTreeUpdate(root, button, checkbox),
134           NULL,
135           new CountedBrowserAccessibilityFactory());
136
137   ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
138
139   // Delete the manager and test that all 3 instances are deleted.
140   delete manager;
141   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
142
143   // Construct a manager again, and this time save references to two of
144   // the three nodes in the tree.
145   manager =
146       BrowserAccessibilityManager::Create(
147           MakeAXTreeUpdate(root, button, checkbox),
148           NULL,
149           new CountedBrowserAccessibilityFactory());
150   ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
151
152   CountedBrowserAccessibility* root_accessible =
153       static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
154   root_accessible->NativeAddReference();
155   CountedBrowserAccessibility* child1_accessible =
156       static_cast<CountedBrowserAccessibility*>(
157           root_accessible->PlatformGetChild(1));
158   child1_accessible->NativeAddReference();
159
160   // Now delete the manager, and only one of the three nodes in the tree
161   // should be released.
162   delete manager;
163   ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_);
164
165   // Release each of our references and make sure that each one results in
166   // the instance being deleted as its reference count hits zero.
167   root_accessible->NativeReleaseReference();
168   ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_);
169   child1_accessible->NativeReleaseReference();
170   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
171 }
172
173 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
174   // Make sure that changes to a subtree reuse as many objects as possible.
175
176   // Tree 1:
177   //
178   // root
179   //   child1
180   //   child2
181   //   child3
182
183   ui::AXNodeData tree1_child1;
184   tree1_child1.id = 2;
185   tree1_child1.SetName("Child1");
186   tree1_child1.role = ui::AX_ROLE_BUTTON;
187   tree1_child1.state = 0;
188
189   ui::AXNodeData tree1_child2;
190   tree1_child2.id = 3;
191   tree1_child2.SetName("Child2");
192   tree1_child2.role = ui::AX_ROLE_BUTTON;
193   tree1_child2.state = 0;
194
195   ui::AXNodeData tree1_child3;
196   tree1_child3.id = 4;
197   tree1_child3.SetName("Child3");
198   tree1_child3.role = ui::AX_ROLE_BUTTON;
199   tree1_child3.state = 0;
200
201   ui::AXNodeData tree1_root;
202   tree1_root.id = 1;
203   tree1_root.SetName("Document");
204   tree1_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
205   tree1_root.state = 0;
206   tree1_root.child_ids.push_back(2);
207   tree1_root.child_ids.push_back(3);
208   tree1_root.child_ids.push_back(4);
209
210   // Tree 2:
211   //
212   // root
213   //   child0  <-- inserted
214   //   child1
215   //   child2
216   //           <-- child3 deleted
217
218   ui::AXNodeData tree2_child0;
219   tree2_child0.id = 5;
220   tree2_child0.SetName("Child0");
221   tree2_child0.role = ui::AX_ROLE_BUTTON;
222   tree2_child0.state = 0;
223
224   ui::AXNodeData tree2_root;
225   tree2_root.id = 1;
226   tree2_root.SetName("DocumentChanged");
227   tree2_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
228   tree2_root.state = 0;
229   tree2_root.child_ids.push_back(5);
230   tree2_root.child_ids.push_back(2);
231   tree2_root.child_ids.push_back(3);
232
233   // Construct a BrowserAccessibilityManager with tree1.
234   CountedBrowserAccessibility::global_obj_count_ = 0;
235   BrowserAccessibilityManager* manager =
236       BrowserAccessibilityManager::Create(
237           MakeAXTreeUpdate(tree1_root,
238                            tree1_child1, tree1_child2, tree1_child3),
239           NULL,
240           new CountedBrowserAccessibilityFactory());
241   ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
242
243   // Save references to all of the objects.
244   CountedBrowserAccessibility* root_accessible =
245       static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
246   root_accessible->NativeAddReference();
247   CountedBrowserAccessibility* child1_accessible =
248       static_cast<CountedBrowserAccessibility*>(
249           root_accessible->PlatformGetChild(0));
250   child1_accessible->NativeAddReference();
251   CountedBrowserAccessibility* child2_accessible =
252       static_cast<CountedBrowserAccessibility*>(
253           root_accessible->PlatformGetChild(1));
254   child2_accessible->NativeAddReference();
255   CountedBrowserAccessibility* child3_accessible =
256       static_cast<CountedBrowserAccessibility*>(
257           root_accessible->PlatformGetChild(2));
258   child3_accessible->NativeAddReference();
259
260   // Check the index in parent.
261   EXPECT_EQ(0, child1_accessible->GetIndexInParent());
262   EXPECT_EQ(1, child2_accessible->GetIndexInParent());
263   EXPECT_EQ(2, child3_accessible->GetIndexInParent());
264
265   // Process a notification containing the changed subtree.
266   std::vector<AccessibilityHostMsg_EventParams> params;
267   params.push_back(AccessibilityHostMsg_EventParams());
268   AccessibilityHostMsg_EventParams* msg = &params[0];
269   msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
270   msg->update.nodes.push_back(tree2_root);
271   msg->update.nodes.push_back(tree2_child0);
272   msg->id = tree2_root.id;
273   manager->OnAccessibilityEvents(params);
274
275   // There should be 5 objects now: the 4 from the new tree, plus the
276   // reference to child3 we kept.
277   EXPECT_EQ(5, CountedBrowserAccessibility::global_obj_count_);
278
279   // Check that our references to the root, child1, and child2 are still valid,
280   // but that the reference to child3 is now invalid.
281   EXPECT_TRUE(root_accessible->instance_active());
282   EXPECT_TRUE(child1_accessible->instance_active());
283   EXPECT_TRUE(child2_accessible->instance_active());
284   EXPECT_FALSE(child3_accessible->instance_active());
285
286   // Check that the index in parent has been updated.
287   EXPECT_EQ(1, child1_accessible->GetIndexInParent());
288   EXPECT_EQ(2, child2_accessible->GetIndexInParent());
289
290   // Release our references. The object count should only decrease by 1
291   // for child3.
292   root_accessible->NativeReleaseReference();
293   child1_accessible->NativeReleaseReference();
294   child2_accessible->NativeReleaseReference();
295   child3_accessible->NativeReleaseReference();
296
297   EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
298
299   // Delete the manager and make sure all memory is cleaned up.
300   delete manager;
301   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
302 }
303
304 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
305   // Similar to the test above, but with a more complicated tree.
306
307   // Tree 1:
308   //
309   // root
310   //   container
311   //     child1
312   //       grandchild1
313   //     child2
314   //       grandchild2
315   //     child3
316   //       grandchild3
317
318   ui::AXNodeData tree1_grandchild1;
319   tree1_grandchild1.id = 4;
320   tree1_grandchild1.SetName("GrandChild1");
321   tree1_grandchild1.role = ui::AX_ROLE_BUTTON;
322   tree1_grandchild1.state = 0;
323
324   ui::AXNodeData tree1_child1;
325   tree1_child1.id = 3;
326   tree1_child1.SetName("Child1");
327   tree1_child1.role = ui::AX_ROLE_BUTTON;
328   tree1_child1.state = 0;
329   tree1_child1.child_ids.push_back(4);
330
331   ui::AXNodeData tree1_grandchild2;
332   tree1_grandchild2.id = 6;
333   tree1_grandchild2.SetName("GrandChild1");
334   tree1_grandchild2.role = ui::AX_ROLE_BUTTON;
335   tree1_grandchild2.state = 0;
336
337   ui::AXNodeData tree1_child2;
338   tree1_child2.id = 5;
339   tree1_child2.SetName("Child2");
340   tree1_child2.role = ui::AX_ROLE_BUTTON;
341   tree1_child2.state = 0;
342   tree1_child2.child_ids.push_back(6);
343
344   ui::AXNodeData tree1_grandchild3;
345   tree1_grandchild3.id = 8;
346   tree1_grandchild3.SetName("GrandChild3");
347   tree1_grandchild3.role = ui::AX_ROLE_BUTTON;
348   tree1_grandchild3.state = 0;
349
350   ui::AXNodeData tree1_child3;
351   tree1_child3.id = 7;
352   tree1_child3.SetName("Child3");
353   tree1_child3.role = ui::AX_ROLE_BUTTON;
354   tree1_child3.state = 0;
355   tree1_child3.child_ids.push_back(8);
356
357   ui::AXNodeData tree1_container;
358   tree1_container.id = 2;
359   tree1_container.SetName("Container");
360   tree1_container.role = ui::AX_ROLE_GROUP;
361   tree1_container.state = 0;
362   tree1_container.child_ids.push_back(3);
363   tree1_container.child_ids.push_back(5);
364   tree1_container.child_ids.push_back(7);
365
366   ui::AXNodeData tree1_root;
367   tree1_root.id = 1;
368   tree1_root.SetName("Document");
369   tree1_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
370   tree1_root.state = 0;
371   tree1_root.child_ids.push_back(2);
372
373   // Tree 2:
374   //
375   // root
376   //   container
377   //     child0         <-- inserted
378   //       grandchild0  <--
379   //     child1
380   //       grandchild1
381   //     child2
382   //       grandchild2
383   //                    <-- child3 (and grandchild3) deleted
384
385   ui::AXNodeData tree2_grandchild0;
386   tree2_grandchild0.id = 9;
387   tree2_grandchild0.SetName("GrandChild0");
388   tree2_grandchild0.role = ui::AX_ROLE_BUTTON;
389   tree2_grandchild0.state = 0;
390
391   ui::AXNodeData tree2_child0;
392   tree2_child0.id = 10;
393   tree2_child0.SetName("Child0");
394   tree2_child0.role = ui::AX_ROLE_BUTTON;
395   tree2_child0.state = 0;
396   tree2_child0.child_ids.push_back(9);
397
398   ui::AXNodeData tree2_container;
399   tree2_container.id = 2;
400   tree2_container.SetName("Container");
401   tree2_container.role = ui::AX_ROLE_GROUP;
402   tree2_container.state = 0;
403   tree2_container.child_ids.push_back(10);
404   tree2_container.child_ids.push_back(3);
405   tree2_container.child_ids.push_back(5);
406
407   // Construct a BrowserAccessibilityManager with tree1.
408   CountedBrowserAccessibility::global_obj_count_ = 0;
409   BrowserAccessibilityManager* manager =
410       BrowserAccessibilityManager::Create(
411           MakeAXTreeUpdate(tree1_root, tree1_container,
412                            tree1_child1, tree1_grandchild1,
413                            tree1_child2, tree1_grandchild2,
414                            tree1_child3, tree1_grandchild3),
415           NULL,
416           new CountedBrowserAccessibilityFactory());
417   ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
418
419   // Save references to some objects.
420   CountedBrowserAccessibility* root_accessible =
421       static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
422   root_accessible->NativeAddReference();
423   CountedBrowserAccessibility* container_accessible =
424       static_cast<CountedBrowserAccessibility*>(
425           root_accessible->PlatformGetChild(0));
426   container_accessible->NativeAddReference();
427   CountedBrowserAccessibility* child2_accessible =
428       static_cast<CountedBrowserAccessibility*>(
429           container_accessible->PlatformGetChild(1));
430   child2_accessible->NativeAddReference();
431   CountedBrowserAccessibility* child3_accessible =
432       static_cast<CountedBrowserAccessibility*>(
433           container_accessible->PlatformGetChild(2));
434   child3_accessible->NativeAddReference();
435
436   // Check the index in parent.
437   EXPECT_EQ(1, child2_accessible->GetIndexInParent());
438   EXPECT_EQ(2, child3_accessible->GetIndexInParent());
439
440   // Process a notification containing the changed subtree rooted at
441   // the container.
442   std::vector<AccessibilityHostMsg_EventParams> params;
443   params.push_back(AccessibilityHostMsg_EventParams());
444   AccessibilityHostMsg_EventParams* msg = &params[0];
445   msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
446   msg->update.nodes.push_back(tree2_container);
447   msg->update.nodes.push_back(tree2_child0);
448   msg->update.nodes.push_back(tree2_grandchild0);
449   msg->id = tree2_container.id;
450   manager->OnAccessibilityEvents(params);
451
452   // There should be 9 objects now: the 8 from the new tree, plus the
453   // reference to child3 we kept.
454   EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_);
455
456   // Check that our references to the root and container and child2 are
457   // still valid, but that the reference to child3 is now invalid.
458   EXPECT_TRUE(root_accessible->instance_active());
459   EXPECT_TRUE(container_accessible->instance_active());
460   EXPECT_TRUE(child2_accessible->instance_active());
461   EXPECT_FALSE(child3_accessible->instance_active());
462
463   // Ensure that we retain the parent of the detached subtree.
464   EXPECT_EQ(root_accessible, container_accessible->GetParent());
465   EXPECT_EQ(0, container_accessible->GetIndexInParent());
466
467   // Check that the index in parent has been updated.
468   EXPECT_EQ(2, child2_accessible->GetIndexInParent());
469
470   // Release our references. The object count should only decrease by 1
471   // for child3.
472   root_accessible->NativeReleaseReference();
473   container_accessible->NativeReleaseReference();
474   child2_accessible->NativeReleaseReference();
475   child3_accessible->NativeReleaseReference();
476
477   EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
478
479   // Delete the manager and make sure all memory is cleaned up.
480   delete manager;
481   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
482 }
483
484 TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
485   // Tree 1:
486   //
487   // 1
488   //   2
489   //   3
490   //     4
491
492   ui::AXNodeData tree1_4;
493   tree1_4.id = 4;
494   tree1_4.state = 0;
495
496   ui::AXNodeData tree1_3;
497   tree1_3.id = 3;
498   tree1_3.state = 0;
499   tree1_3.child_ids.push_back(4);
500
501   ui::AXNodeData tree1_2;
502   tree1_2.id = 2;
503   tree1_2.state = 0;
504
505   ui::AXNodeData tree1_1;
506   tree1_1.id = 1;
507   tree1_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
508   tree1_1.state = 0;
509   tree1_1.child_ids.push_back(2);
510   tree1_1.child_ids.push_back(3);
511
512   // Tree 2:
513   //
514   // 1
515   //   4    <-- moves up a level and gains child
516   //     6  <-- new
517   //   5    <-- new
518
519   ui::AXNodeData tree2_6;
520   tree2_6.id = 6;
521   tree2_6.state = 0;
522
523   ui::AXNodeData tree2_5;
524   tree2_5.id = 5;
525   tree2_5.state = 0;
526
527   ui::AXNodeData tree2_4;
528   tree2_4.id = 4;
529   tree2_4.state = 0;
530   tree2_4.child_ids.push_back(6);
531
532   ui::AXNodeData tree2_1;
533   tree2_1.id = 1;
534   tree2_1.state = 0;
535   tree2_1.child_ids.push_back(4);
536   tree2_1.child_ids.push_back(5);
537
538   // Construct a BrowserAccessibilityManager with tree1.
539   CountedBrowserAccessibility::global_obj_count_ = 0;
540   BrowserAccessibilityManager* manager =
541       BrowserAccessibilityManager::Create(
542           MakeAXTreeUpdate(tree1_1, tree1_2, tree1_3, tree1_4),
543           NULL,
544           new CountedBrowserAccessibilityFactory());
545   ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
546
547   // Process a notification containing the changed subtree.
548   std::vector<AccessibilityHostMsg_EventParams> params;
549   params.push_back(AccessibilityHostMsg_EventParams());
550   AccessibilityHostMsg_EventParams* msg = &params[0];
551   msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
552   msg->update.nodes.push_back(tree2_1);
553   msg->update.nodes.push_back(tree2_4);
554   msg->update.nodes.push_back(tree2_5);
555   msg->update.nodes.push_back(tree2_6);
556   msg->id = tree2_1.id;
557   manager->OnAccessibilityEvents(params);
558
559   // There should be 4 objects now.
560   EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
561
562   // Delete the manager and make sure all memory is cleaned up.
563   delete manager;
564   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
565 }
566
567 TEST(BrowserAccessibilityManagerTest, TestFatalError) {
568   // Test that BrowserAccessibilityManager raises a fatal error
569   // (which will crash the renderer) if the same id is used in
570   // two places in the tree.
571
572   ui::AXNodeData root;
573   root.id = 1;
574   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
575   root.child_ids.push_back(2);
576   root.child_ids.push_back(2);
577
578   CountedBrowserAccessibilityFactory* factory =
579       new CountedBrowserAccessibilityFactory();
580   scoped_ptr<TestBrowserAccessibilityDelegate> delegate(
581       new TestBrowserAccessibilityDelegate());
582   scoped_ptr<BrowserAccessibilityManager> manager;
583   ASSERT_FALSE(delegate->got_fatal_error());
584   manager.reset(BrowserAccessibilityManager::Create(
585       MakeAXTreeUpdate(root),
586       delegate.get(),
587       factory));
588   ASSERT_TRUE(delegate->got_fatal_error());
589
590   ui::AXNodeData root2;
591   root2.id = 1;
592   root2.role = ui::AX_ROLE_ROOT_WEB_AREA;
593   root2.child_ids.push_back(2);
594   root2.child_ids.push_back(3);
595
596   ui::AXNodeData child1;
597   child1.id = 2;
598   child1.child_ids.push_back(4);
599   child1.child_ids.push_back(5);
600
601   ui::AXNodeData child2;
602   child2.id = 3;
603   child2.child_ids.push_back(6);
604   child2.child_ids.push_back(5);  // Duplicate
605
606   ui::AXNodeData grandchild4;
607   grandchild4.id = 4;
608
609   ui::AXNodeData grandchild5;
610   grandchild5.id = 5;
611
612   ui::AXNodeData grandchild6;
613   grandchild6.id = 6;
614
615   delegate->reset_got_fatal_error();
616   factory = new CountedBrowserAccessibilityFactory();
617   manager.reset(BrowserAccessibilityManager::Create(
618       MakeAXTreeUpdate(root2, child1, child2,
619                        grandchild4, grandchild5, grandchild6),
620       delegate.get(),
621       factory));
622   ASSERT_TRUE(delegate->got_fatal_error());
623 }
624
625 TEST(BrowserAccessibilityManagerTest, BoundsForRange) {
626   ui::AXNodeData root;
627   root.id = 1;
628   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
629
630   ui::AXNodeData static_text;
631   static_text.id = 2;
632   static_text.SetValue("Hello, world.");
633   static_text.role = ui::AX_ROLE_STATIC_TEXT;
634   static_text.location = gfx::Rect(100, 100, 29, 18);
635   root.child_ids.push_back(2);
636
637   ui::AXNodeData inline_text1;
638   inline_text1.id = 3;
639   inline_text1.SetValue("Hello, ");
640   inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
641   inline_text1.location = gfx::Rect(100, 100, 29, 9);
642   inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
643                                ui::AX_TEXT_DIRECTION_LR);
644   std::vector<int32> character_offsets1;
645   character_offsets1.push_back(6);   // 0
646   character_offsets1.push_back(11);  // 1
647   character_offsets1.push_back(16);  // 2
648   character_offsets1.push_back(21);  // 3
649   character_offsets1.push_back(26);  // 4
650   character_offsets1.push_back(29);  // 5
651   character_offsets1.push_back(29);  // 6 (note that the space has no width)
652   inline_text1.AddIntListAttribute(
653       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
654   static_text.child_ids.push_back(3);
655
656   ui::AXNodeData inline_text2;
657   inline_text2.id = 4;
658   inline_text2.SetValue("world.");
659   inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
660   inline_text2.location = gfx::Rect(100, 109, 28, 9);
661   inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
662                                ui::AX_TEXT_DIRECTION_LR);
663   std::vector<int32> character_offsets2;
664   character_offsets2.push_back(5);
665   character_offsets2.push_back(10);
666   character_offsets2.push_back(15);
667   character_offsets2.push_back(20);
668   character_offsets2.push_back(25);
669   character_offsets2.push_back(28);
670   inline_text2.AddIntListAttribute(
671       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
672   static_text.child_ids.push_back(4);
673
674   scoped_ptr<BrowserAccessibilityManager> manager(
675       BrowserAccessibilityManager::Create(
676           MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
677           NULL,
678           new CountedBrowserAccessibilityFactory()));
679
680   BrowserAccessibility* root_accessible = manager->GetRoot();
681   BrowserAccessibility* static_text_accessible =
682       root_accessible->PlatformGetChild(0);
683
684   EXPECT_EQ(gfx::Rect(100, 100, 6, 9).ToString(),
685             static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
686
687   EXPECT_EQ(gfx::Rect(100, 100, 26, 9).ToString(),
688             static_text_accessible->GetLocalBoundsForRange(0, 5).ToString());
689
690   EXPECT_EQ(gfx::Rect(100, 109, 5, 9).ToString(),
691             static_text_accessible->GetLocalBoundsForRange(7, 1).ToString());
692
693   EXPECT_EQ(gfx::Rect(100, 109, 25, 9).ToString(),
694             static_text_accessible->GetLocalBoundsForRange(7, 5).ToString());
695
696   EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
697             static_text_accessible->GetLocalBoundsForRange(5, 3).ToString());
698
699   EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
700             static_text_accessible->GetLocalBoundsForRange(0, 13).ToString());
701
702   // Test range that's beyond the text.
703   EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
704             static_text_accessible->GetLocalBoundsForRange(-1, 999).ToString());
705
706   // Test that we can call bounds for range on the parent element, too,
707   // and it still works.
708   EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
709             root_accessible->GetLocalBoundsForRange(0, 13).ToString());
710 }
711
712 TEST(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) {
713   // In this example, we assume that the string "123abc" is rendered with
714   // "123" going left-to-right and "abc" going right-to-left. In other
715   // words, on-screen it would look like "123cba". This is possible to
716   // acheive if the source string had unicode control characters
717   // to switch directions. This test doesn't worry about how, though - it just
718   // tests that if something like that were to occur, GetLocalBoundsForRange
719   // returns the correct bounds for different ranges.
720
721   ui::AXNodeData root;
722   root.id = 1;
723   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
724
725   ui::AXNodeData static_text;
726   static_text.id = 2;
727   static_text.SetValue("123abc");
728   static_text.role = ui::AX_ROLE_STATIC_TEXT;
729   static_text.location = gfx::Rect(100, 100, 60, 20);
730   root.child_ids.push_back(2);
731
732   ui::AXNodeData inline_text1;
733   inline_text1.id = 3;
734   inline_text1.SetValue("123");
735   inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
736   inline_text1.location = gfx::Rect(100, 100, 30, 20);
737   inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
738                                ui::AX_TEXT_DIRECTION_LR);
739   std::vector<int32> character_offsets1;
740   character_offsets1.push_back(10);  // 0
741   character_offsets1.push_back(20);  // 1
742   character_offsets1.push_back(30);  // 2
743   inline_text1.AddIntListAttribute(
744       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
745   static_text.child_ids.push_back(3);
746
747   ui::AXNodeData inline_text2;
748   inline_text2.id = 4;
749   inline_text2.SetValue("abc");
750   inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
751   inline_text2.location = gfx::Rect(130, 100, 30, 20);
752   inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
753                                ui::AX_TEXT_DIRECTION_RL);
754   std::vector<int32> character_offsets2;
755   character_offsets2.push_back(10);
756   character_offsets2.push_back(20);
757   character_offsets2.push_back(30);
758   inline_text2.AddIntListAttribute(
759       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
760   static_text.child_ids.push_back(4);
761
762   scoped_ptr<BrowserAccessibilityManager> manager(
763       BrowserAccessibilityManager::Create(
764           MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
765           NULL,
766           new CountedBrowserAccessibilityFactory()));
767
768   BrowserAccessibility* root_accessible = manager->GetRoot();
769   BrowserAccessibility* static_text_accessible =
770       root_accessible->PlatformGetChild(0);
771
772   EXPECT_EQ(gfx::Rect(100, 100, 60, 20).ToString(),
773             static_text_accessible->GetLocalBoundsForRange(0, 6).ToString());
774
775   EXPECT_EQ(gfx::Rect(100, 100, 10, 20).ToString(),
776             static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
777
778   EXPECT_EQ(gfx::Rect(100, 100, 30, 20).ToString(),
779             static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
780
781   EXPECT_EQ(gfx::Rect(150, 100, 10, 20).ToString(),
782             static_text_accessible->GetLocalBoundsForRange(3, 1).ToString());
783
784   EXPECT_EQ(gfx::Rect(130, 100, 30, 20).ToString(),
785             static_text_accessible->GetLocalBoundsForRange(3, 3).ToString());
786
787   // This range is only two characters, but because of the direction switch
788   // the bounds are as wide as four characters.
789   EXPECT_EQ(gfx::Rect(120, 100, 40, 20).ToString(),
790             static_text_accessible->GetLocalBoundsForRange(2, 2).ToString());
791 }
792
793 #if defined(OS_WIN)
794 #define MAYBE_BoundsForRangeOnParentElement \
795   DISABLED_BoundsForRangeOnParentElement
796 #else
797 #define MAYBE_BoundsForRangeOnParentElement BoundsForRangeOnParentElement
798 #endif
799 TEST(BrowserAccessibilityManagerTest, MAYBE_BoundsForRangeOnParentElement) {
800   ui::AXNodeData root;
801   root.id = 1;
802   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
803   root.child_ids.push_back(2);
804
805   ui::AXNodeData div;
806   div.id = 2;
807   div.role = ui::AX_ROLE_DIV;
808   div.location = gfx::Rect(100, 100, 100, 20);
809   div.child_ids.push_back(3);
810   div.child_ids.push_back(4);
811   div.child_ids.push_back(5);
812
813   ui::AXNodeData static_text1;
814   static_text1.id = 3;
815   static_text1.SetValue("AB");
816   static_text1.role = ui::AX_ROLE_STATIC_TEXT;
817   static_text1.location = gfx::Rect(100, 100, 40, 20);
818   static_text1.child_ids.push_back(6);
819
820   ui::AXNodeData img;
821   img.id = 4;
822   img.role = ui::AX_ROLE_IMAGE;
823   img.location = gfx::Rect(140, 100, 20, 20);
824
825   ui::AXNodeData static_text2;
826   static_text2.id = 5;
827   static_text2.SetValue("CD");
828   static_text2.role = ui::AX_ROLE_STATIC_TEXT;
829   static_text2.location = gfx::Rect(160, 100, 40, 20);
830   static_text2.child_ids.push_back(7);
831
832   ui::AXNodeData inline_text1;
833   inline_text1.id = 6;
834   inline_text1.SetValue("AB");
835   inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
836   inline_text1.location = gfx::Rect(100, 100, 40, 20);
837   inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
838                                ui::AX_TEXT_DIRECTION_LR);
839   std::vector<int32> character_offsets1;
840   character_offsets1.push_back(20);  // 0
841   character_offsets1.push_back(40);  // 1
842   inline_text1.AddIntListAttribute(
843       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
844
845   ui::AXNodeData inline_text2;
846   inline_text2.id = 7;
847   inline_text2.SetValue("CD");
848   inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
849   inline_text2.location = gfx::Rect(160, 100, 40, 20);
850   inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
851                                ui::AX_TEXT_DIRECTION_LR);
852   std::vector<int32> character_offsets2;
853   character_offsets2.push_back(20);  // 0
854   character_offsets2.push_back(40);  // 1
855   inline_text2.AddIntListAttribute(
856       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
857
858   scoped_ptr<BrowserAccessibilityManager> manager(
859       BrowserAccessibilityManager::Create(
860           MakeAXTreeUpdate(
861               root, div, static_text1, img,
862               static_text2, inline_text1, inline_text2),
863           NULL,
864           new CountedBrowserAccessibilityFactory()));
865   BrowserAccessibility* root_accessible = manager->GetRoot();
866
867   EXPECT_EQ(gfx::Rect(100, 100, 20, 20).ToString(),
868             root_accessible->GetLocalBoundsForRange(0, 1).ToString());
869
870   EXPECT_EQ(gfx::Rect(100, 100, 40, 20).ToString(),
871             root_accessible->GetLocalBoundsForRange(0, 2).ToString());
872
873   EXPECT_EQ(gfx::Rect(100, 100, 80, 20).ToString(),
874             root_accessible->GetLocalBoundsForRange(0, 3).ToString());
875
876   EXPECT_EQ(gfx::Rect(120, 100, 60, 20).ToString(),
877             root_accessible->GetLocalBoundsForRange(1, 2).ToString());
878
879   EXPECT_EQ(gfx::Rect(120, 100, 80, 20).ToString(),
880             root_accessible->GetLocalBoundsForRange(1, 3).ToString());
881
882   EXPECT_EQ(gfx::Rect(100, 100, 100, 20).ToString(),
883             root_accessible->GetLocalBoundsForRange(0, 4).ToString());
884 }
885
886 TEST(BrowserAccessibilityManagerTest, NextPreviousInTreeOrder) {
887   ui::AXNodeData root;
888   root.id = 1;
889   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
890
891   ui::AXNodeData node2;
892   node2.id = 2;
893   root.child_ids.push_back(2);
894
895   ui::AXNodeData node3;
896   node3.id = 3;
897   root.child_ids.push_back(3);
898
899   ui::AXNodeData node4;
900   node4.id = 4;
901   node3.child_ids.push_back(4);
902
903   ui::AXNodeData node5;
904   node5.id = 5;
905   root.child_ids.push_back(5);
906
907   scoped_ptr<BrowserAccessibilityManager> manager(
908       BrowserAccessibilityManager::Create(
909           MakeAXTreeUpdate(root, node2, node3, node4, node5),
910           NULL,
911           new CountedBrowserAccessibilityFactory()));
912
913   BrowserAccessibility* root_accessible = manager->GetRoot();
914   BrowserAccessibility* node2_accessible = root_accessible->PlatformGetChild(0);
915   BrowserAccessibility* node3_accessible = root_accessible->PlatformGetChild(1);
916   BrowserAccessibility* node4_accessible =
917       node3_accessible->PlatformGetChild(0);
918   BrowserAccessibility* node5_accessible = root_accessible->PlatformGetChild(2);
919
920   ASSERT_EQ(NULL, manager->NextInTreeOrder(NULL));
921   ASSERT_EQ(node2_accessible, manager->NextInTreeOrder(root_accessible));
922   ASSERT_EQ(node3_accessible, manager->NextInTreeOrder(node2_accessible));
923   ASSERT_EQ(node4_accessible, manager->NextInTreeOrder(node3_accessible));
924   ASSERT_EQ(node5_accessible, manager->NextInTreeOrder(node4_accessible));
925   ASSERT_EQ(NULL, manager->NextInTreeOrder(node5_accessible));
926
927   ASSERT_EQ(NULL, manager->PreviousInTreeOrder(NULL));
928   ASSERT_EQ(node4_accessible, manager->PreviousInTreeOrder(node5_accessible));
929   ASSERT_EQ(node3_accessible, manager->PreviousInTreeOrder(node4_accessible));
930   ASSERT_EQ(node2_accessible, manager->PreviousInTreeOrder(node3_accessible));
931   ASSERT_EQ(root_accessible, manager->PreviousInTreeOrder(node2_accessible));
932 }
933
934 }  // namespace content