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.
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"
10 #include "content/browser/accessibility/browser_accessibility_win.h"
12 #include "content/common/accessibility_messages.h"
13 #include "testing/gtest/include/gtest/gtest.h"
18 // Subclass of BrowserAccessibility that counts the number of instances.
19 class CountedBrowserAccessibility : public BrowserAccessibility {
21 CountedBrowserAccessibility() {
23 native_ref_count_ = 1;
25 virtual ~CountedBrowserAccessibility() {
29 virtual void NativeAddReference() OVERRIDE {
33 virtual void NativeReleaseReference() OVERRIDE {
35 if (native_ref_count_ == 0)
39 int native_ref_count_;
40 static int global_obj_count_;
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];
52 int CountedBrowserAccessibility::global_obj_count_ = 0;
54 // Factory that creates a CountedBrowserAccessibility.
55 class CountedBrowserAccessibilityFactory
56 : public BrowserAccessibilityFactory {
58 virtual ~CountedBrowserAccessibilityFactory() {}
59 virtual BrowserAccessibility* Create() OVERRIDE {
60 return new CountedBrowserAccessibility();
64 class TestBrowserAccessibilityDelegate
65 : public BrowserAccessibilityDelegate {
67 TestBrowserAccessibilityDelegate()
68 : got_fatal_error_(false) {}
70 virtual void AccessibilitySetFocus(int acc_obj_id) OVERRIDE {}
71 virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE {}
72 virtual void AccessibilityShowMenu(int acc_obj_id) OVERRIDE {}
73 virtual void AccessibilityScrollToMakeVisible(
74 int acc_obj_id, gfx::Rect subfocus) OVERRIDE {}
75 virtual void AccessibilityScrollToPoint(
76 int acc_obj_id, gfx::Point point) OVERRIDE {}
77 virtual void AccessibilitySetTextSelection(
78 int acc_obj_id, int start_offset, int end_offset) OVERRIDE {}
79 virtual bool AccessibilityViewHasFocus() const OVERRIDE {
82 virtual gfx::Rect AccessibilityGetViewBounds() const OVERRIDE {
85 virtual gfx::Point AccessibilityOriginInScreen(
86 const gfx::Rect& bounds) const OVERRIDE {
89 virtual void AccessibilityFatalError() OVERRIDE {
90 got_fatal_error_ = true;
93 bool got_fatal_error() const { return got_fatal_error_; }
94 void reset_got_fatal_error() { got_fatal_error_ = false; }
97 bool got_fatal_error_;
100 } // anonymous namespace
102 TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
103 // Create ui::AXNodeData objects for a simple document tree,
104 // representing the accessibility information used to initialize
105 // BrowserAccessibilityManager.
106 ui::AXNodeData button;
108 button.SetName("Button");
109 button.role = ui::AX_ROLE_BUTTON;
112 ui::AXNodeData checkbox;
114 checkbox.SetName("Checkbox");
115 checkbox.role = ui::AX_ROLE_CHECK_BOX;
120 root.SetName("Document");
121 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
123 root.child_ids.push_back(2);
124 root.child_ids.push_back(3);
126 // Construct a BrowserAccessibilityManager with this
127 // ui::AXNodeData tree and a factory for an instance-counting
128 // BrowserAccessibility, and ensure that exactly 3 instances were
129 // created. Note that the manager takes ownership of the factory.
130 CountedBrowserAccessibility::global_obj_count_ = 0;
131 BrowserAccessibilityManager* manager =
132 BrowserAccessibilityManager::Create(
133 MakeAXTreeUpdate(root, button, checkbox),
135 new CountedBrowserAccessibilityFactory());
137 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
139 // Delete the manager and test that all 3 instances are deleted.
141 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
143 // Construct a manager again, and this time save references to two of
144 // the three nodes in the tree.
146 BrowserAccessibilityManager::Create(
147 MakeAXTreeUpdate(root, button, checkbox),
149 new CountedBrowserAccessibilityFactory());
150 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
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();
160 // Now delete the manager, and only one of the three nodes in the tree
161 // should be released.
163 ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_);
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_);
173 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
174 // Make sure that changes to a subtree reuse as many objects as possible.
183 ui::AXNodeData tree1_child1;
185 tree1_child1.SetName("Child1");
186 tree1_child1.role = ui::AX_ROLE_BUTTON;
187 tree1_child1.state = 0;
189 ui::AXNodeData tree1_child2;
191 tree1_child2.SetName("Child2");
192 tree1_child2.role = ui::AX_ROLE_BUTTON;
193 tree1_child2.state = 0;
195 ui::AXNodeData tree1_child3;
197 tree1_child3.SetName("Child3");
198 tree1_child3.role = ui::AX_ROLE_BUTTON;
199 tree1_child3.state = 0;
201 ui::AXNodeData tree1_root;
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);
213 // child0 <-- inserted
216 // <-- child3 deleted
218 ui::AXNodeData tree2_child0;
220 tree2_child0.SetName("Child0");
221 tree2_child0.role = ui::AX_ROLE_BUTTON;
222 tree2_child0.state = 0;
224 ui::AXNodeData tree2_root;
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);
233 // Construct a BrowserAccessibilityManager with tree1.
234 CountedBrowserAccessibility::global_obj_count_ = 0;
235 BrowserAccessibilityManager* manager =
236 BrowserAccessibilityManager::Create(
237 MakeAXTreeUpdate(tree1_root,
238 tree1_child1, tree1_child2, tree1_child3),
240 new CountedBrowserAccessibilityFactory());
241 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
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();
260 // Check the index in parent.
261 EXPECT_EQ(0, child1_accessible->GetIndexInParent());
262 EXPECT_EQ(1, child2_accessible->GetIndexInParent());
263 EXPECT_EQ(2, child3_accessible->GetIndexInParent());
265 // Process a notification containing the changed subtree.
266 std::vector<AccessibilityHostMsg_EventParams> params;
267 params.push_back(AccessibilityHostMsg_EventParams());
268 AccessibilityHostMsg_EventParams* msg = ¶ms[0];
269 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
270 msg->update.nodes.push_back(tree2_root);
271 msg->update.nodes.push_back(tree2_child0);
272 msg->id = tree2_root.id;
273 manager->OnAccessibilityEvents(params);
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_);
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());
286 // Check that the index in parent has been updated.
287 EXPECT_EQ(1, child1_accessible->GetIndexInParent());
288 EXPECT_EQ(2, child2_accessible->GetIndexInParent());
290 // Release our references. The object count should only decrease by 1
292 root_accessible->NativeReleaseReference();
293 child1_accessible->NativeReleaseReference();
294 child2_accessible->NativeReleaseReference();
295 child3_accessible->NativeReleaseReference();
297 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
299 // Delete the manager and make sure all memory is cleaned up.
301 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
304 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
305 // Similar to the test above, but with a more complicated tree.
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;
324 ui::AXNodeData tree1_child1;
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);
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;
337 ui::AXNodeData tree1_child2;
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);
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;
350 ui::AXNodeData tree1_child3;
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);
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);
366 ui::AXNodeData tree1_root;
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);
377 // child0 <-- inserted
383 // <-- child3 (and grandchild3) deleted
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;
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);
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);
407 // Construct a BrowserAccessibilityManager with tree1.
408 CountedBrowserAccessibility::global_obj_count_ = 0;
409 BrowserAccessibilityManager* manager =
410 BrowserAccessibilityManager::Create(
411 MakeAXTreeUpdate(tree1_root, tree1_container,
412 tree1_child1, tree1_grandchild1,
413 tree1_child2, tree1_grandchild2,
414 tree1_child3, tree1_grandchild3),
416 new CountedBrowserAccessibilityFactory());
417 ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
419 // Save references to some objects.
420 CountedBrowserAccessibility* root_accessible =
421 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
422 root_accessible->NativeAddReference();
423 CountedBrowserAccessibility* container_accessible =
424 static_cast<CountedBrowserAccessibility*>(
425 root_accessible->PlatformGetChild(0));
426 container_accessible->NativeAddReference();
427 CountedBrowserAccessibility* child2_accessible =
428 static_cast<CountedBrowserAccessibility*>(
429 container_accessible->PlatformGetChild(1));
430 child2_accessible->NativeAddReference();
431 CountedBrowserAccessibility* child3_accessible =
432 static_cast<CountedBrowserAccessibility*>(
433 container_accessible->PlatformGetChild(2));
434 child3_accessible->NativeAddReference();
436 // Check the index in parent.
437 EXPECT_EQ(1, child2_accessible->GetIndexInParent());
438 EXPECT_EQ(2, child3_accessible->GetIndexInParent());
440 // Process a notification containing the changed subtree rooted at
442 std::vector<AccessibilityHostMsg_EventParams> params;
443 params.push_back(AccessibilityHostMsg_EventParams());
444 AccessibilityHostMsg_EventParams* msg = ¶ms[0];
445 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
446 msg->update.nodes.push_back(tree2_container);
447 msg->update.nodes.push_back(tree2_child0);
448 msg->update.nodes.push_back(tree2_grandchild0);
449 msg->id = tree2_container.id;
450 manager->OnAccessibilityEvents(params);
452 // There should be 9 objects now: the 8 from the new tree, plus the
453 // reference to child3 we kept.
454 EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_);
456 // Check that our references to the root and container and child2 are
457 // still valid, but that the reference to child3 is now invalid.
458 EXPECT_TRUE(root_accessible->instance_active());
459 EXPECT_TRUE(container_accessible->instance_active());
460 EXPECT_TRUE(child2_accessible->instance_active());
461 EXPECT_FALSE(child3_accessible->instance_active());
463 // Ensure that we retain the parent of the detached subtree.
464 EXPECT_EQ(root_accessible, container_accessible->GetParent());
465 EXPECT_EQ(0, container_accessible->GetIndexInParent());
467 // Check that the index in parent has been updated.
468 EXPECT_EQ(2, child2_accessible->GetIndexInParent());
470 // Release our references. The object count should only decrease by 1
472 root_accessible->NativeReleaseReference();
473 container_accessible->NativeReleaseReference();
474 child2_accessible->NativeReleaseReference();
475 child3_accessible->NativeReleaseReference();
477 EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
479 // Delete the manager and make sure all memory is cleaned up.
481 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
484 TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
492 ui::AXNodeData tree1_4;
496 ui::AXNodeData tree1_3;
499 tree1_3.child_ids.push_back(4);
501 ui::AXNodeData tree1_2;
505 ui::AXNodeData tree1_1;
507 tree1_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
509 tree1_1.child_ids.push_back(2);
510 tree1_1.child_ids.push_back(3);
515 // 4 <-- moves up a level and gains child
519 ui::AXNodeData tree2_6;
523 ui::AXNodeData tree2_5;
527 ui::AXNodeData tree2_4;
530 tree2_4.child_ids.push_back(6);
532 ui::AXNodeData tree2_1;
535 tree2_1.child_ids.push_back(4);
536 tree2_1.child_ids.push_back(5);
538 // Construct a BrowserAccessibilityManager with tree1.
539 CountedBrowserAccessibility::global_obj_count_ = 0;
540 BrowserAccessibilityManager* manager =
541 BrowserAccessibilityManager::Create(
542 MakeAXTreeUpdate(tree1_1, tree1_2, tree1_3, tree1_4),
544 new CountedBrowserAccessibilityFactory());
545 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
547 // Process a notification containing the changed subtree.
548 std::vector<AccessibilityHostMsg_EventParams> params;
549 params.push_back(AccessibilityHostMsg_EventParams());
550 AccessibilityHostMsg_EventParams* msg = ¶ms[0];
551 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
552 msg->update.nodes.push_back(tree2_1);
553 msg->update.nodes.push_back(tree2_4);
554 msg->update.nodes.push_back(tree2_5);
555 msg->update.nodes.push_back(tree2_6);
556 msg->id = tree2_1.id;
557 manager->OnAccessibilityEvents(params);
559 // There should be 4 objects now.
560 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
562 // Delete the manager and make sure all memory is cleaned up.
564 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
567 TEST(BrowserAccessibilityManagerTest, TestFatalError) {
568 // Test that BrowserAccessibilityManager raises a fatal error
569 // (which will crash the renderer) if the same id is used in
570 // two places in the tree.
574 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
575 root.child_ids.push_back(2);
576 root.child_ids.push_back(2);
578 CountedBrowserAccessibilityFactory* factory =
579 new CountedBrowserAccessibilityFactory();
580 scoped_ptr<TestBrowserAccessibilityDelegate> delegate(
581 new TestBrowserAccessibilityDelegate());
582 scoped_ptr<BrowserAccessibilityManager> manager;
583 ASSERT_FALSE(delegate->got_fatal_error());
584 manager.reset(BrowserAccessibilityManager::Create(
585 MakeAXTreeUpdate(root),
588 ASSERT_TRUE(delegate->got_fatal_error());
590 ui::AXNodeData root2;
592 root2.role = ui::AX_ROLE_ROOT_WEB_AREA;
593 root2.child_ids.push_back(2);
594 root2.child_ids.push_back(3);
596 ui::AXNodeData child1;
598 child1.child_ids.push_back(4);
599 child1.child_ids.push_back(5);
601 ui::AXNodeData child2;
603 child2.child_ids.push_back(6);
604 child2.child_ids.push_back(5); // Duplicate
606 ui::AXNodeData grandchild4;
609 ui::AXNodeData grandchild5;
612 ui::AXNodeData grandchild6;
615 delegate->reset_got_fatal_error();
616 factory = new CountedBrowserAccessibilityFactory();
617 manager.reset(BrowserAccessibilityManager::Create(
618 MakeAXTreeUpdate(root2, child1, child2,
619 grandchild4, grandchild5, grandchild6),
622 ASSERT_TRUE(delegate->got_fatal_error());
625 TEST(BrowserAccessibilityManagerTest, BoundsForRange) {
628 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
630 ui::AXNodeData static_text;
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);
637 ui::AXNodeData inline_text1;
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);
656 ui::AXNodeData inline_text2;
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);
674 scoped_ptr<BrowserAccessibilityManager> manager(
675 BrowserAccessibilityManager::Create(
676 MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
678 new CountedBrowserAccessibilityFactory()));
680 BrowserAccessibility* root_accessible = manager->GetRoot();
681 BrowserAccessibility* static_text_accessible =
682 root_accessible->PlatformGetChild(0);
684 EXPECT_EQ(gfx::Rect(100, 100, 6, 9).ToString(),
685 static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
687 EXPECT_EQ(gfx::Rect(100, 100, 26, 9).ToString(),
688 static_text_accessible->GetLocalBoundsForRange(0, 5).ToString());
690 EXPECT_EQ(gfx::Rect(100, 109, 5, 9).ToString(),
691 static_text_accessible->GetLocalBoundsForRange(7, 1).ToString());
693 EXPECT_EQ(gfx::Rect(100, 109, 25, 9).ToString(),
694 static_text_accessible->GetLocalBoundsForRange(7, 5).ToString());
696 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
697 static_text_accessible->GetLocalBoundsForRange(5, 3).ToString());
699 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
700 static_text_accessible->GetLocalBoundsForRange(0, 13).ToString());
702 // Test range that's beyond the text.
703 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
704 static_text_accessible->GetLocalBoundsForRange(-1, 999).ToString());
706 // Test that we can call bounds for range on the parent element, too,
707 // and it still works.
708 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
709 root_accessible->GetLocalBoundsForRange(0, 13).ToString());
712 TEST(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) {
713 // In this example, we assume that the string "123abc" is rendered with
714 // "123" going left-to-right and "abc" going right-to-left. In other
715 // words, on-screen it would look like "123cba". This is possible to
716 // acheive if the source string had unicode control characters
717 // to switch directions. This test doesn't worry about how, though - it just
718 // tests that if something like that were to occur, GetLocalBoundsForRange
719 // returns the correct bounds for different ranges.
723 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
725 ui::AXNodeData static_text;
727 static_text.SetValue("123abc");
728 static_text.role = ui::AX_ROLE_STATIC_TEXT;
729 static_text.location = gfx::Rect(100, 100, 60, 20);
730 root.child_ids.push_back(2);
732 ui::AXNodeData inline_text1;
734 inline_text1.SetValue("123");
735 inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
736 inline_text1.location = gfx::Rect(100, 100, 30, 20);
737 inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
738 ui::AX_TEXT_DIRECTION_LR);
739 std::vector<int32> character_offsets1;
740 character_offsets1.push_back(10); // 0
741 character_offsets1.push_back(20); // 1
742 character_offsets1.push_back(30); // 2
743 inline_text1.AddIntListAttribute(
744 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
745 static_text.child_ids.push_back(3);
747 ui::AXNodeData inline_text2;
749 inline_text2.SetValue("abc");
750 inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
751 inline_text2.location = gfx::Rect(130, 100, 30, 20);
752 inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
753 ui::AX_TEXT_DIRECTION_RL);
754 std::vector<int32> character_offsets2;
755 character_offsets2.push_back(10);
756 character_offsets2.push_back(20);
757 character_offsets2.push_back(30);
758 inline_text2.AddIntListAttribute(
759 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
760 static_text.child_ids.push_back(4);
762 scoped_ptr<BrowserAccessibilityManager> manager(
763 BrowserAccessibilityManager::Create(
764 MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
766 new CountedBrowserAccessibilityFactory()));
768 BrowserAccessibility* root_accessible = manager->GetRoot();
769 BrowserAccessibility* static_text_accessible =
770 root_accessible->PlatformGetChild(0);
772 EXPECT_EQ(gfx::Rect(100, 100, 60, 20).ToString(),
773 static_text_accessible->GetLocalBoundsForRange(0, 6).ToString());
775 EXPECT_EQ(gfx::Rect(100, 100, 10, 20).ToString(),
776 static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
778 EXPECT_EQ(gfx::Rect(100, 100, 30, 20).ToString(),
779 static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
781 EXPECT_EQ(gfx::Rect(150, 100, 10, 20).ToString(),
782 static_text_accessible->GetLocalBoundsForRange(3, 1).ToString());
784 EXPECT_EQ(gfx::Rect(130, 100, 30, 20).ToString(),
785 static_text_accessible->GetLocalBoundsForRange(3, 3).ToString());
787 // This range is only two characters, but because of the direction switch
788 // the bounds are as wide as four characters.
789 EXPECT_EQ(gfx::Rect(120, 100, 40, 20).ToString(),
790 static_text_accessible->GetLocalBoundsForRange(2, 2).ToString());
794 #define MAYBE_BoundsForRangeOnParentElement \
795 DISABLED_BoundsForRangeOnParentElement
797 #define MAYBE_BoundsForRangeOnParentElement BoundsForRangeOnParentElement
799 TEST(BrowserAccessibilityManagerTest, MAYBE_BoundsForRangeOnParentElement) {
802 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
803 root.child_ids.push_back(2);
807 div.role = ui::AX_ROLE_DIV;
808 div.location = gfx::Rect(100, 100, 100, 20);
809 div.child_ids.push_back(3);
810 div.child_ids.push_back(4);
811 div.child_ids.push_back(5);
813 ui::AXNodeData static_text1;
815 static_text1.SetValue("AB");
816 static_text1.role = ui::AX_ROLE_STATIC_TEXT;
817 static_text1.location = gfx::Rect(100, 100, 40, 20);
818 static_text1.child_ids.push_back(6);
822 img.role = ui::AX_ROLE_IMAGE;
823 img.location = gfx::Rect(140, 100, 20, 20);
825 ui::AXNodeData static_text2;
827 static_text2.SetValue("CD");
828 static_text2.role = ui::AX_ROLE_STATIC_TEXT;
829 static_text2.location = gfx::Rect(160, 100, 40, 20);
830 static_text2.child_ids.push_back(7);
832 ui::AXNodeData inline_text1;
834 inline_text1.SetValue("AB");
835 inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
836 inline_text1.location = gfx::Rect(100, 100, 40, 20);
837 inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
838 ui::AX_TEXT_DIRECTION_LR);
839 std::vector<int32> character_offsets1;
840 character_offsets1.push_back(20); // 0
841 character_offsets1.push_back(40); // 1
842 inline_text1.AddIntListAttribute(
843 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
845 ui::AXNodeData inline_text2;
847 inline_text2.SetValue("CD");
848 inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
849 inline_text2.location = gfx::Rect(160, 100, 40, 20);
850 inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
851 ui::AX_TEXT_DIRECTION_LR);
852 std::vector<int32> character_offsets2;
853 character_offsets2.push_back(20); // 0
854 character_offsets2.push_back(40); // 1
855 inline_text2.AddIntListAttribute(
856 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
858 scoped_ptr<BrowserAccessibilityManager> manager(
859 BrowserAccessibilityManager::Create(
861 root, div, static_text1, img,
862 static_text2, inline_text1, inline_text2),
864 new CountedBrowserAccessibilityFactory()));
865 BrowserAccessibility* root_accessible = manager->GetRoot();
867 EXPECT_EQ(gfx::Rect(100, 100, 20, 20).ToString(),
868 root_accessible->GetLocalBoundsForRange(0, 1).ToString());
870 EXPECT_EQ(gfx::Rect(100, 100, 40, 20).ToString(),
871 root_accessible->GetLocalBoundsForRange(0, 2).ToString());
873 EXPECT_EQ(gfx::Rect(100, 100, 80, 20).ToString(),
874 root_accessible->GetLocalBoundsForRange(0, 3).ToString());
876 EXPECT_EQ(gfx::Rect(120, 100, 60, 20).ToString(),
877 root_accessible->GetLocalBoundsForRange(1, 2).ToString());
879 EXPECT_EQ(gfx::Rect(120, 100, 80, 20).ToString(),
880 root_accessible->GetLocalBoundsForRange(1, 3).ToString());
882 EXPECT_EQ(gfx::Rect(100, 100, 100, 20).ToString(),
883 root_accessible->GetLocalBoundsForRange(0, 4).ToString());
886 TEST(BrowserAccessibilityManagerTest, NextPreviousInTreeOrder) {
889 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
891 ui::AXNodeData node2;
893 root.child_ids.push_back(2);
895 ui::AXNodeData node3;
897 root.child_ids.push_back(3);
899 ui::AXNodeData node4;
901 node3.child_ids.push_back(4);
903 ui::AXNodeData node5;
905 root.child_ids.push_back(5);
907 scoped_ptr<BrowserAccessibilityManager> manager(
908 BrowserAccessibilityManager::Create(
909 MakeAXTreeUpdate(root, node2, node3, node4, node5),
911 new CountedBrowserAccessibilityFactory()));
913 BrowserAccessibility* root_accessible = manager->GetRoot();
914 BrowserAccessibility* node2_accessible = root_accessible->PlatformGetChild(0);
915 BrowserAccessibility* node3_accessible = root_accessible->PlatformGetChild(1);
916 BrowserAccessibility* node4_accessible =
917 node3_accessible->PlatformGetChild(0);
918 BrowserAccessibility* node5_accessible = root_accessible->PlatformGetChild(2);
920 ASSERT_EQ(NULL, manager->NextInTreeOrder(NULL));
921 ASSERT_EQ(node2_accessible, manager->NextInTreeOrder(root_accessible));
922 ASSERT_EQ(node3_accessible, manager->NextInTreeOrder(node2_accessible));
923 ASSERT_EQ(node4_accessible, manager->NextInTreeOrder(node3_accessible));
924 ASSERT_EQ(node5_accessible, manager->NextInTreeOrder(node4_accessible));
925 ASSERT_EQ(NULL, manager->NextInTreeOrder(node5_accessible));
927 ASSERT_EQ(NULL, manager->PreviousInTreeOrder(NULL));
928 ASSERT_EQ(node4_accessible, manager->PreviousInTreeOrder(node5_accessible));
929 ASSERT_EQ(node3_accessible, manager->PreviousInTreeOrder(node4_accessible));
930 ASSERT_EQ(node2_accessible, manager->PreviousInTreeOrder(node3_accessible));
931 ASSERT_EQ(root_accessible, manager->PreviousInTreeOrder(node2_accessible));
934 } // namespace content