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 ~CountedBrowserAccessibility() override
{ global_obj_count_
--; }
27 void NativeAddReference() override
{ native_ref_count_
++; }
29 void NativeReleaseReference() override
{
31 if (native_ref_count_
== 0)
35 int native_ref_count_
;
36 static int global_obj_count_
;
39 // Adds some padding to prevent a heap-buffer-overflow when an instance of
40 // this class is casted into a BrowserAccessibilityWin pointer.
41 // http://crbug.com/235508
42 // TODO(dmazzoni): Fix this properly.
43 static const size_t kDataSize
= sizeof(int) + sizeof(BrowserAccessibility
);
44 uint8 padding_
[sizeof(BrowserAccessibilityWin
) - kDataSize
];
48 int CountedBrowserAccessibility::global_obj_count_
= 0;
50 // Factory that creates a CountedBrowserAccessibility.
51 class CountedBrowserAccessibilityFactory
52 : public BrowserAccessibilityFactory
{
54 ~CountedBrowserAccessibilityFactory() override
{}
55 BrowserAccessibility
* Create() override
{
56 return new CountedBrowserAccessibility();
60 class TestBrowserAccessibilityDelegate
61 : public BrowserAccessibilityDelegate
{
63 TestBrowserAccessibilityDelegate()
64 : got_fatal_error_(false) {}
66 void AccessibilitySetFocus(int acc_obj_id
) override
{}
67 void AccessibilityDoDefaultAction(int acc_obj_id
) override
{}
68 void AccessibilityShowMenu(const gfx::Point
& point
) override
{}
69 void AccessibilityScrollToMakeVisible(int acc_obj_id
,
70 const gfx::Rect
& subfocus
) override
{}
71 void AccessibilityScrollToPoint(int acc_obj_id
,
72 const gfx::Point
& point
) override
{}
73 void AccessibilitySetTextSelection(int acc_obj_id
,
75 int end_offset
) override
{}
76 void AccessibilitySetValue(int acc_obj_id
, const base::string16
& value
)
78 bool AccessibilityViewHasFocus() const override
{ return false; }
79 gfx::Rect
AccessibilityGetViewBounds() const override
{ return gfx::Rect(); }
80 gfx::Point
AccessibilityOriginInScreen(
81 const gfx::Rect
& bounds
) const override
{
84 void AccessibilityHitTest(const gfx::Point
& point
) override
{}
85 void AccessibilityFatalError() override
{ got_fatal_error_
= true; }
86 gfx::AcceleratedWidget
AccessibilityGetAcceleratedWidget() override
{
87 return gfx::kNullAcceleratedWidget
;
89 gfx::NativeViewAccessible
AccessibilityGetNativeViewAccessible() override
{
92 BrowserAccessibilityManager
* AccessibilityGetChildFrame(
93 int accessibility_node_id
) override
{
96 BrowserAccessibility
* AccessibilityGetParentFrame() override
{ return NULL
; }
98 bool got_fatal_error() const { return got_fatal_error_
; }
99 void reset_got_fatal_error() { got_fatal_error_
= false; }
102 bool got_fatal_error_
;
105 } // anonymous namespace
107 TEST(BrowserAccessibilityManagerTest
, TestNoLeaks
) {
108 // Create ui::AXNodeData objects for a simple document tree,
109 // representing the accessibility information used to initialize
110 // BrowserAccessibilityManager.
111 ui::AXNodeData button
;
113 button
.SetName("Button");
114 button
.role
= ui::AX_ROLE_BUTTON
;
117 ui::AXNodeData checkbox
;
119 checkbox
.SetName("Checkbox");
120 checkbox
.role
= ui::AX_ROLE_CHECK_BOX
;
125 root
.SetName("Document");
126 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
128 root
.child_ids
.push_back(2);
129 root
.child_ids
.push_back(3);
131 // Construct a BrowserAccessibilityManager with this
132 // ui::AXNodeData tree and a factory for an instance-counting
133 // BrowserAccessibility, and ensure that exactly 3 instances were
134 // created. Note that the manager takes ownership of the factory.
135 CountedBrowserAccessibility::global_obj_count_
= 0;
136 BrowserAccessibilityManager
* manager
=
137 BrowserAccessibilityManager::Create(
138 MakeAXTreeUpdate(root
, button
, checkbox
),
140 new CountedBrowserAccessibilityFactory());
142 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_
);
144 // Delete the manager and test that all 3 instances are deleted.
146 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
148 // Construct a manager again, and this time save references to two of
149 // the three nodes in the tree.
151 BrowserAccessibilityManager::Create(
152 MakeAXTreeUpdate(root
, button
, checkbox
),
154 new CountedBrowserAccessibilityFactory());
155 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_
);
157 CountedBrowserAccessibility
* root_accessible
=
158 static_cast<CountedBrowserAccessibility
*>(manager
->GetRoot());
159 root_accessible
->NativeAddReference();
160 CountedBrowserAccessibility
* child1_accessible
=
161 static_cast<CountedBrowserAccessibility
*>(
162 root_accessible
->PlatformGetChild(1));
163 child1_accessible
->NativeAddReference();
165 // Now delete the manager, and only one of the three nodes in the tree
166 // should be released.
168 ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_
);
170 // Release each of our references and make sure that each one results in
171 // the instance being deleted as its reference count hits zero.
172 root_accessible
->NativeReleaseReference();
173 ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_
);
174 child1_accessible
->NativeReleaseReference();
175 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
178 TEST(BrowserAccessibilityManagerTest
, TestReuseBrowserAccessibilityObjects
) {
179 // Make sure that changes to a subtree reuse as many objects as possible.
188 ui::AXNodeData tree1_child1
;
190 tree1_child1
.SetName("Child1");
191 tree1_child1
.role
= ui::AX_ROLE_BUTTON
;
192 tree1_child1
.state
= 0;
194 ui::AXNodeData tree1_child2
;
196 tree1_child2
.SetName("Child2");
197 tree1_child2
.role
= ui::AX_ROLE_BUTTON
;
198 tree1_child2
.state
= 0;
200 ui::AXNodeData tree1_child3
;
202 tree1_child3
.SetName("Child3");
203 tree1_child3
.role
= ui::AX_ROLE_BUTTON
;
204 tree1_child3
.state
= 0;
206 ui::AXNodeData tree1_root
;
208 tree1_root
.SetName("Document");
209 tree1_root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
210 tree1_root
.state
= 0;
211 tree1_root
.child_ids
.push_back(2);
212 tree1_root
.child_ids
.push_back(3);
213 tree1_root
.child_ids
.push_back(4);
218 // child0 <-- inserted
221 // <-- child3 deleted
223 ui::AXNodeData tree2_child0
;
225 tree2_child0
.SetName("Child0");
226 tree2_child0
.role
= ui::AX_ROLE_BUTTON
;
227 tree2_child0
.state
= 0;
229 ui::AXNodeData tree2_root
;
231 tree2_root
.SetName("DocumentChanged");
232 tree2_root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
233 tree2_root
.state
= 0;
234 tree2_root
.child_ids
.push_back(5);
235 tree2_root
.child_ids
.push_back(2);
236 tree2_root
.child_ids
.push_back(3);
238 // Construct a BrowserAccessibilityManager with tree1.
239 CountedBrowserAccessibility::global_obj_count_
= 0;
240 BrowserAccessibilityManager
* manager
=
241 BrowserAccessibilityManager::Create(
242 MakeAXTreeUpdate(tree1_root
,
243 tree1_child1
, tree1_child2
, tree1_child3
),
245 new CountedBrowserAccessibilityFactory());
246 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_
);
248 // Save references to all of the objects.
249 CountedBrowserAccessibility
* root_accessible
=
250 static_cast<CountedBrowserAccessibility
*>(manager
->GetRoot());
251 root_accessible
->NativeAddReference();
252 CountedBrowserAccessibility
* child1_accessible
=
253 static_cast<CountedBrowserAccessibility
*>(
254 root_accessible
->PlatformGetChild(0));
255 child1_accessible
->NativeAddReference();
256 CountedBrowserAccessibility
* child2_accessible
=
257 static_cast<CountedBrowserAccessibility
*>(
258 root_accessible
->PlatformGetChild(1));
259 child2_accessible
->NativeAddReference();
260 CountedBrowserAccessibility
* child3_accessible
=
261 static_cast<CountedBrowserAccessibility
*>(
262 root_accessible
->PlatformGetChild(2));
263 child3_accessible
->NativeAddReference();
265 // Check the index in parent.
266 EXPECT_EQ(0, child1_accessible
->GetIndexInParent());
267 EXPECT_EQ(1, child2_accessible
->GetIndexInParent());
268 EXPECT_EQ(2, child3_accessible
->GetIndexInParent());
270 // Process a notification containing the changed subtree.
271 std::vector
<AccessibilityHostMsg_EventParams
> params
;
272 params
.push_back(AccessibilityHostMsg_EventParams());
273 AccessibilityHostMsg_EventParams
* msg
= ¶ms
[0];
274 msg
->event_type
= ui::AX_EVENT_CHILDREN_CHANGED
;
275 msg
->update
.nodes
.push_back(tree2_root
);
276 msg
->update
.nodes
.push_back(tree2_child0
);
277 msg
->id
= tree2_root
.id
;
278 manager
->OnAccessibilityEvents(params
);
280 // There should be 5 objects now: the 4 from the new tree, plus the
281 // reference to child3 we kept.
282 EXPECT_EQ(5, CountedBrowserAccessibility::global_obj_count_
);
284 // Check that our references to the root, child1, and child2 are still valid,
285 // but that the reference to child3 is now invalid.
286 EXPECT_TRUE(root_accessible
->instance_active());
287 EXPECT_TRUE(child1_accessible
->instance_active());
288 EXPECT_TRUE(child2_accessible
->instance_active());
289 EXPECT_FALSE(child3_accessible
->instance_active());
291 // Check that the index in parent has been updated.
292 EXPECT_EQ(1, child1_accessible
->GetIndexInParent());
293 EXPECT_EQ(2, child2_accessible
->GetIndexInParent());
295 // Release our references. The object count should only decrease by 1
297 root_accessible
->NativeReleaseReference();
298 child1_accessible
->NativeReleaseReference();
299 child2_accessible
->NativeReleaseReference();
300 child3_accessible
->NativeReleaseReference();
302 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_
);
304 // Delete the manager and make sure all memory is cleaned up.
306 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
309 TEST(BrowserAccessibilityManagerTest
, TestReuseBrowserAccessibilityObjects2
) {
310 // Similar to the test above, but with a more complicated tree.
323 ui::AXNodeData tree1_grandchild1
;
324 tree1_grandchild1
.id
= 4;
325 tree1_grandchild1
.SetName("GrandChild1");
326 tree1_grandchild1
.role
= ui::AX_ROLE_BUTTON
;
327 tree1_grandchild1
.state
= 0;
329 ui::AXNodeData tree1_child1
;
331 tree1_child1
.SetName("Child1");
332 tree1_child1
.role
= ui::AX_ROLE_BUTTON
;
333 tree1_child1
.state
= 0;
334 tree1_child1
.child_ids
.push_back(4);
336 ui::AXNodeData tree1_grandchild2
;
337 tree1_grandchild2
.id
= 6;
338 tree1_grandchild2
.SetName("GrandChild1");
339 tree1_grandchild2
.role
= ui::AX_ROLE_BUTTON
;
340 tree1_grandchild2
.state
= 0;
342 ui::AXNodeData tree1_child2
;
344 tree1_child2
.SetName("Child2");
345 tree1_child2
.role
= ui::AX_ROLE_BUTTON
;
346 tree1_child2
.state
= 0;
347 tree1_child2
.child_ids
.push_back(6);
349 ui::AXNodeData tree1_grandchild3
;
350 tree1_grandchild3
.id
= 8;
351 tree1_grandchild3
.SetName("GrandChild3");
352 tree1_grandchild3
.role
= ui::AX_ROLE_BUTTON
;
353 tree1_grandchild3
.state
= 0;
355 ui::AXNodeData tree1_child3
;
357 tree1_child3
.SetName("Child3");
358 tree1_child3
.role
= ui::AX_ROLE_BUTTON
;
359 tree1_child3
.state
= 0;
360 tree1_child3
.child_ids
.push_back(8);
362 ui::AXNodeData tree1_container
;
363 tree1_container
.id
= 2;
364 tree1_container
.SetName("Container");
365 tree1_container
.role
= ui::AX_ROLE_GROUP
;
366 tree1_container
.state
= 0;
367 tree1_container
.child_ids
.push_back(3);
368 tree1_container
.child_ids
.push_back(5);
369 tree1_container
.child_ids
.push_back(7);
371 ui::AXNodeData tree1_root
;
373 tree1_root
.SetName("Document");
374 tree1_root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
375 tree1_root
.state
= 0;
376 tree1_root
.child_ids
.push_back(2);
382 // child0 <-- inserted
388 // <-- child3 (and grandchild3) deleted
390 ui::AXNodeData tree2_grandchild0
;
391 tree2_grandchild0
.id
= 9;
392 tree2_grandchild0
.SetName("GrandChild0");
393 tree2_grandchild0
.role
= ui::AX_ROLE_BUTTON
;
394 tree2_grandchild0
.state
= 0;
396 ui::AXNodeData tree2_child0
;
397 tree2_child0
.id
= 10;
398 tree2_child0
.SetName("Child0");
399 tree2_child0
.role
= ui::AX_ROLE_BUTTON
;
400 tree2_child0
.state
= 0;
401 tree2_child0
.child_ids
.push_back(9);
403 ui::AXNodeData tree2_container
;
404 tree2_container
.id
= 2;
405 tree2_container
.SetName("Container");
406 tree2_container
.role
= ui::AX_ROLE_GROUP
;
407 tree2_container
.state
= 0;
408 tree2_container
.child_ids
.push_back(10);
409 tree2_container
.child_ids
.push_back(3);
410 tree2_container
.child_ids
.push_back(5);
412 // Construct a BrowserAccessibilityManager with tree1.
413 CountedBrowserAccessibility::global_obj_count_
= 0;
414 BrowserAccessibilityManager
* manager
=
415 BrowserAccessibilityManager::Create(
416 MakeAXTreeUpdate(tree1_root
, tree1_container
,
417 tree1_child1
, tree1_grandchild1
,
418 tree1_child2
, tree1_grandchild2
,
419 tree1_child3
, tree1_grandchild3
),
421 new CountedBrowserAccessibilityFactory());
422 ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_
);
424 // Save references to some objects.
425 CountedBrowserAccessibility
* root_accessible
=
426 static_cast<CountedBrowserAccessibility
*>(manager
->GetRoot());
427 root_accessible
->NativeAddReference();
428 CountedBrowserAccessibility
* container_accessible
=
429 static_cast<CountedBrowserAccessibility
*>(
430 root_accessible
->PlatformGetChild(0));
431 container_accessible
->NativeAddReference();
432 CountedBrowserAccessibility
* child2_accessible
=
433 static_cast<CountedBrowserAccessibility
*>(
434 container_accessible
->PlatformGetChild(1));
435 child2_accessible
->NativeAddReference();
436 CountedBrowserAccessibility
* child3_accessible
=
437 static_cast<CountedBrowserAccessibility
*>(
438 container_accessible
->PlatformGetChild(2));
439 child3_accessible
->NativeAddReference();
441 // Check the index in parent.
442 EXPECT_EQ(1, child2_accessible
->GetIndexInParent());
443 EXPECT_EQ(2, child3_accessible
->GetIndexInParent());
445 // Process a notification containing the changed subtree rooted at
447 std::vector
<AccessibilityHostMsg_EventParams
> params
;
448 params
.push_back(AccessibilityHostMsg_EventParams());
449 AccessibilityHostMsg_EventParams
* msg
= ¶ms
[0];
450 msg
->event_type
= ui::AX_EVENT_CHILDREN_CHANGED
;
451 msg
->update
.nodes
.push_back(tree2_container
);
452 msg
->update
.nodes
.push_back(tree2_child0
);
453 msg
->update
.nodes
.push_back(tree2_grandchild0
);
454 msg
->id
= tree2_container
.id
;
455 manager
->OnAccessibilityEvents(params
);
457 // There should be 9 objects now: the 8 from the new tree, plus the
458 // reference to child3 we kept.
459 EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_
);
461 // Check that our references to the root and container and child2 are
462 // still valid, but that the reference to child3 is now invalid.
463 EXPECT_TRUE(root_accessible
->instance_active());
464 EXPECT_TRUE(container_accessible
->instance_active());
465 EXPECT_TRUE(child2_accessible
->instance_active());
466 EXPECT_FALSE(child3_accessible
->instance_active());
468 // Ensure that we retain the parent of the detached subtree.
469 EXPECT_EQ(root_accessible
, container_accessible
->GetParent());
470 EXPECT_EQ(0, container_accessible
->GetIndexInParent());
472 // Check that the index in parent has been updated.
473 EXPECT_EQ(2, child2_accessible
->GetIndexInParent());
475 // Release our references. The object count should only decrease by 1
477 root_accessible
->NativeReleaseReference();
478 container_accessible
->NativeReleaseReference();
479 child2_accessible
->NativeReleaseReference();
480 child3_accessible
->NativeReleaseReference();
482 EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_
);
484 // Delete the manager and make sure all memory is cleaned up.
486 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
489 TEST(BrowserAccessibilityManagerTest
, TestMoveChildUp
) {
497 ui::AXNodeData tree1_4
;
501 ui::AXNodeData tree1_3
;
504 tree1_3
.child_ids
.push_back(4);
506 ui::AXNodeData tree1_2
;
510 ui::AXNodeData tree1_1
;
512 tree1_1
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
514 tree1_1
.child_ids
.push_back(2);
515 tree1_1
.child_ids
.push_back(3);
520 // 4 <-- moves up a level and gains child
524 ui::AXNodeData tree2_6
;
528 ui::AXNodeData tree2_5
;
532 ui::AXNodeData tree2_4
;
535 tree2_4
.child_ids
.push_back(6);
537 ui::AXNodeData tree2_1
;
540 tree2_1
.child_ids
.push_back(4);
541 tree2_1
.child_ids
.push_back(5);
543 // Construct a BrowserAccessibilityManager with tree1.
544 CountedBrowserAccessibility::global_obj_count_
= 0;
545 BrowserAccessibilityManager
* manager
=
546 BrowserAccessibilityManager::Create(
547 MakeAXTreeUpdate(tree1_1
, tree1_2
, tree1_3
, tree1_4
),
549 new CountedBrowserAccessibilityFactory());
550 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_
);
552 // Process a notification containing the changed subtree.
553 std::vector
<AccessibilityHostMsg_EventParams
> params
;
554 params
.push_back(AccessibilityHostMsg_EventParams());
555 AccessibilityHostMsg_EventParams
* msg
= ¶ms
[0];
556 msg
->event_type
= ui::AX_EVENT_CHILDREN_CHANGED
;
557 msg
->update
.nodes
.push_back(tree2_1
);
558 msg
->update
.nodes
.push_back(tree2_4
);
559 msg
->update
.nodes
.push_back(tree2_5
);
560 msg
->update
.nodes
.push_back(tree2_6
);
561 msg
->id
= tree2_1
.id
;
562 manager
->OnAccessibilityEvents(params
);
564 // There should be 4 objects now.
565 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_
);
567 // Delete the manager and make sure all memory is cleaned up.
569 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
572 TEST(BrowserAccessibilityManagerTest
, TestFatalError
) {
573 // Test that BrowserAccessibilityManager raises a fatal error
574 // (which will crash the renderer) if the same id is used in
575 // two places in the tree.
579 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
580 root
.child_ids
.push_back(2);
581 root
.child_ids
.push_back(2);
583 CountedBrowserAccessibilityFactory
* factory
=
584 new CountedBrowserAccessibilityFactory();
585 scoped_ptr
<TestBrowserAccessibilityDelegate
> delegate(
586 new TestBrowserAccessibilityDelegate());
587 scoped_ptr
<BrowserAccessibilityManager
> manager
;
588 ASSERT_FALSE(delegate
->got_fatal_error());
589 manager
.reset(BrowserAccessibilityManager::Create(
590 MakeAXTreeUpdate(root
),
593 ASSERT_TRUE(delegate
->got_fatal_error());
595 ui::AXNodeData root2
;
597 root2
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
598 root2
.child_ids
.push_back(2);
599 root2
.child_ids
.push_back(3);
601 ui::AXNodeData child1
;
603 child1
.child_ids
.push_back(4);
604 child1
.child_ids
.push_back(5);
606 ui::AXNodeData child2
;
608 child2
.child_ids
.push_back(6);
609 child2
.child_ids
.push_back(5); // Duplicate
611 ui::AXNodeData grandchild4
;
614 ui::AXNodeData grandchild5
;
617 ui::AXNodeData grandchild6
;
620 delegate
->reset_got_fatal_error();
621 factory
= new CountedBrowserAccessibilityFactory();
622 manager
.reset(BrowserAccessibilityManager::Create(
623 MakeAXTreeUpdate(root2
, child1
, child2
,
624 grandchild4
, grandchild5
, grandchild6
),
627 ASSERT_TRUE(delegate
->got_fatal_error());
630 TEST(BrowserAccessibilityManagerTest
, BoundsForRange
) {
633 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
635 ui::AXNodeData static_text
;
637 static_text
.SetValue("Hello, world.");
638 static_text
.role
= ui::AX_ROLE_STATIC_TEXT
;
639 static_text
.location
= gfx::Rect(100, 100, 29, 18);
640 root
.child_ids
.push_back(2);
642 ui::AXNodeData inline_text1
;
644 inline_text1
.SetValue("Hello, ");
645 inline_text1
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
646 inline_text1
.location
= gfx::Rect(100, 100, 29, 9);
647 inline_text1
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
648 ui::AX_TEXT_DIRECTION_LR
);
649 std::vector
<int32
> character_offsets1
;
650 character_offsets1
.push_back(6); // 0
651 character_offsets1
.push_back(11); // 1
652 character_offsets1
.push_back(16); // 2
653 character_offsets1
.push_back(21); // 3
654 character_offsets1
.push_back(26); // 4
655 character_offsets1
.push_back(29); // 5
656 character_offsets1
.push_back(29); // 6 (note that the space has no width)
657 inline_text1
.AddIntListAttribute(
658 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets1
);
659 static_text
.child_ids
.push_back(3);
661 ui::AXNodeData inline_text2
;
663 inline_text2
.SetValue("world.");
664 inline_text2
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
665 inline_text2
.location
= gfx::Rect(100, 109, 28, 9);
666 inline_text2
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
667 ui::AX_TEXT_DIRECTION_LR
);
668 std::vector
<int32
> character_offsets2
;
669 character_offsets2
.push_back(5);
670 character_offsets2
.push_back(10);
671 character_offsets2
.push_back(15);
672 character_offsets2
.push_back(20);
673 character_offsets2
.push_back(25);
674 character_offsets2
.push_back(28);
675 inline_text2
.AddIntListAttribute(
676 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets2
);
677 static_text
.child_ids
.push_back(4);
679 scoped_ptr
<BrowserAccessibilityManager
> manager(
680 BrowserAccessibilityManager::Create(
681 MakeAXTreeUpdate(root
, static_text
, inline_text1
, inline_text2
),
683 new CountedBrowserAccessibilityFactory()));
685 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
686 BrowserAccessibility
* static_text_accessible
=
687 root_accessible
->PlatformGetChild(0);
689 EXPECT_EQ(gfx::Rect(100, 100, 6, 9).ToString(),
690 static_text_accessible
->GetLocalBoundsForRange(0, 1).ToString());
692 EXPECT_EQ(gfx::Rect(100, 100, 26, 9).ToString(),
693 static_text_accessible
->GetLocalBoundsForRange(0, 5).ToString());
695 EXPECT_EQ(gfx::Rect(100, 109, 5, 9).ToString(),
696 static_text_accessible
->GetLocalBoundsForRange(7, 1).ToString());
698 EXPECT_EQ(gfx::Rect(100, 109, 25, 9).ToString(),
699 static_text_accessible
->GetLocalBoundsForRange(7, 5).ToString());
701 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
702 static_text_accessible
->GetLocalBoundsForRange(5, 3).ToString());
704 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
705 static_text_accessible
->GetLocalBoundsForRange(0, 13).ToString());
707 // Test range that's beyond the text.
708 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
709 static_text_accessible
->GetLocalBoundsForRange(-1, 999).ToString());
711 // Test that we can call bounds for range on the parent element, too,
712 // and it still works.
713 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
714 root_accessible
->GetLocalBoundsForRange(0, 13).ToString());
717 TEST(BrowserAccessibilityManagerTest
, BoundsForRangeBiDi
) {
718 // In this example, we assume that the string "123abc" is rendered with
719 // "123" going left-to-right and "abc" going right-to-left. In other
720 // words, on-screen it would look like "123cba". This is possible to
721 // acheive if the source string had unicode control characters
722 // to switch directions. This test doesn't worry about how, though - it just
723 // tests that if something like that were to occur, GetLocalBoundsForRange
724 // returns the correct bounds for different ranges.
728 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
730 ui::AXNodeData static_text
;
732 static_text
.SetValue("123abc");
733 static_text
.role
= ui::AX_ROLE_STATIC_TEXT
;
734 static_text
.location
= gfx::Rect(100, 100, 60, 20);
735 root
.child_ids
.push_back(2);
737 ui::AXNodeData inline_text1
;
739 inline_text1
.SetValue("123");
740 inline_text1
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
741 inline_text1
.location
= gfx::Rect(100, 100, 30, 20);
742 inline_text1
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
743 ui::AX_TEXT_DIRECTION_LR
);
744 std::vector
<int32
> character_offsets1
;
745 character_offsets1
.push_back(10); // 0
746 character_offsets1
.push_back(20); // 1
747 character_offsets1
.push_back(30); // 2
748 inline_text1
.AddIntListAttribute(
749 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets1
);
750 static_text
.child_ids
.push_back(3);
752 ui::AXNodeData inline_text2
;
754 inline_text2
.SetValue("abc");
755 inline_text2
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
756 inline_text2
.location
= gfx::Rect(130, 100, 30, 20);
757 inline_text2
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
758 ui::AX_TEXT_DIRECTION_RL
);
759 std::vector
<int32
> character_offsets2
;
760 character_offsets2
.push_back(10);
761 character_offsets2
.push_back(20);
762 character_offsets2
.push_back(30);
763 inline_text2
.AddIntListAttribute(
764 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets2
);
765 static_text
.child_ids
.push_back(4);
767 scoped_ptr
<BrowserAccessibilityManager
> manager(
768 BrowserAccessibilityManager::Create(
769 MakeAXTreeUpdate(root
, static_text
, inline_text1
, inline_text2
),
771 new CountedBrowserAccessibilityFactory()));
773 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
774 BrowserAccessibility
* static_text_accessible
=
775 root_accessible
->PlatformGetChild(0);
777 EXPECT_EQ(gfx::Rect(100, 100, 60, 20).ToString(),
778 static_text_accessible
->GetLocalBoundsForRange(0, 6).ToString());
780 EXPECT_EQ(gfx::Rect(100, 100, 10, 20).ToString(),
781 static_text_accessible
->GetLocalBoundsForRange(0, 1).ToString());
783 EXPECT_EQ(gfx::Rect(100, 100, 30, 20).ToString(),
784 static_text_accessible
->GetLocalBoundsForRange(0, 3).ToString());
786 EXPECT_EQ(gfx::Rect(150, 100, 10, 20).ToString(),
787 static_text_accessible
->GetLocalBoundsForRange(3, 1).ToString());
789 EXPECT_EQ(gfx::Rect(130, 100, 30, 20).ToString(),
790 static_text_accessible
->GetLocalBoundsForRange(3, 3).ToString());
792 // This range is only two characters, but because of the direction switch
793 // the bounds are as wide as four characters.
794 EXPECT_EQ(gfx::Rect(120, 100, 40, 20).ToString(),
795 static_text_accessible
->GetLocalBoundsForRange(2, 2).ToString());
799 #define MAYBE_BoundsForRangeOnParentElement \
800 DISABLED_BoundsForRangeOnParentElement
802 #define MAYBE_BoundsForRangeOnParentElement BoundsForRangeOnParentElement
804 TEST(BrowserAccessibilityManagerTest
, MAYBE_BoundsForRangeOnParentElement
) {
807 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
808 root
.child_ids
.push_back(2);
812 div
.role
= ui::AX_ROLE_DIV
;
813 div
.location
= gfx::Rect(100, 100, 100, 20);
814 div
.child_ids
.push_back(3);
815 div
.child_ids
.push_back(4);
816 div
.child_ids
.push_back(5);
818 ui::AXNodeData static_text1
;
820 static_text1
.SetValue("AB");
821 static_text1
.role
= ui::AX_ROLE_STATIC_TEXT
;
822 static_text1
.location
= gfx::Rect(100, 100, 40, 20);
823 static_text1
.child_ids
.push_back(6);
827 img
.role
= ui::AX_ROLE_IMAGE
;
828 img
.location
= gfx::Rect(140, 100, 20, 20);
830 ui::AXNodeData static_text2
;
832 static_text2
.SetValue("CD");
833 static_text2
.role
= ui::AX_ROLE_STATIC_TEXT
;
834 static_text2
.location
= gfx::Rect(160, 100, 40, 20);
835 static_text2
.child_ids
.push_back(7);
837 ui::AXNodeData inline_text1
;
839 inline_text1
.SetValue("AB");
840 inline_text1
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
841 inline_text1
.location
= gfx::Rect(100, 100, 40, 20);
842 inline_text1
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
843 ui::AX_TEXT_DIRECTION_LR
);
844 std::vector
<int32
> character_offsets1
;
845 character_offsets1
.push_back(20); // 0
846 character_offsets1
.push_back(40); // 1
847 inline_text1
.AddIntListAttribute(
848 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets1
);
850 ui::AXNodeData inline_text2
;
852 inline_text2
.SetValue("CD");
853 inline_text2
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
854 inline_text2
.location
= gfx::Rect(160, 100, 40, 20);
855 inline_text2
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
856 ui::AX_TEXT_DIRECTION_LR
);
857 std::vector
<int32
> character_offsets2
;
858 character_offsets2
.push_back(20); // 0
859 character_offsets2
.push_back(40); // 1
860 inline_text2
.AddIntListAttribute(
861 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets2
);
863 scoped_ptr
<BrowserAccessibilityManager
> manager(
864 BrowserAccessibilityManager::Create(
866 root
, div
, static_text1
, img
,
867 static_text2
, inline_text1
, inline_text2
),
869 new CountedBrowserAccessibilityFactory()));
870 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
872 EXPECT_EQ(gfx::Rect(100, 100, 20, 20).ToString(),
873 root_accessible
->GetLocalBoundsForRange(0, 1).ToString());
875 EXPECT_EQ(gfx::Rect(100, 100, 40, 20).ToString(),
876 root_accessible
->GetLocalBoundsForRange(0, 2).ToString());
878 EXPECT_EQ(gfx::Rect(100, 100, 80, 20).ToString(),
879 root_accessible
->GetLocalBoundsForRange(0, 3).ToString());
881 EXPECT_EQ(gfx::Rect(120, 100, 60, 20).ToString(),
882 root_accessible
->GetLocalBoundsForRange(1, 2).ToString());
884 EXPECT_EQ(gfx::Rect(120, 100, 80, 20).ToString(),
885 root_accessible
->GetLocalBoundsForRange(1, 3).ToString());
887 EXPECT_EQ(gfx::Rect(100, 100, 100, 20).ToString(),
888 root_accessible
->GetLocalBoundsForRange(0, 4).ToString());
891 TEST(BrowserAccessibilityManagerTest
, NextPreviousInTreeOrder
) {
894 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
896 ui::AXNodeData node2
;
898 root
.child_ids
.push_back(2);
900 ui::AXNodeData node3
;
902 root
.child_ids
.push_back(3);
904 ui::AXNodeData node4
;
906 node3
.child_ids
.push_back(4);
908 ui::AXNodeData node5
;
910 root
.child_ids
.push_back(5);
912 scoped_ptr
<BrowserAccessibilityManager
> manager(
913 BrowserAccessibilityManager::Create(
914 MakeAXTreeUpdate(root
, node2
, node3
, node4
, node5
),
916 new CountedBrowserAccessibilityFactory()));
918 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
919 BrowserAccessibility
* node2_accessible
= root_accessible
->PlatformGetChild(0);
920 BrowserAccessibility
* node3_accessible
= root_accessible
->PlatformGetChild(1);
921 BrowserAccessibility
* node4_accessible
=
922 node3_accessible
->PlatformGetChild(0);
923 BrowserAccessibility
* node5_accessible
= root_accessible
->PlatformGetChild(2);
925 ASSERT_EQ(NULL
, manager
->NextInTreeOrder(NULL
));
926 ASSERT_EQ(node2_accessible
, manager
->NextInTreeOrder(root_accessible
));
927 ASSERT_EQ(node3_accessible
, manager
->NextInTreeOrder(node2_accessible
));
928 ASSERT_EQ(node4_accessible
, manager
->NextInTreeOrder(node3_accessible
));
929 ASSERT_EQ(node5_accessible
, manager
->NextInTreeOrder(node4_accessible
));
930 ASSERT_EQ(NULL
, manager
->NextInTreeOrder(node5_accessible
));
932 ASSERT_EQ(NULL
, manager
->PreviousInTreeOrder(NULL
));
933 ASSERT_EQ(node4_accessible
, manager
->PreviousInTreeOrder(node5_accessible
));
934 ASSERT_EQ(node3_accessible
, manager
->PreviousInTreeOrder(node4_accessible
));
935 ASSERT_EQ(node2_accessible
, manager
->PreviousInTreeOrder(node3_accessible
));
936 ASSERT_EQ(root_accessible
, manager
->PreviousInTreeOrder(node2_accessible
));
939 } // namespace content