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 AccessibilityShowContextMenu(int acc_obj_id
) 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 AccessibilitySetScrollOffset(int acc_obj_id
,
74 const gfx::Point
& offset
) override
{}
75 void AccessibilitySetTextSelection(int acc_obj_id
,
77 int end_offset
) override
{}
78 void AccessibilitySetValue(int acc_obj_id
, const base::string16
& value
)
80 bool AccessibilityViewHasFocus() const override
{ return false; }
81 gfx::Rect
AccessibilityGetViewBounds() const override
{ return gfx::Rect(); }
82 gfx::Point
AccessibilityOriginInScreen(
83 const gfx::Rect
& bounds
) const override
{
86 void AccessibilityHitTest(const gfx::Point
& point
) override
{}
87 void AccessibilitySetAccessibilityFocus(int acc_obj_id
) override
{}
88 void AccessibilityFatalError() override
{ got_fatal_error_
= true; }
89 gfx::AcceleratedWidget
AccessibilityGetAcceleratedWidget() override
{
90 return gfx::kNullAcceleratedWidget
;
92 gfx::NativeViewAccessible
AccessibilityGetNativeViewAccessible() override
{
95 BrowserAccessibilityManager
* AccessibilityGetChildFrame(
96 int accessibility_node_id
) override
{
99 BrowserAccessibility
* AccessibilityGetParentFrame() override
{ return NULL
; }
100 void AccessibilityGetAllChildFrames(
101 std::vector
<BrowserAccessibilityManager
*>* child_frames
) override
{}
103 bool got_fatal_error() const { return got_fatal_error_
; }
104 void reset_got_fatal_error() { got_fatal_error_
= false; }
107 bool got_fatal_error_
;
110 } // anonymous namespace
112 TEST(BrowserAccessibilityManagerTest
, TestNoLeaks
) {
113 // Create ui::AXNodeData objects for a simple document tree,
114 // representing the accessibility information used to initialize
115 // BrowserAccessibilityManager.
116 ui::AXNodeData button
;
118 button
.SetName("Button");
119 button
.role
= ui::AX_ROLE_BUTTON
;
122 ui::AXNodeData checkbox
;
124 checkbox
.SetName("Checkbox");
125 checkbox
.role
= ui::AX_ROLE_CHECK_BOX
;
130 root
.SetName("Document");
131 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
133 root
.child_ids
.push_back(2);
134 root
.child_ids
.push_back(3);
136 // Construct a BrowserAccessibilityManager with this
137 // ui::AXNodeData tree and a factory for an instance-counting
138 // BrowserAccessibility, and ensure that exactly 3 instances were
139 // created. Note that the manager takes ownership of the factory.
140 CountedBrowserAccessibility::global_obj_count_
= 0;
141 BrowserAccessibilityManager
* manager
=
142 BrowserAccessibilityManager::Create(
143 MakeAXTreeUpdate(root
, button
, checkbox
),
145 new CountedBrowserAccessibilityFactory());
147 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_
);
149 // Delete the manager and test that all 3 instances are deleted.
151 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
153 // Construct a manager again, and this time save references to two of
154 // the three nodes in the tree.
156 BrowserAccessibilityManager::Create(
157 MakeAXTreeUpdate(root
, button
, checkbox
),
159 new CountedBrowserAccessibilityFactory());
160 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_
);
162 CountedBrowserAccessibility
* root_accessible
=
163 static_cast<CountedBrowserAccessibility
*>(manager
->GetRoot());
164 root_accessible
->NativeAddReference();
165 CountedBrowserAccessibility
* child1_accessible
=
166 static_cast<CountedBrowserAccessibility
*>(
167 root_accessible
->PlatformGetChild(1));
168 child1_accessible
->NativeAddReference();
170 // Now delete the manager, and only one of the three nodes in the tree
171 // should be released.
173 ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_
);
175 // Release each of our references and make sure that each one results in
176 // the instance being deleted as its reference count hits zero.
177 root_accessible
->NativeReleaseReference();
178 ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_
);
179 child1_accessible
->NativeReleaseReference();
180 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
183 TEST(BrowserAccessibilityManagerTest
, TestReuseBrowserAccessibilityObjects
) {
184 // Make sure that changes to a subtree reuse as many objects as possible.
193 ui::AXNodeData tree1_child1
;
195 tree1_child1
.SetName("Child1");
196 tree1_child1
.role
= ui::AX_ROLE_BUTTON
;
197 tree1_child1
.state
= 0;
199 ui::AXNodeData tree1_child2
;
201 tree1_child2
.SetName("Child2");
202 tree1_child2
.role
= ui::AX_ROLE_BUTTON
;
203 tree1_child2
.state
= 0;
205 ui::AXNodeData tree1_child3
;
207 tree1_child3
.SetName("Child3");
208 tree1_child3
.role
= ui::AX_ROLE_BUTTON
;
209 tree1_child3
.state
= 0;
211 ui::AXNodeData tree1_root
;
213 tree1_root
.SetName("Document");
214 tree1_root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
215 tree1_root
.state
= 0;
216 tree1_root
.child_ids
.push_back(2);
217 tree1_root
.child_ids
.push_back(3);
218 tree1_root
.child_ids
.push_back(4);
223 // child0 <-- inserted
226 // <-- child3 deleted
228 ui::AXNodeData tree2_child0
;
230 tree2_child0
.SetName("Child0");
231 tree2_child0
.role
= ui::AX_ROLE_BUTTON
;
232 tree2_child0
.state
= 0;
234 ui::AXNodeData tree2_root
;
236 tree2_root
.SetName("DocumentChanged");
237 tree2_root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
238 tree2_root
.state
= 0;
239 tree2_root
.child_ids
.push_back(5);
240 tree2_root
.child_ids
.push_back(2);
241 tree2_root
.child_ids
.push_back(3);
243 // Construct a BrowserAccessibilityManager with tree1.
244 CountedBrowserAccessibility::global_obj_count_
= 0;
245 BrowserAccessibilityManager
* manager
=
246 BrowserAccessibilityManager::Create(
247 MakeAXTreeUpdate(tree1_root
,
248 tree1_child1
, tree1_child2
, tree1_child3
),
250 new CountedBrowserAccessibilityFactory());
251 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_
);
253 // Save references to all of the objects.
254 CountedBrowserAccessibility
* root_accessible
=
255 static_cast<CountedBrowserAccessibility
*>(manager
->GetRoot());
256 root_accessible
->NativeAddReference();
257 CountedBrowserAccessibility
* child1_accessible
=
258 static_cast<CountedBrowserAccessibility
*>(
259 root_accessible
->PlatformGetChild(0));
260 child1_accessible
->NativeAddReference();
261 CountedBrowserAccessibility
* child2_accessible
=
262 static_cast<CountedBrowserAccessibility
*>(
263 root_accessible
->PlatformGetChild(1));
264 child2_accessible
->NativeAddReference();
265 CountedBrowserAccessibility
* child3_accessible
=
266 static_cast<CountedBrowserAccessibility
*>(
267 root_accessible
->PlatformGetChild(2));
268 child3_accessible
->NativeAddReference();
270 // Check the index in parent.
271 EXPECT_EQ(0, child1_accessible
->GetIndexInParent());
272 EXPECT_EQ(1, child2_accessible
->GetIndexInParent());
273 EXPECT_EQ(2, child3_accessible
->GetIndexInParent());
275 // Process a notification containing the changed subtree.
276 std::vector
<AccessibilityHostMsg_EventParams
> params
;
277 params
.push_back(AccessibilityHostMsg_EventParams());
278 AccessibilityHostMsg_EventParams
* msg
= ¶ms
[0];
279 msg
->event_type
= ui::AX_EVENT_CHILDREN_CHANGED
;
280 msg
->update
.nodes
.push_back(tree2_root
);
281 msg
->update
.nodes
.push_back(tree2_child0
);
282 msg
->id
= tree2_root
.id
;
283 manager
->OnAccessibilityEvents(params
);
285 // There should be 5 objects now: the 4 from the new tree, plus the
286 // reference to child3 we kept.
287 EXPECT_EQ(5, CountedBrowserAccessibility::global_obj_count_
);
289 // Check that our references to the root, child1, and child2 are still valid,
290 // but that the reference to child3 is now invalid.
291 EXPECT_TRUE(root_accessible
->instance_active());
292 EXPECT_TRUE(child1_accessible
->instance_active());
293 EXPECT_TRUE(child2_accessible
->instance_active());
294 EXPECT_FALSE(child3_accessible
->instance_active());
296 // Check that the index in parent has been updated.
297 EXPECT_EQ(1, child1_accessible
->GetIndexInParent());
298 EXPECT_EQ(2, child2_accessible
->GetIndexInParent());
300 // Release our references. The object count should only decrease by 1
302 root_accessible
->NativeReleaseReference();
303 child1_accessible
->NativeReleaseReference();
304 child2_accessible
->NativeReleaseReference();
305 child3_accessible
->NativeReleaseReference();
307 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_
);
309 // Delete the manager and make sure all memory is cleaned up.
311 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
314 TEST(BrowserAccessibilityManagerTest
, TestReuseBrowserAccessibilityObjects2
) {
315 // Similar to the test above, but with a more complicated tree.
328 ui::AXNodeData tree1_grandchild1
;
329 tree1_grandchild1
.id
= 4;
330 tree1_grandchild1
.SetName("GrandChild1");
331 tree1_grandchild1
.role
= ui::AX_ROLE_BUTTON
;
332 tree1_grandchild1
.state
= 0;
334 ui::AXNodeData tree1_child1
;
336 tree1_child1
.SetName("Child1");
337 tree1_child1
.role
= ui::AX_ROLE_BUTTON
;
338 tree1_child1
.state
= 0;
339 tree1_child1
.child_ids
.push_back(4);
341 ui::AXNodeData tree1_grandchild2
;
342 tree1_grandchild2
.id
= 6;
343 tree1_grandchild2
.SetName("GrandChild1");
344 tree1_grandchild2
.role
= ui::AX_ROLE_BUTTON
;
345 tree1_grandchild2
.state
= 0;
347 ui::AXNodeData tree1_child2
;
349 tree1_child2
.SetName("Child2");
350 tree1_child2
.role
= ui::AX_ROLE_BUTTON
;
351 tree1_child2
.state
= 0;
352 tree1_child2
.child_ids
.push_back(6);
354 ui::AXNodeData tree1_grandchild3
;
355 tree1_grandchild3
.id
= 8;
356 tree1_grandchild3
.SetName("GrandChild3");
357 tree1_grandchild3
.role
= ui::AX_ROLE_BUTTON
;
358 tree1_grandchild3
.state
= 0;
360 ui::AXNodeData tree1_child3
;
362 tree1_child3
.SetName("Child3");
363 tree1_child3
.role
= ui::AX_ROLE_BUTTON
;
364 tree1_child3
.state
= 0;
365 tree1_child3
.child_ids
.push_back(8);
367 ui::AXNodeData tree1_container
;
368 tree1_container
.id
= 2;
369 tree1_container
.SetName("Container");
370 tree1_container
.role
= ui::AX_ROLE_GROUP
;
371 tree1_container
.state
= 0;
372 tree1_container
.child_ids
.push_back(3);
373 tree1_container
.child_ids
.push_back(5);
374 tree1_container
.child_ids
.push_back(7);
376 ui::AXNodeData tree1_root
;
378 tree1_root
.SetName("Document");
379 tree1_root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
380 tree1_root
.state
= 0;
381 tree1_root
.child_ids
.push_back(2);
387 // child0 <-- inserted
393 // <-- child3 (and grandchild3) deleted
395 ui::AXNodeData tree2_grandchild0
;
396 tree2_grandchild0
.id
= 9;
397 tree2_grandchild0
.SetName("GrandChild0");
398 tree2_grandchild0
.role
= ui::AX_ROLE_BUTTON
;
399 tree2_grandchild0
.state
= 0;
401 ui::AXNodeData tree2_child0
;
402 tree2_child0
.id
= 10;
403 tree2_child0
.SetName("Child0");
404 tree2_child0
.role
= ui::AX_ROLE_BUTTON
;
405 tree2_child0
.state
= 0;
406 tree2_child0
.child_ids
.push_back(9);
408 ui::AXNodeData tree2_container
;
409 tree2_container
.id
= 2;
410 tree2_container
.SetName("Container");
411 tree2_container
.role
= ui::AX_ROLE_GROUP
;
412 tree2_container
.state
= 0;
413 tree2_container
.child_ids
.push_back(10);
414 tree2_container
.child_ids
.push_back(3);
415 tree2_container
.child_ids
.push_back(5);
417 // Construct a BrowserAccessibilityManager with tree1.
418 CountedBrowserAccessibility::global_obj_count_
= 0;
419 BrowserAccessibilityManager
* manager
=
420 BrowserAccessibilityManager::Create(
421 MakeAXTreeUpdate(tree1_root
, tree1_container
,
422 tree1_child1
, tree1_grandchild1
,
423 tree1_child2
, tree1_grandchild2
,
424 tree1_child3
, tree1_grandchild3
),
426 new CountedBrowserAccessibilityFactory());
427 ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_
);
429 // Save references to some objects.
430 CountedBrowserAccessibility
* root_accessible
=
431 static_cast<CountedBrowserAccessibility
*>(manager
->GetRoot());
432 root_accessible
->NativeAddReference();
433 CountedBrowserAccessibility
* container_accessible
=
434 static_cast<CountedBrowserAccessibility
*>(
435 root_accessible
->PlatformGetChild(0));
436 container_accessible
->NativeAddReference();
437 CountedBrowserAccessibility
* child2_accessible
=
438 static_cast<CountedBrowserAccessibility
*>(
439 container_accessible
->PlatformGetChild(1));
440 child2_accessible
->NativeAddReference();
441 CountedBrowserAccessibility
* child3_accessible
=
442 static_cast<CountedBrowserAccessibility
*>(
443 container_accessible
->PlatformGetChild(2));
444 child3_accessible
->NativeAddReference();
446 // Check the index in parent.
447 EXPECT_EQ(1, child2_accessible
->GetIndexInParent());
448 EXPECT_EQ(2, child3_accessible
->GetIndexInParent());
450 // Process a notification containing the changed subtree rooted at
452 std::vector
<AccessibilityHostMsg_EventParams
> params
;
453 params
.push_back(AccessibilityHostMsg_EventParams());
454 AccessibilityHostMsg_EventParams
* msg
= ¶ms
[0];
455 msg
->event_type
= ui::AX_EVENT_CHILDREN_CHANGED
;
456 msg
->update
.nodes
.push_back(tree2_container
);
457 msg
->update
.nodes
.push_back(tree2_child0
);
458 msg
->update
.nodes
.push_back(tree2_grandchild0
);
459 msg
->id
= tree2_container
.id
;
460 manager
->OnAccessibilityEvents(params
);
462 // There should be 9 objects now: the 8 from the new tree, plus the
463 // reference to child3 we kept.
464 EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_
);
466 // Check that our references to the root and container and child2 are
467 // still valid, but that the reference to child3 is now invalid.
468 EXPECT_TRUE(root_accessible
->instance_active());
469 EXPECT_TRUE(container_accessible
->instance_active());
470 EXPECT_TRUE(child2_accessible
->instance_active());
471 EXPECT_FALSE(child3_accessible
->instance_active());
473 // Ensure that we retain the parent of the detached subtree.
474 EXPECT_EQ(root_accessible
, container_accessible
->GetParent());
475 EXPECT_EQ(0, container_accessible
->GetIndexInParent());
477 // Check that the index in parent has been updated.
478 EXPECT_EQ(2, child2_accessible
->GetIndexInParent());
480 // Release our references. The object count should only decrease by 1
482 root_accessible
->NativeReleaseReference();
483 container_accessible
->NativeReleaseReference();
484 child2_accessible
->NativeReleaseReference();
485 child3_accessible
->NativeReleaseReference();
487 EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_
);
489 // Delete the manager and make sure all memory is cleaned up.
491 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
494 TEST(BrowserAccessibilityManagerTest
, TestMoveChildUp
) {
502 ui::AXNodeData tree1_4
;
506 ui::AXNodeData tree1_3
;
509 tree1_3
.child_ids
.push_back(4);
511 ui::AXNodeData tree1_2
;
515 ui::AXNodeData tree1_1
;
517 tree1_1
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
519 tree1_1
.child_ids
.push_back(2);
520 tree1_1
.child_ids
.push_back(3);
525 // 4 <-- moves up a level and gains child
529 ui::AXNodeData tree2_6
;
533 ui::AXNodeData tree2_5
;
537 ui::AXNodeData tree2_4
;
540 tree2_4
.child_ids
.push_back(6);
542 ui::AXNodeData tree2_1
;
545 tree2_1
.child_ids
.push_back(4);
546 tree2_1
.child_ids
.push_back(5);
548 // Construct a BrowserAccessibilityManager with tree1.
549 CountedBrowserAccessibility::global_obj_count_
= 0;
550 BrowserAccessibilityManager
* manager
=
551 BrowserAccessibilityManager::Create(
552 MakeAXTreeUpdate(tree1_1
, tree1_2
, tree1_3
, tree1_4
),
554 new CountedBrowserAccessibilityFactory());
555 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_
);
557 // Process a notification containing the changed subtree.
558 std::vector
<AccessibilityHostMsg_EventParams
> params
;
559 params
.push_back(AccessibilityHostMsg_EventParams());
560 AccessibilityHostMsg_EventParams
* msg
= ¶ms
[0];
561 msg
->event_type
= ui::AX_EVENT_CHILDREN_CHANGED
;
562 msg
->update
.nodes
.push_back(tree2_1
);
563 msg
->update
.nodes
.push_back(tree2_4
);
564 msg
->update
.nodes
.push_back(tree2_5
);
565 msg
->update
.nodes
.push_back(tree2_6
);
566 msg
->id
= tree2_1
.id
;
567 manager
->OnAccessibilityEvents(params
);
569 // There should be 4 objects now.
570 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_
);
572 // Delete the manager and make sure all memory is cleaned up.
574 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
577 TEST(BrowserAccessibilityManagerTest
, TestFatalError
) {
578 // Test that BrowserAccessibilityManager raises a fatal error
579 // (which will crash the renderer) if the same id is used in
580 // two places in the tree.
584 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
585 root
.child_ids
.push_back(2);
586 root
.child_ids
.push_back(2);
588 CountedBrowserAccessibilityFactory
* factory
=
589 new CountedBrowserAccessibilityFactory();
590 scoped_ptr
<TestBrowserAccessibilityDelegate
> delegate(
591 new TestBrowserAccessibilityDelegate());
592 scoped_ptr
<BrowserAccessibilityManager
> manager
;
593 ASSERT_FALSE(delegate
->got_fatal_error());
594 manager
.reset(BrowserAccessibilityManager::Create(
595 MakeAXTreeUpdate(root
),
598 ASSERT_TRUE(delegate
->got_fatal_error());
600 ui::AXNodeData root2
;
602 root2
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
603 root2
.child_ids
.push_back(2);
604 root2
.child_ids
.push_back(3);
606 ui::AXNodeData child1
;
608 child1
.child_ids
.push_back(4);
609 child1
.child_ids
.push_back(5);
611 ui::AXNodeData child2
;
613 child2
.child_ids
.push_back(6);
614 child2
.child_ids
.push_back(5); // Duplicate
616 ui::AXNodeData grandchild4
;
619 ui::AXNodeData grandchild5
;
622 ui::AXNodeData grandchild6
;
625 delegate
->reset_got_fatal_error();
626 factory
= new CountedBrowserAccessibilityFactory();
627 manager
.reset(BrowserAccessibilityManager::Create(
628 MakeAXTreeUpdate(root2
, child1
, child2
,
629 grandchild4
, grandchild5
, grandchild6
),
632 ASSERT_TRUE(delegate
->got_fatal_error());
635 TEST(BrowserAccessibilityManagerTest
, BoundsForRange
) {
638 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
640 ui::AXNodeData static_text
;
642 static_text
.SetValue("Hello, world.");
643 static_text
.role
= ui::AX_ROLE_STATIC_TEXT
;
644 static_text
.location
= gfx::Rect(100, 100, 29, 18);
645 root
.child_ids
.push_back(2);
647 ui::AXNodeData inline_text1
;
649 inline_text1
.SetValue("Hello, ");
650 inline_text1
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
651 inline_text1
.location
= gfx::Rect(100, 100, 29, 9);
652 inline_text1
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
653 ui::AX_TEXT_DIRECTION_LTR
);
654 std::vector
<int32
> character_offsets1
;
655 character_offsets1
.push_back(6); // 0
656 character_offsets1
.push_back(11); // 1
657 character_offsets1
.push_back(16); // 2
658 character_offsets1
.push_back(21); // 3
659 character_offsets1
.push_back(26); // 4
660 character_offsets1
.push_back(29); // 5
661 character_offsets1
.push_back(29); // 6 (note that the space has no width)
662 inline_text1
.AddIntListAttribute(
663 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets1
);
664 static_text
.child_ids
.push_back(3);
666 ui::AXNodeData inline_text2
;
668 inline_text2
.SetValue("world.");
669 inline_text2
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
670 inline_text2
.location
= gfx::Rect(100, 109, 28, 9);
671 inline_text2
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
672 ui::AX_TEXT_DIRECTION_LTR
);
673 std::vector
<int32
> character_offsets2
;
674 character_offsets2
.push_back(5);
675 character_offsets2
.push_back(10);
676 character_offsets2
.push_back(15);
677 character_offsets2
.push_back(20);
678 character_offsets2
.push_back(25);
679 character_offsets2
.push_back(28);
680 inline_text2
.AddIntListAttribute(
681 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets2
);
682 static_text
.child_ids
.push_back(4);
684 scoped_ptr
<BrowserAccessibilityManager
> manager(
685 BrowserAccessibilityManager::Create(
686 MakeAXTreeUpdate(root
, static_text
, inline_text1
, inline_text2
),
688 new CountedBrowserAccessibilityFactory()));
690 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
691 BrowserAccessibility
* static_text_accessible
=
692 root_accessible
->PlatformGetChild(0);
694 EXPECT_EQ(gfx::Rect(100, 100, 6, 9).ToString(),
695 static_text_accessible
->GetLocalBoundsForRange(0, 1).ToString());
697 EXPECT_EQ(gfx::Rect(100, 100, 26, 9).ToString(),
698 static_text_accessible
->GetLocalBoundsForRange(0, 5).ToString());
700 EXPECT_EQ(gfx::Rect(100, 109, 5, 9).ToString(),
701 static_text_accessible
->GetLocalBoundsForRange(7, 1).ToString());
703 EXPECT_EQ(gfx::Rect(100, 109, 25, 9).ToString(),
704 static_text_accessible
->GetLocalBoundsForRange(7, 5).ToString());
706 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
707 static_text_accessible
->GetLocalBoundsForRange(5, 3).ToString());
709 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
710 static_text_accessible
->GetLocalBoundsForRange(0, 13).ToString());
712 // Test range that's beyond the text.
713 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
714 static_text_accessible
->GetLocalBoundsForRange(-1, 999).ToString());
716 // Test that we can call bounds for range on the parent element, too,
717 // and it still works.
718 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
719 root_accessible
->GetLocalBoundsForRange(0, 13).ToString());
722 TEST(BrowserAccessibilityManagerTest
, BoundsForRangeBiDi
) {
723 // In this example, we assume that the string "123abc" is rendered with
724 // "123" going left-to-right and "abc" going right-to-left. In other
725 // words, on-screen it would look like "123cba". This is possible to
726 // acheive if the source string had unicode control characters
727 // to switch directions. This test doesn't worry about how, though - it just
728 // tests that if something like that were to occur, GetLocalBoundsForRange
729 // returns the correct bounds for different ranges.
733 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
735 ui::AXNodeData static_text
;
737 static_text
.SetValue("123abc");
738 static_text
.role
= ui::AX_ROLE_STATIC_TEXT
;
739 static_text
.location
= gfx::Rect(100, 100, 60, 20);
740 root
.child_ids
.push_back(2);
742 ui::AXNodeData inline_text1
;
744 inline_text1
.SetValue("123");
745 inline_text1
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
746 inline_text1
.location
= gfx::Rect(100, 100, 30, 20);
747 inline_text1
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
748 ui::AX_TEXT_DIRECTION_LTR
);
749 std::vector
<int32
> character_offsets1
;
750 character_offsets1
.push_back(10); // 0
751 character_offsets1
.push_back(20); // 1
752 character_offsets1
.push_back(30); // 2
753 inline_text1
.AddIntListAttribute(
754 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets1
);
755 static_text
.child_ids
.push_back(3);
757 ui::AXNodeData inline_text2
;
759 inline_text2
.SetValue("abc");
760 inline_text2
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
761 inline_text2
.location
= gfx::Rect(130, 100, 30, 20);
762 inline_text2
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
763 ui::AX_TEXT_DIRECTION_RTL
);
764 std::vector
<int32
> character_offsets2
;
765 character_offsets2
.push_back(10);
766 character_offsets2
.push_back(20);
767 character_offsets2
.push_back(30);
768 inline_text2
.AddIntListAttribute(
769 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets2
);
770 static_text
.child_ids
.push_back(4);
772 scoped_ptr
<BrowserAccessibilityManager
> manager(
773 BrowserAccessibilityManager::Create(
774 MakeAXTreeUpdate(root
, static_text
, inline_text1
, inline_text2
),
776 new CountedBrowserAccessibilityFactory()));
778 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
779 BrowserAccessibility
* static_text_accessible
=
780 root_accessible
->PlatformGetChild(0);
782 EXPECT_EQ(gfx::Rect(100, 100, 60, 20).ToString(),
783 static_text_accessible
->GetLocalBoundsForRange(0, 6).ToString());
785 EXPECT_EQ(gfx::Rect(100, 100, 10, 20).ToString(),
786 static_text_accessible
->GetLocalBoundsForRange(0, 1).ToString());
788 EXPECT_EQ(gfx::Rect(100, 100, 30, 20).ToString(),
789 static_text_accessible
->GetLocalBoundsForRange(0, 3).ToString());
791 EXPECT_EQ(gfx::Rect(150, 100, 10, 20).ToString(),
792 static_text_accessible
->GetLocalBoundsForRange(3, 1).ToString());
794 EXPECT_EQ(gfx::Rect(130, 100, 30, 20).ToString(),
795 static_text_accessible
->GetLocalBoundsForRange(3, 3).ToString());
797 // This range is only two characters, but because of the direction switch
798 // the bounds are as wide as four characters.
799 EXPECT_EQ(gfx::Rect(120, 100, 40, 20).ToString(),
800 static_text_accessible
->GetLocalBoundsForRange(2, 2).ToString());
803 TEST(BrowserAccessibilityManagerTest
, BoundsForRangeScrolledWindow
) {
806 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
807 root
.AddIntAttribute(ui::AX_ATTR_SCROLL_X
, 25);
808 root
.AddIntAttribute(ui::AX_ATTR_SCROLL_Y
, 50);
810 ui::AXNodeData static_text
;
812 static_text
.SetValue("ABC");
813 static_text
.role
= ui::AX_ROLE_STATIC_TEXT
;
814 static_text
.location
= gfx::Rect(100, 100, 16, 9);
815 root
.child_ids
.push_back(2);
817 ui::AXNodeData inline_text
;
819 inline_text
.SetValue("ABC");
820 inline_text
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
821 inline_text
.location
= gfx::Rect(100, 100, 16, 9);
822 inline_text
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
823 ui::AX_TEXT_DIRECTION_LTR
);
824 std::vector
<int32
> character_offsets1
;
825 character_offsets1
.push_back(6); // 0
826 character_offsets1
.push_back(11); // 1
827 character_offsets1
.push_back(16); // 2
828 inline_text
.AddIntListAttribute(
829 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets1
);
830 static_text
.child_ids
.push_back(3);
832 scoped_ptr
<BrowserAccessibilityManager
> manager(
833 BrowserAccessibilityManager::Create(
834 MakeAXTreeUpdate(root
, static_text
, inline_text
),
836 new CountedBrowserAccessibilityFactory()));
838 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
839 BrowserAccessibility
* static_text_accessible
=
840 root_accessible
->PlatformGetChild(0);
842 if (manager
->UseRootScrollOffsetsWhenComputingBounds()) {
843 EXPECT_EQ(gfx::Rect(75, 50, 16, 9).ToString(),
844 static_text_accessible
->GetLocalBoundsForRange(0, 3).ToString());
846 EXPECT_EQ(gfx::Rect(100, 100, 16, 9).ToString(),
847 static_text_accessible
->GetLocalBoundsForRange(0, 3).ToString());
852 #define MAYBE_BoundsForRangeOnParentElement \
853 DISABLED_BoundsForRangeOnParentElement
855 #define MAYBE_BoundsForRangeOnParentElement BoundsForRangeOnParentElement
857 TEST(BrowserAccessibilityManagerTest
, MAYBE_BoundsForRangeOnParentElement
) {
860 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
861 root
.child_ids
.push_back(2);
865 div
.role
= ui::AX_ROLE_DIV
;
866 div
.location
= gfx::Rect(100, 100, 100, 20);
867 div
.child_ids
.push_back(3);
868 div
.child_ids
.push_back(4);
869 div
.child_ids
.push_back(5);
871 ui::AXNodeData static_text1
;
873 static_text1
.SetValue("AB");
874 static_text1
.role
= ui::AX_ROLE_STATIC_TEXT
;
875 static_text1
.location
= gfx::Rect(100, 100, 40, 20);
876 static_text1
.child_ids
.push_back(6);
880 img
.role
= ui::AX_ROLE_IMAGE
;
881 img
.location
= gfx::Rect(140, 100, 20, 20);
883 ui::AXNodeData static_text2
;
885 static_text2
.SetValue("CD");
886 static_text2
.role
= ui::AX_ROLE_STATIC_TEXT
;
887 static_text2
.location
= gfx::Rect(160, 100, 40, 20);
888 static_text2
.child_ids
.push_back(7);
890 ui::AXNodeData inline_text1
;
892 inline_text1
.SetValue("AB");
893 inline_text1
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
894 inline_text1
.location
= gfx::Rect(100, 100, 40, 20);
895 inline_text1
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
896 ui::AX_TEXT_DIRECTION_LTR
);
897 std::vector
<int32
> character_offsets1
;
898 character_offsets1
.push_back(20); // 0
899 character_offsets1
.push_back(40); // 1
900 inline_text1
.AddIntListAttribute(
901 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets1
);
903 ui::AXNodeData inline_text2
;
905 inline_text2
.SetValue("CD");
906 inline_text2
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
907 inline_text2
.location
= gfx::Rect(160, 100, 40, 20);
908 inline_text2
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
909 ui::AX_TEXT_DIRECTION_LTR
);
910 std::vector
<int32
> character_offsets2
;
911 character_offsets2
.push_back(20); // 0
912 character_offsets2
.push_back(40); // 1
913 inline_text2
.AddIntListAttribute(
914 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets2
);
916 scoped_ptr
<BrowserAccessibilityManager
> manager(
917 BrowserAccessibilityManager::Create(
919 root
, div
, static_text1
, img
,
920 static_text2
, inline_text1
, inline_text2
),
922 new CountedBrowserAccessibilityFactory()));
923 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
925 EXPECT_EQ(gfx::Rect(100, 100, 20, 20).ToString(),
926 root_accessible
->GetLocalBoundsForRange(0, 1).ToString());
928 EXPECT_EQ(gfx::Rect(100, 100, 40, 20).ToString(),
929 root_accessible
->GetLocalBoundsForRange(0, 2).ToString());
931 EXPECT_EQ(gfx::Rect(100, 100, 80, 20).ToString(),
932 root_accessible
->GetLocalBoundsForRange(0, 3).ToString());
934 EXPECT_EQ(gfx::Rect(120, 100, 60, 20).ToString(),
935 root_accessible
->GetLocalBoundsForRange(1, 2).ToString());
937 EXPECT_EQ(gfx::Rect(120, 100, 80, 20).ToString(),
938 root_accessible
->GetLocalBoundsForRange(1, 3).ToString());
940 EXPECT_EQ(gfx::Rect(100, 100, 100, 20).ToString(),
941 root_accessible
->GetLocalBoundsForRange(0, 4).ToString());
944 TEST(BrowserAccessibilityManagerTest
, NextPreviousInTreeOrder
) {
947 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
949 ui::AXNodeData node2
;
951 root
.child_ids
.push_back(2);
953 ui::AXNodeData node3
;
955 root
.child_ids
.push_back(3);
957 ui::AXNodeData node4
;
959 node3
.child_ids
.push_back(4);
961 ui::AXNodeData node5
;
963 root
.child_ids
.push_back(5);
965 scoped_ptr
<BrowserAccessibilityManager
> manager(
966 BrowserAccessibilityManager::Create(
967 MakeAXTreeUpdate(root
, node2
, node3
, node4
, node5
),
969 new CountedBrowserAccessibilityFactory()));
971 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
972 BrowserAccessibility
* node2_accessible
= root_accessible
->PlatformGetChild(0);
973 BrowserAccessibility
* node3_accessible
= root_accessible
->PlatformGetChild(1);
974 BrowserAccessibility
* node4_accessible
=
975 node3_accessible
->PlatformGetChild(0);
976 BrowserAccessibility
* node5_accessible
= root_accessible
->PlatformGetChild(2);
978 ASSERT_EQ(NULL
, manager
->NextInTreeOrder(NULL
));
979 ASSERT_EQ(node2_accessible
, manager
->NextInTreeOrder(root_accessible
));
980 ASSERT_EQ(node3_accessible
, manager
->NextInTreeOrder(node2_accessible
));
981 ASSERT_EQ(node4_accessible
, manager
->NextInTreeOrder(node3_accessible
));
982 ASSERT_EQ(node5_accessible
, manager
->NextInTreeOrder(node4_accessible
));
983 ASSERT_EQ(NULL
, manager
->NextInTreeOrder(node5_accessible
));
985 ASSERT_EQ(NULL
, manager
->PreviousInTreeOrder(NULL
));
986 ASSERT_EQ(node4_accessible
, manager
->PreviousInTreeOrder(node5_accessible
));
987 ASSERT_EQ(node3_accessible
, manager
->PreviousInTreeOrder(node4_accessible
));
988 ASSERT_EQ(node2_accessible
, manager
->PreviousInTreeOrder(node3_accessible
));
989 ASSERT_EQ(root_accessible
, manager
->PreviousInTreeOrder(node2_accessible
));
992 } // namespace content