Upstream version 5.34.104.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 SetAccessibilityFocus(int acc_obj_id) OVERRIDE {}
71   virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE {}
72   virtual void AccessibilityScrollToMakeVisible(
73       int acc_obj_id, gfx::Rect subfocus) OVERRIDE {}
74   virtual void AccessibilityScrollToPoint(
75       int acc_obj_id, gfx::Point point) OVERRIDE {}
76   virtual void AccessibilitySetTextSelection(
77       int acc_obj_id, int start_offset, int end_offset) OVERRIDE {}
78   virtual bool HasFocus() const OVERRIDE {
79     return false;
80   }
81   virtual gfx::Rect GetViewBounds() const OVERRIDE {
82     return gfx::Rect();
83   }
84   virtual gfx::Point GetLastTouchEventLocation() const OVERRIDE {
85     return gfx::Point();
86   }
87   virtual void FatalAccessibilityTreeError() OVERRIDE {
88     got_fatal_error_ = true;
89   }
90
91   bool got_fatal_error() const { return got_fatal_error_; }
92   void reset_got_fatal_error() { got_fatal_error_ = false; }
93
94 private:
95   bool got_fatal_error_;
96 };
97
98 }  // anonymous namespace
99
100 TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
101   // Create ui::AXNodeData objects for a simple document tree,
102   // representing the accessibility information used to initialize
103   // BrowserAccessibilityManager.
104   ui::AXNodeData button;
105   button.id = 2;
106   button.SetName("Button");
107   button.role = ui::AX_ROLE_BUTTON;
108   button.state = 0;
109
110   ui::AXNodeData checkbox;
111   checkbox.id = 3;
112   checkbox.SetName("Checkbox");
113   checkbox.role = ui::AX_ROLE_CHECK_BOX;
114   checkbox.state = 0;
115
116   ui::AXNodeData root;
117   root.id = 1;
118   root.SetName("Document");
119   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
120   root.state = 0;
121   root.child_ids.push_back(2);
122   root.child_ids.push_back(3);
123
124   // Construct a BrowserAccessibilityManager with this
125   // ui::AXNodeData tree and a factory for an instance-counting
126   // BrowserAccessibility, and ensure that exactly 3 instances were
127   // created. Note that the manager takes ownership of the factory.
128   CountedBrowserAccessibility::global_obj_count_ = 0;
129   BrowserAccessibilityManager* manager =
130       BrowserAccessibilityManager::Create(
131           root,
132           NULL,
133           new CountedBrowserAccessibilityFactory());
134   manager->UpdateNodesForTesting(button, checkbox);
135
136   ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
137
138   // Delete the manager and test that all 3 instances are deleted.
139   delete manager;
140   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
141
142   // Construct a manager again, and this time save references to two of
143   // the three nodes in the tree.
144   manager =
145       BrowserAccessibilityManager::Create(
146           root,
147           NULL,
148           new CountedBrowserAccessibilityFactory());
149   manager->UpdateNodesForTesting(button, checkbox);
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           tree1_root,
238           NULL,
239           new CountedBrowserAccessibilityFactory());
240   manager->UpdateNodesForTesting(tree1_child1, tree1_child2, tree1_child3);
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->index_in_parent());
262   EXPECT_EQ(1, child2_accessible->index_in_parent());
263   EXPECT_EQ(2, child3_accessible->index_in_parent());
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->nodes.push_back(tree2_root);
271   msg->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->index_in_parent());
288   EXPECT_EQ(2, child2_accessible->index_in_parent());
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           tree1_root,
412           NULL,
413           new CountedBrowserAccessibilityFactory());
414   manager->UpdateNodesForTesting(tree1_container,
415                                  tree1_child1, tree1_grandchild1,
416                                  tree1_child2, tree1_grandchild2,
417                                  tree1_child3, tree1_grandchild3);
418   ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
419
420   // Save references to some objects.
421   CountedBrowserAccessibility* root_accessible =
422       static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
423   root_accessible->NativeAddReference();
424   CountedBrowserAccessibility* container_accessible =
425       static_cast<CountedBrowserAccessibility*>(
426           root_accessible->PlatformGetChild(0));
427   container_accessible->NativeAddReference();
428   CountedBrowserAccessibility* child2_accessible =
429       static_cast<CountedBrowserAccessibility*>(
430           container_accessible->PlatformGetChild(1));
431   child2_accessible->NativeAddReference();
432   CountedBrowserAccessibility* child3_accessible =
433       static_cast<CountedBrowserAccessibility*>(
434           container_accessible->PlatformGetChild(2));
435   child3_accessible->NativeAddReference();
436
437   // Check the index in parent.
438   EXPECT_EQ(1, child2_accessible->index_in_parent());
439   EXPECT_EQ(2, child3_accessible->index_in_parent());
440
441   // Process a notification containing the changed subtree rooted at
442   // the container.
443   std::vector<AccessibilityHostMsg_EventParams> params;
444   params.push_back(AccessibilityHostMsg_EventParams());
445   AccessibilityHostMsg_EventParams* msg = &params[0];
446   msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
447   msg->nodes.push_back(tree2_container);
448   msg->nodes.push_back(tree2_child0);
449   msg->nodes.push_back(tree2_grandchild0);
450   msg->id = tree2_container.id;
451   manager->OnAccessibilityEvents(params);
452
453   // There should be 9 objects now: the 8 from the new tree, plus the
454   // reference to child3 we kept.
455   EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_);
456
457   // Check that our references to the root and container and child2 are
458   // still valid, but that the reference to child3 is now invalid.
459   EXPECT_TRUE(root_accessible->instance_active());
460   EXPECT_TRUE(container_accessible->instance_active());
461   EXPECT_TRUE(child2_accessible->instance_active());
462   EXPECT_FALSE(child3_accessible->instance_active());
463
464   // Ensure that we retain the parent of the detached subtree.
465   EXPECT_EQ(root_accessible, container_accessible->parent());
466   EXPECT_EQ(0, container_accessible->index_in_parent());
467
468   // Check that the index in parent has been updated.
469   EXPECT_EQ(2, child2_accessible->index_in_parent());
470
471   // Release our references. The object count should only decrease by 1
472   // for child3.
473   root_accessible->NativeReleaseReference();
474   container_accessible->NativeReleaseReference();
475   child2_accessible->NativeReleaseReference();
476   child3_accessible->NativeReleaseReference();
477
478   EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
479
480   // Delete the manager and make sure all memory is cleaned up.
481   delete manager;
482   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
483 }
484
485 TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
486   // Tree 1:
487   //
488   // 1
489   //   2
490   //   3
491   //     4
492
493   ui::AXNodeData tree1_4;
494   tree1_4.id = 4;
495   tree1_4.state = 0;
496
497   ui::AXNodeData tree1_3;
498   tree1_3.id = 3;
499   tree1_3.state = 0;
500   tree1_3.child_ids.push_back(4);
501
502   ui::AXNodeData tree1_2;
503   tree1_2.id = 2;
504   tree1_2.state = 0;
505
506   ui::AXNodeData tree1_1;
507   tree1_1.id = 1;
508   tree1_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
509   tree1_1.state = 0;
510   tree1_1.child_ids.push_back(2);
511   tree1_1.child_ids.push_back(3);
512
513   // Tree 2:
514   //
515   // 1
516   //   4    <-- moves up a level and gains child
517   //     6  <-- new
518   //   5    <-- new
519
520   ui::AXNodeData tree2_6;
521   tree2_6.id = 6;
522   tree2_6.state = 0;
523
524   ui::AXNodeData tree2_5;
525   tree2_5.id = 5;
526   tree2_5.state = 0;
527
528   ui::AXNodeData tree2_4;
529   tree2_4.id = 4;
530   tree2_4.state = 0;
531   tree2_4.child_ids.push_back(6);
532
533   ui::AXNodeData tree2_1;
534   tree2_1.id = 1;
535   tree2_1.state = 0;
536   tree2_1.child_ids.push_back(4);
537   tree2_1.child_ids.push_back(5);
538
539   // Construct a BrowserAccessibilityManager with tree1.
540   CountedBrowserAccessibility::global_obj_count_ = 0;
541   BrowserAccessibilityManager* manager =
542       BrowserAccessibilityManager::Create(
543           tree1_1,
544           NULL,
545           new CountedBrowserAccessibilityFactory());
546   manager->UpdateNodesForTesting(tree1_2, tree1_3, tree1_4);
547   ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
548
549   // Process a notification containing the changed subtree.
550   std::vector<AccessibilityHostMsg_EventParams> params;
551   params.push_back(AccessibilityHostMsg_EventParams());
552   AccessibilityHostMsg_EventParams* msg = &params[0];
553   msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
554   msg->nodes.push_back(tree2_1);
555   msg->nodes.push_back(tree2_4);
556   msg->nodes.push_back(tree2_5);
557   msg->nodes.push_back(tree2_6);
558   msg->id = tree2_1.id;
559   manager->OnAccessibilityEvents(params);
560
561   // There should be 4 objects now.
562   EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
563
564   // Delete the manager and make sure all memory is cleaned up.
565   delete manager;
566   ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
567 }
568
569 // Crashes on Windows. http://crbug.com/304130
570 #if defined(OS_WIN)
571 #define MAYBE_TestFatalError DISABLED_TestFatalError
572 #else
573 #define MAYBE_TestFatalError TestFatalError
574 #endif
575 TEST(BrowserAccessibilityManagerTest, MAYBE_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       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   delegate->reset_got_fatal_error();
615   factory = new CountedBrowserAccessibilityFactory();
616   manager.reset(BrowserAccessibilityManager::Create(
617       root2,
618       delegate.get(),
619       factory));
620   ASSERT_FALSE(delegate->got_fatal_error());
621   manager->UpdateNodesForTesting(child1, child2);
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           root,
677           NULL,
678           new CountedBrowserAccessibilityFactory()));
679   manager->UpdateNodesForTesting(static_text, inline_text1, inline_text2);
680
681   BrowserAccessibility* root_accessible = manager->GetRoot();
682   BrowserAccessibility* static_text_accessible =
683       root_accessible->PlatformGetChild(0);
684
685   EXPECT_EQ(gfx::Rect(100, 100, 6, 9).ToString(),
686             static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
687
688   EXPECT_EQ(gfx::Rect(100, 100, 26, 9).ToString(),
689             static_text_accessible->GetLocalBoundsForRange(0, 5).ToString());
690
691   EXPECT_EQ(gfx::Rect(100, 109, 5, 9).ToString(),
692             static_text_accessible->GetLocalBoundsForRange(7, 1).ToString());
693
694   EXPECT_EQ(gfx::Rect(100, 109, 25, 9).ToString(),
695             static_text_accessible->GetLocalBoundsForRange(7, 5).ToString());
696
697   EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
698             static_text_accessible->GetLocalBoundsForRange(5, 3).ToString());
699
700   EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
701             static_text_accessible->GetLocalBoundsForRange(0, 13).ToString());
702
703   // Test range that's beyond the text.
704   EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
705             static_text_accessible->GetLocalBoundsForRange(-1, 999).ToString());
706
707   // Test that we can call bounds for range on the parent element, too,
708   // and it still works.
709   EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
710             root_accessible->GetLocalBoundsForRange(0, 13).ToString());
711 }
712
713 TEST(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) {
714   // In this example, we assume that the string "123abc" is rendered with
715   // "123" going left-to-right and "abc" going right-to-left. In other
716   // words, on-screen it would look like "123cba". This is possible to
717   // acheive if the source string had unicode control characters
718   // to switch directions. This test doesn't worry about how, though - it just
719   // tests that if something like that were to occur, GetLocalBoundsForRange
720   // returns the correct bounds for different ranges.
721
722   ui::AXNodeData root;
723   root.id = 1;
724   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
725
726   ui::AXNodeData static_text;
727   static_text.id = 2;
728   static_text.SetValue("123abc");
729   static_text.role = ui::AX_ROLE_STATIC_TEXT;
730   static_text.location = gfx::Rect(100, 100, 60, 20);
731   root.child_ids.push_back(2);
732
733   ui::AXNodeData inline_text1;
734   inline_text1.id = 3;
735   inline_text1.SetValue("123");
736   inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
737   inline_text1.location = gfx::Rect(100, 100, 30, 20);
738   inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
739                                ui::AX_TEXT_DIRECTION_LR);
740   std::vector<int32> character_offsets1;
741   character_offsets1.push_back(10);  // 0
742   character_offsets1.push_back(20);  // 1
743   character_offsets1.push_back(30);  // 2
744   inline_text1.AddIntListAttribute(
745       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
746   static_text.child_ids.push_back(3);
747
748   ui::AXNodeData inline_text2;
749   inline_text2.id = 4;
750   inline_text2.SetValue("abc");
751   inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
752   inline_text2.location = gfx::Rect(130, 100, 30, 20);
753   inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
754                                ui::AX_TEXT_DIRECTION_RL);
755   std::vector<int32> character_offsets2;
756   character_offsets2.push_back(10);
757   character_offsets2.push_back(20);
758   character_offsets2.push_back(30);
759   inline_text2.AddIntListAttribute(
760       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
761   static_text.child_ids.push_back(4);
762
763   scoped_ptr<BrowserAccessibilityManager> manager(
764       BrowserAccessibilityManager::Create(
765           root,
766           NULL,
767           new CountedBrowserAccessibilityFactory()));
768   manager->UpdateNodesForTesting(static_text, inline_text1, inline_text2);
769
770   BrowserAccessibility* root_accessible = manager->GetRoot();
771   BrowserAccessibility* static_text_accessible =
772       root_accessible->PlatformGetChild(0);
773
774   EXPECT_EQ(gfx::Rect(100, 100, 60, 20).ToString(),
775             static_text_accessible->GetLocalBoundsForRange(0, 6).ToString());
776
777   EXPECT_EQ(gfx::Rect(100, 100, 10, 20).ToString(),
778             static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
779
780   EXPECT_EQ(gfx::Rect(100, 100, 30, 20).ToString(),
781             static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
782
783   EXPECT_EQ(gfx::Rect(150, 100, 10, 20).ToString(),
784             static_text_accessible->GetLocalBoundsForRange(3, 1).ToString());
785
786   EXPECT_EQ(gfx::Rect(130, 100, 30, 20).ToString(),
787             static_text_accessible->GetLocalBoundsForRange(3, 3).ToString());
788
789   // This range is only two characters, but because of the direction switch
790   // the bounds are as wide as four characters.
791   EXPECT_EQ(gfx::Rect(120, 100, 40, 20).ToString(),
792             static_text_accessible->GetLocalBoundsForRange(2, 2).ToString());
793 }
794
795 #if defined(OS_WIN)
796 #define MAYBE_BoundsForRangeOnParentElement \
797   DISABLED_BoundsForRangeOnParentElement
798 #else
799 #define MAYBE_BoundsForRangeOnParentElement BoundsForRangeOnParentElement
800 #endif
801 TEST(BrowserAccessibilityManagerTest, MAYBE_BoundsForRangeOnParentElement) {
802   ui::AXNodeData root;
803   root.id = 1;
804   root.role = ui::AX_ROLE_ROOT_WEB_AREA;
805   root.child_ids.push_back(2);
806
807   ui::AXNodeData div;
808   div.id = 2;
809   div.role = ui::AX_ROLE_DIV;
810   div.location = gfx::Rect(100, 100, 100, 20);
811   div.child_ids.push_back(3);
812   div.child_ids.push_back(4);
813   div.child_ids.push_back(5);
814
815   ui::AXNodeData static_text1;
816   static_text1.id = 3;
817   static_text1.SetValue("AB");
818   static_text1.role = ui::AX_ROLE_STATIC_TEXT;
819   static_text1.location = gfx::Rect(100, 100, 40, 20);
820   static_text1.child_ids.push_back(6);
821
822   ui::AXNodeData img;
823   img.id = 4;
824   img.role = ui::AX_ROLE_IMAGE;
825   img.location = gfx::Rect(140, 100, 20, 20);
826
827   ui::AXNodeData static_text2;
828   static_text2.id = 5;
829   static_text2.SetValue("CD");
830   static_text2.role = ui::AX_ROLE_STATIC_TEXT;
831   static_text2.location = gfx::Rect(160, 100, 40, 20);
832   static_text2.child_ids.push_back(7);
833
834   ui::AXNodeData inline_text1;
835   inline_text1.id = 6;
836   inline_text1.SetValue("AB");
837   inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
838   inline_text1.location = gfx::Rect(100, 100, 40, 20);
839   inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
840                                ui::AX_TEXT_DIRECTION_LR);
841   std::vector<int32> character_offsets1;
842   character_offsets1.push_back(20);  // 0
843   character_offsets1.push_back(40);  // 1
844   inline_text1.AddIntListAttribute(
845       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
846
847   ui::AXNodeData inline_text2;
848   inline_text2.id = 7;
849   inline_text2.SetValue("CD");
850   inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
851   inline_text2.location = gfx::Rect(160, 100, 40, 20);
852   inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
853                                ui::AX_TEXT_DIRECTION_LR);
854   std::vector<int32> character_offsets2;
855   character_offsets2.push_back(20);  // 0
856   character_offsets2.push_back(40);  // 1
857   inline_text2.AddIntListAttribute(
858       ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
859
860   scoped_ptr<BrowserAccessibilityManager> manager(
861       BrowserAccessibilityManager::Create(
862           root,
863           NULL,
864           new CountedBrowserAccessibilityFactory()));
865   manager->UpdateNodesForTesting(
866       div, static_text1, img, static_text2, inline_text1, inline_text2);
867
868   BrowserAccessibility* root_accessible = manager->GetRoot();
869
870   EXPECT_EQ(gfx::Rect(100, 100, 20, 20).ToString(),
871             root_accessible->GetLocalBoundsForRange(0, 1).ToString());
872
873   EXPECT_EQ(gfx::Rect(100, 100, 40, 20).ToString(),
874             root_accessible->GetLocalBoundsForRange(0, 2).ToString());
875
876   EXPECT_EQ(gfx::Rect(100, 100, 80, 20).ToString(),
877             root_accessible->GetLocalBoundsForRange(0, 3).ToString());
878
879   EXPECT_EQ(gfx::Rect(120, 100, 60, 20).ToString(),
880             root_accessible->GetLocalBoundsForRange(1, 2).ToString());
881
882   EXPECT_EQ(gfx::Rect(120, 100, 80, 20).ToString(),
883             root_accessible->GetLocalBoundsForRange(1, 3).ToString());
884
885   EXPECT_EQ(gfx::Rect(100, 100, 100, 20).ToString(),
886             root_accessible->GetLocalBoundsForRange(0, 4).ToString());
887 }
888
889 }  // namespace content