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