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 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
{
81 virtual gfx::Rect
GetViewBounds() const OVERRIDE
{
84 virtual gfx::Point
GetLastTouchEventLocation() const OVERRIDE
{
87 virtual void FatalAccessibilityTreeError() OVERRIDE
{
88 got_fatal_error_
= true;
91 bool got_fatal_error() const { return got_fatal_error_
; }
92 void reset_got_fatal_error() { got_fatal_error_
= false; }
95 bool got_fatal_error_
;
98 } // anonymous namespace
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
;
106 button
.SetName("Button");
107 button
.role
= ui::AX_ROLE_BUTTON
;
110 ui::AXNodeData checkbox
;
112 checkbox
.SetName("Checkbox");
113 checkbox
.role
= ui::AX_ROLE_CHECK_BOX
;
118 root
.SetName("Document");
119 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
121 root
.child_ids
.push_back(2);
122 root
.child_ids
.push_back(3);
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(
133 new CountedBrowserAccessibilityFactory());
134 manager
->UpdateNodesForTesting(button
, checkbox
);
136 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_
);
138 // Delete the manager and test that all 3 instances are deleted.
140 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
142 // Construct a manager again, and this time save references to two of
143 // the three nodes in the tree.
145 BrowserAccessibilityManager::Create(
148 new CountedBrowserAccessibilityFactory());
149 manager
->UpdateNodesForTesting(button
, checkbox
);
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(
239 new CountedBrowserAccessibilityFactory());
240 manager
->UpdateNodesForTesting(tree1_child1
, tree1_child2
, tree1_child3
);
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
->index_in_parent());
262 EXPECT_EQ(1, child2_accessible
->index_in_parent());
263 EXPECT_EQ(2, child3_accessible
->index_in_parent());
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
->nodes
.push_back(tree2_root
);
271 msg
->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
->index_in_parent());
288 EXPECT_EQ(2, child2_accessible
->index_in_parent());
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(
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_
);
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();
437 // Check the index in parent.
438 EXPECT_EQ(1, child2_accessible
->index_in_parent());
439 EXPECT_EQ(2, child3_accessible
->index_in_parent());
441 // Process a notification containing the changed subtree rooted at
443 std::vector
<AccessibilityHostMsg_EventParams
> params
;
444 params
.push_back(AccessibilityHostMsg_EventParams());
445 AccessibilityHostMsg_EventParams
* msg
= ¶ms
[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
);
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_
);
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());
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());
468 // Check that the index in parent has been updated.
469 EXPECT_EQ(2, child2_accessible
->index_in_parent());
471 // Release our references. The object count should only decrease by 1
473 root_accessible
->NativeReleaseReference();
474 container_accessible
->NativeReleaseReference();
475 child2_accessible
->NativeReleaseReference();
476 child3_accessible
->NativeReleaseReference();
478 EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_
);
480 // Delete the manager and make sure all memory is cleaned up.
482 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
485 TEST(BrowserAccessibilityManagerTest
, TestMoveChildUp
) {
493 ui::AXNodeData tree1_4
;
497 ui::AXNodeData tree1_3
;
500 tree1_3
.child_ids
.push_back(4);
502 ui::AXNodeData tree1_2
;
506 ui::AXNodeData tree1_1
;
508 tree1_1
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
510 tree1_1
.child_ids
.push_back(2);
511 tree1_1
.child_ids
.push_back(3);
516 // 4 <-- moves up a level and gains child
520 ui::AXNodeData tree2_6
;
524 ui::AXNodeData tree2_5
;
528 ui::AXNodeData tree2_4
;
531 tree2_4
.child_ids
.push_back(6);
533 ui::AXNodeData tree2_1
;
536 tree2_1
.child_ids
.push_back(4);
537 tree2_1
.child_ids
.push_back(5);
539 // Construct a BrowserAccessibilityManager with tree1.
540 CountedBrowserAccessibility::global_obj_count_
= 0;
541 BrowserAccessibilityManager
* manager
=
542 BrowserAccessibilityManager::Create(
545 new CountedBrowserAccessibilityFactory());
546 manager
->UpdateNodesForTesting(tree1_2
, tree1_3
, tree1_4
);
547 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_
);
549 // Process a notification containing the changed subtree.
550 std::vector
<AccessibilityHostMsg_EventParams
> params
;
551 params
.push_back(AccessibilityHostMsg_EventParams());
552 AccessibilityHostMsg_EventParams
* msg
= ¶ms
[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
);
561 // There should be 4 objects now.
562 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_
);
564 // Delete the manager and make sure all memory is cleaned up.
566 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
569 // Crashes on Windows. http://crbug.com/304130
571 #define MAYBE_TestFatalError DISABLED_TestFatalError
573 #define MAYBE_TestFatalError TestFatalError
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.
582 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
583 root
.child_ids
.push_back(2);
584 root
.child_ids
.push_back(2);
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(
596 ASSERT_TRUE(delegate
->got_fatal_error());
598 ui::AXNodeData root2
;
600 root2
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
601 root2
.child_ids
.push_back(2);
602 root2
.child_ids
.push_back(3);
604 ui::AXNodeData child1
;
606 child1
.child_ids
.push_back(4);
607 child1
.child_ids
.push_back(5);
609 ui::AXNodeData child2
;
611 child2
.child_ids
.push_back(6);
612 child2
.child_ids
.push_back(5); // Duplicate
614 delegate
->reset_got_fatal_error();
615 factory
= new CountedBrowserAccessibilityFactory();
616 manager
.reset(BrowserAccessibilityManager::Create(
620 ASSERT_FALSE(delegate
->got_fatal_error());
621 manager
->UpdateNodesForTesting(child1
, child2
);
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(
678 new CountedBrowserAccessibilityFactory()));
679 manager
->UpdateNodesForTesting(static_text
, inline_text1
, inline_text2
);
681 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
682 BrowserAccessibility
* static_text_accessible
=
683 root_accessible
->PlatformGetChild(0);
685 EXPECT_EQ(gfx::Rect(100, 100, 6, 9).ToString(),
686 static_text_accessible
->GetLocalBoundsForRange(0, 1).ToString());
688 EXPECT_EQ(gfx::Rect(100, 100, 26, 9).ToString(),
689 static_text_accessible
->GetLocalBoundsForRange(0, 5).ToString());
691 EXPECT_EQ(gfx::Rect(100, 109, 5, 9).ToString(),
692 static_text_accessible
->GetLocalBoundsForRange(7, 1).ToString());
694 EXPECT_EQ(gfx::Rect(100, 109, 25, 9).ToString(),
695 static_text_accessible
->GetLocalBoundsForRange(7, 5).ToString());
697 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
698 static_text_accessible
->GetLocalBoundsForRange(5, 3).ToString());
700 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
701 static_text_accessible
->GetLocalBoundsForRange(0, 13).ToString());
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());
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());
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.
724 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
726 ui::AXNodeData static_text
;
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);
733 ui::AXNodeData inline_text1
;
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);
748 ui::AXNodeData inline_text2
;
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);
763 scoped_ptr
<BrowserAccessibilityManager
> manager(
764 BrowserAccessibilityManager::Create(
767 new CountedBrowserAccessibilityFactory()));
768 manager
->UpdateNodesForTesting(static_text
, inline_text1
, inline_text2
);
770 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
771 BrowserAccessibility
* static_text_accessible
=
772 root_accessible
->PlatformGetChild(0);
774 EXPECT_EQ(gfx::Rect(100, 100, 60, 20).ToString(),
775 static_text_accessible
->GetLocalBoundsForRange(0, 6).ToString());
777 EXPECT_EQ(gfx::Rect(100, 100, 10, 20).ToString(),
778 static_text_accessible
->GetLocalBoundsForRange(0, 1).ToString());
780 EXPECT_EQ(gfx::Rect(100, 100, 30, 20).ToString(),
781 static_text_accessible
->GetLocalBoundsForRange(0, 3).ToString());
783 EXPECT_EQ(gfx::Rect(150, 100, 10, 20).ToString(),
784 static_text_accessible
->GetLocalBoundsForRange(3, 1).ToString());
786 EXPECT_EQ(gfx::Rect(130, 100, 30, 20).ToString(),
787 static_text_accessible
->GetLocalBoundsForRange(3, 3).ToString());
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());
796 #define MAYBE_BoundsForRangeOnParentElement \
797 DISABLED_BoundsForRangeOnParentElement
799 #define MAYBE_BoundsForRangeOnParentElement BoundsForRangeOnParentElement
801 TEST(BrowserAccessibilityManagerTest
, MAYBE_BoundsForRangeOnParentElement
) {
804 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
805 root
.child_ids
.push_back(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);
815 ui::AXNodeData static_text1
;
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);
824 img
.role
= ui::AX_ROLE_IMAGE
;
825 img
.location
= gfx::Rect(140, 100, 20, 20);
827 ui::AXNodeData static_text2
;
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);
834 ui::AXNodeData inline_text1
;
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
);
847 ui::AXNodeData inline_text2
;
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
);
860 scoped_ptr
<BrowserAccessibilityManager
> manager(
861 BrowserAccessibilityManager::Create(
864 new CountedBrowserAccessibilityFactory()));
865 manager
->UpdateNodesForTesting(
866 div
, static_text1
, img
, static_text2
, inline_text1
, inline_text2
);
868 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
870 EXPECT_EQ(gfx::Rect(100, 100, 20, 20).ToString(),
871 root_accessible
->GetLocalBoundsForRange(0, 1).ToString());
873 EXPECT_EQ(gfx::Rect(100, 100, 40, 20).ToString(),
874 root_accessible
->GetLocalBoundsForRange(0, 2).ToString());
876 EXPECT_EQ(gfx::Rect(100, 100, 80, 20).ToString(),
877 root_accessible
->GetLocalBoundsForRange(0, 3).ToString());
879 EXPECT_EQ(gfx::Rect(120, 100, 60, 20).ToString(),
880 root_accessible
->GetLocalBoundsForRange(1, 2).ToString());
882 EXPECT_EQ(gfx::Rect(120, 100, 80, 20).ToString(),
883 root_accessible
->GetLocalBoundsForRange(1, 3).ToString());
885 EXPECT_EQ(gfx::Rect(100, 100, 100, 20).ToString(),
886 root_accessible
->GetLocalBoundsForRange(0, 4).ToString());
889 TEST(BrowserAccessibilityManagerTest
, NextPreviousInTreeOrder
) {
892 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
894 ui::AXNodeData node2
;
896 root
.child_ids
.push_back(2);
898 ui::AXNodeData node3
;
900 root
.child_ids
.push_back(3);
902 ui::AXNodeData node4
;
904 node3
.child_ids
.push_back(4);
906 ui::AXNodeData node5
;
908 root
.child_ids
.push_back(5);
910 scoped_ptr
<BrowserAccessibilityManager
> manager(
911 BrowserAccessibilityManager::Create(
914 new CountedBrowserAccessibilityFactory()));
915 manager
->UpdateNodesForTesting(node2
, node3
, node4
, node5
);
917 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
918 BrowserAccessibility
* node2_accessible
= root_accessible
->PlatformGetChild(0);
919 BrowserAccessibility
* node3_accessible
= root_accessible
->PlatformGetChild(1);
920 BrowserAccessibility
* node4_accessible
=
921 node3_accessible
->PlatformGetChild(0);
922 BrowserAccessibility
* node5_accessible
= root_accessible
->PlatformGetChild(2);
924 ASSERT_EQ(NULL
, manager
->NextInTreeOrder(NULL
));
925 ASSERT_EQ(node2_accessible
, manager
->NextInTreeOrder(root_accessible
));
926 ASSERT_EQ(node3_accessible
, manager
->NextInTreeOrder(node2_accessible
));
927 ASSERT_EQ(node4_accessible
, manager
->NextInTreeOrder(node3_accessible
));
928 ASSERT_EQ(node5_accessible
, manager
->NextInTreeOrder(node4_accessible
));
929 ASSERT_EQ(NULL
, manager
->NextInTreeOrder(node5_accessible
));
931 ASSERT_EQ(NULL
, manager
->PreviousInTreeOrder(NULL
));
932 ASSERT_EQ(node4_accessible
, manager
->PreviousInTreeOrder(node5_accessible
));
933 ASSERT_EQ(node3_accessible
, manager
->PreviousInTreeOrder(node4_accessible
));
934 ASSERT_EQ(node2_accessible
, manager
->PreviousInTreeOrder(node3_accessible
));
935 ASSERT_EQ(root_accessible
, manager
->PreviousInTreeOrder(node2_accessible
));
938 } // namespace content