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