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
{
102 void AccessibilityGetAllChildFrames(
103 std::vector
<BrowserAccessibilityManager
*>* child_frames
) override
{}
105 bool got_fatal_error() const { return got_fatal_error_
; }
106 void reset_got_fatal_error() { got_fatal_error_
= false; }
109 bool got_fatal_error_
;
112 } // anonymous namespace
114 TEST(BrowserAccessibilityManagerTest
, TestNoLeaks
) {
115 // Create ui::AXNodeData objects for a simple document tree,
116 // representing the accessibility information used to initialize
117 // BrowserAccessibilityManager.
118 ui::AXNodeData button
;
120 button
.SetName("Button");
121 button
.role
= ui::AX_ROLE_BUTTON
;
124 ui::AXNodeData checkbox
;
126 checkbox
.SetName("Checkbox");
127 checkbox
.role
= ui::AX_ROLE_CHECK_BOX
;
132 root
.SetName("Document");
133 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
135 root
.child_ids
.push_back(2);
136 root
.child_ids
.push_back(3);
138 // Construct a BrowserAccessibilityManager with this
139 // ui::AXNodeData tree and a factory for an instance-counting
140 // BrowserAccessibility, and ensure that exactly 3 instances were
141 // created. Note that the manager takes ownership of the factory.
142 CountedBrowserAccessibility::global_obj_count_
= 0;
143 BrowserAccessibilityManager
* manager
=
144 BrowserAccessibilityManager::Create(
145 MakeAXTreeUpdate(root
, button
, checkbox
),
147 new CountedBrowserAccessibilityFactory());
149 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_
);
151 // Delete the manager and test that all 3 instances are deleted.
153 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
155 // Construct a manager again, and this time save references to two of
156 // the three nodes in the tree.
158 BrowserAccessibilityManager::Create(
159 MakeAXTreeUpdate(root
, button
, checkbox
),
161 new CountedBrowserAccessibilityFactory());
162 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_
);
164 CountedBrowserAccessibility
* root_accessible
=
165 static_cast<CountedBrowserAccessibility
*>(manager
->GetRoot());
166 root_accessible
->NativeAddReference();
167 CountedBrowserAccessibility
* child1_accessible
=
168 static_cast<CountedBrowserAccessibility
*>(
169 root_accessible
->PlatformGetChild(1));
170 child1_accessible
->NativeAddReference();
172 // Now delete the manager, and only one of the three nodes in the tree
173 // should be released.
175 ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_
);
177 // Release each of our references and make sure that each one results in
178 // the instance being deleted as its reference count hits zero.
179 root_accessible
->NativeReleaseReference();
180 ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_
);
181 child1_accessible
->NativeReleaseReference();
182 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
185 TEST(BrowserAccessibilityManagerTest
, TestReuseBrowserAccessibilityObjects
) {
186 // Make sure that changes to a subtree reuse as many objects as possible.
195 ui::AXNodeData tree1_child1
;
197 tree1_child1
.SetName("Child1");
198 tree1_child1
.role
= ui::AX_ROLE_BUTTON
;
199 tree1_child1
.state
= 0;
201 ui::AXNodeData tree1_child2
;
203 tree1_child2
.SetName("Child2");
204 tree1_child2
.role
= ui::AX_ROLE_BUTTON
;
205 tree1_child2
.state
= 0;
207 ui::AXNodeData tree1_child3
;
209 tree1_child3
.SetName("Child3");
210 tree1_child3
.role
= ui::AX_ROLE_BUTTON
;
211 tree1_child3
.state
= 0;
213 ui::AXNodeData tree1_root
;
215 tree1_root
.SetName("Document");
216 tree1_root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
217 tree1_root
.state
= 0;
218 tree1_root
.child_ids
.push_back(2);
219 tree1_root
.child_ids
.push_back(3);
220 tree1_root
.child_ids
.push_back(4);
225 // child0 <-- inserted
228 // <-- child3 deleted
230 ui::AXNodeData tree2_child0
;
232 tree2_child0
.SetName("Child0");
233 tree2_child0
.role
= ui::AX_ROLE_BUTTON
;
234 tree2_child0
.state
= 0;
236 ui::AXNodeData tree2_root
;
238 tree2_root
.SetName("DocumentChanged");
239 tree2_root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
240 tree2_root
.state
= 0;
241 tree2_root
.child_ids
.push_back(5);
242 tree2_root
.child_ids
.push_back(2);
243 tree2_root
.child_ids
.push_back(3);
245 // Construct a BrowserAccessibilityManager with tree1.
246 CountedBrowserAccessibility::global_obj_count_
= 0;
247 BrowserAccessibilityManager
* manager
=
248 BrowserAccessibilityManager::Create(
249 MakeAXTreeUpdate(tree1_root
,
250 tree1_child1
, tree1_child2
, tree1_child3
),
252 new CountedBrowserAccessibilityFactory());
253 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_
);
255 // Save references to all of the objects.
256 CountedBrowserAccessibility
* root_accessible
=
257 static_cast<CountedBrowserAccessibility
*>(manager
->GetRoot());
258 root_accessible
->NativeAddReference();
259 CountedBrowserAccessibility
* child1_accessible
=
260 static_cast<CountedBrowserAccessibility
*>(
261 root_accessible
->PlatformGetChild(0));
262 child1_accessible
->NativeAddReference();
263 CountedBrowserAccessibility
* child2_accessible
=
264 static_cast<CountedBrowserAccessibility
*>(
265 root_accessible
->PlatformGetChild(1));
266 child2_accessible
->NativeAddReference();
267 CountedBrowserAccessibility
* child3_accessible
=
268 static_cast<CountedBrowserAccessibility
*>(
269 root_accessible
->PlatformGetChild(2));
270 child3_accessible
->NativeAddReference();
272 // Check the index in parent.
273 EXPECT_EQ(0, child1_accessible
->GetIndexInParent());
274 EXPECT_EQ(1, child2_accessible
->GetIndexInParent());
275 EXPECT_EQ(2, child3_accessible
->GetIndexInParent());
277 // Process a notification containing the changed subtree.
278 std::vector
<AccessibilityHostMsg_EventParams
> params
;
279 params
.push_back(AccessibilityHostMsg_EventParams());
280 AccessibilityHostMsg_EventParams
* msg
= ¶ms
[0];
281 msg
->event_type
= ui::AX_EVENT_CHILDREN_CHANGED
;
282 msg
->update
.nodes
.push_back(tree2_root
);
283 msg
->update
.nodes
.push_back(tree2_child0
);
284 msg
->id
= tree2_root
.id
;
285 manager
->OnAccessibilityEvents(params
);
287 // There should be 5 objects now: the 4 from the new tree, plus the
288 // reference to child3 we kept.
289 EXPECT_EQ(5, CountedBrowserAccessibility::global_obj_count_
);
291 // Check that our references to the root, child1, and child2 are still valid,
292 // but that the reference to child3 is now invalid.
293 EXPECT_TRUE(root_accessible
->instance_active());
294 EXPECT_TRUE(child1_accessible
->instance_active());
295 EXPECT_TRUE(child2_accessible
->instance_active());
296 EXPECT_FALSE(child3_accessible
->instance_active());
298 // Check that the index in parent has been updated.
299 EXPECT_EQ(1, child1_accessible
->GetIndexInParent());
300 EXPECT_EQ(2, child2_accessible
->GetIndexInParent());
302 // Release our references. The object count should only decrease by 1
304 root_accessible
->NativeReleaseReference();
305 child1_accessible
->NativeReleaseReference();
306 child2_accessible
->NativeReleaseReference();
307 child3_accessible
->NativeReleaseReference();
309 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_
);
311 // Delete the manager and make sure all memory is cleaned up.
313 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
316 TEST(BrowserAccessibilityManagerTest
, TestReuseBrowserAccessibilityObjects2
) {
317 // Similar to the test above, but with a more complicated tree.
330 ui::AXNodeData tree1_grandchild1
;
331 tree1_grandchild1
.id
= 4;
332 tree1_grandchild1
.SetName("GrandChild1");
333 tree1_grandchild1
.role
= ui::AX_ROLE_BUTTON
;
334 tree1_grandchild1
.state
= 0;
336 ui::AXNodeData tree1_child1
;
338 tree1_child1
.SetName("Child1");
339 tree1_child1
.role
= ui::AX_ROLE_BUTTON
;
340 tree1_child1
.state
= 0;
341 tree1_child1
.child_ids
.push_back(4);
343 ui::AXNodeData tree1_grandchild2
;
344 tree1_grandchild2
.id
= 6;
345 tree1_grandchild2
.SetName("GrandChild1");
346 tree1_grandchild2
.role
= ui::AX_ROLE_BUTTON
;
347 tree1_grandchild2
.state
= 0;
349 ui::AXNodeData tree1_child2
;
351 tree1_child2
.SetName("Child2");
352 tree1_child2
.role
= ui::AX_ROLE_BUTTON
;
353 tree1_child2
.state
= 0;
354 tree1_child2
.child_ids
.push_back(6);
356 ui::AXNodeData tree1_grandchild3
;
357 tree1_grandchild3
.id
= 8;
358 tree1_grandchild3
.SetName("GrandChild3");
359 tree1_grandchild3
.role
= ui::AX_ROLE_BUTTON
;
360 tree1_grandchild3
.state
= 0;
362 ui::AXNodeData tree1_child3
;
364 tree1_child3
.SetName("Child3");
365 tree1_child3
.role
= ui::AX_ROLE_BUTTON
;
366 tree1_child3
.state
= 0;
367 tree1_child3
.child_ids
.push_back(8);
369 ui::AXNodeData tree1_container
;
370 tree1_container
.id
= 2;
371 tree1_container
.SetName("Container");
372 tree1_container
.role
= ui::AX_ROLE_GROUP
;
373 tree1_container
.state
= 0;
374 tree1_container
.child_ids
.push_back(3);
375 tree1_container
.child_ids
.push_back(5);
376 tree1_container
.child_ids
.push_back(7);
378 ui::AXNodeData tree1_root
;
380 tree1_root
.SetName("Document");
381 tree1_root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
382 tree1_root
.state
= 0;
383 tree1_root
.child_ids
.push_back(2);
389 // child0 <-- inserted
395 // <-- child3 (and grandchild3) deleted
397 ui::AXNodeData tree2_grandchild0
;
398 tree2_grandchild0
.id
= 9;
399 tree2_grandchild0
.SetName("GrandChild0");
400 tree2_grandchild0
.role
= ui::AX_ROLE_BUTTON
;
401 tree2_grandchild0
.state
= 0;
403 ui::AXNodeData tree2_child0
;
404 tree2_child0
.id
= 10;
405 tree2_child0
.SetName("Child0");
406 tree2_child0
.role
= ui::AX_ROLE_BUTTON
;
407 tree2_child0
.state
= 0;
408 tree2_child0
.child_ids
.push_back(9);
410 ui::AXNodeData tree2_container
;
411 tree2_container
.id
= 2;
412 tree2_container
.SetName("Container");
413 tree2_container
.role
= ui::AX_ROLE_GROUP
;
414 tree2_container
.state
= 0;
415 tree2_container
.child_ids
.push_back(10);
416 tree2_container
.child_ids
.push_back(3);
417 tree2_container
.child_ids
.push_back(5);
419 // Construct a BrowserAccessibilityManager with tree1.
420 CountedBrowserAccessibility::global_obj_count_
= 0;
421 BrowserAccessibilityManager
* manager
=
422 BrowserAccessibilityManager::Create(
423 MakeAXTreeUpdate(tree1_root
, tree1_container
,
424 tree1_child1
, tree1_grandchild1
,
425 tree1_child2
, tree1_grandchild2
,
426 tree1_child3
, tree1_grandchild3
),
428 new CountedBrowserAccessibilityFactory());
429 ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_
);
431 // Save references to some objects.
432 CountedBrowserAccessibility
* root_accessible
=
433 static_cast<CountedBrowserAccessibility
*>(manager
->GetRoot());
434 root_accessible
->NativeAddReference();
435 CountedBrowserAccessibility
* container_accessible
=
436 static_cast<CountedBrowserAccessibility
*>(
437 root_accessible
->PlatformGetChild(0));
438 container_accessible
->NativeAddReference();
439 CountedBrowserAccessibility
* child2_accessible
=
440 static_cast<CountedBrowserAccessibility
*>(
441 container_accessible
->PlatformGetChild(1));
442 child2_accessible
->NativeAddReference();
443 CountedBrowserAccessibility
* child3_accessible
=
444 static_cast<CountedBrowserAccessibility
*>(
445 container_accessible
->PlatformGetChild(2));
446 child3_accessible
->NativeAddReference();
448 // Check the index in parent.
449 EXPECT_EQ(1, child2_accessible
->GetIndexInParent());
450 EXPECT_EQ(2, child3_accessible
->GetIndexInParent());
452 // Process a notification containing the changed subtree rooted at
454 std::vector
<AccessibilityHostMsg_EventParams
> params
;
455 params
.push_back(AccessibilityHostMsg_EventParams());
456 AccessibilityHostMsg_EventParams
* msg
= ¶ms
[0];
457 msg
->event_type
= ui::AX_EVENT_CHILDREN_CHANGED
;
458 msg
->update
.nodes
.push_back(tree2_container
);
459 msg
->update
.nodes
.push_back(tree2_child0
);
460 msg
->update
.nodes
.push_back(tree2_grandchild0
);
461 msg
->id
= tree2_container
.id
;
462 manager
->OnAccessibilityEvents(params
);
464 // There should be 9 objects now: the 8 from the new tree, plus the
465 // reference to child3 we kept.
466 EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_
);
468 // Check that our references to the root and container and child2 are
469 // still valid, but that the reference to child3 is now invalid.
470 EXPECT_TRUE(root_accessible
->instance_active());
471 EXPECT_TRUE(container_accessible
->instance_active());
472 EXPECT_TRUE(child2_accessible
->instance_active());
473 EXPECT_FALSE(child3_accessible
->instance_active());
475 // Ensure that we retain the parent of the detached subtree.
476 EXPECT_EQ(root_accessible
, container_accessible
->GetParent());
477 EXPECT_EQ(0, container_accessible
->GetIndexInParent());
479 // Check that the index in parent has been updated.
480 EXPECT_EQ(2, child2_accessible
->GetIndexInParent());
482 // Release our references. The object count should only decrease by 1
484 root_accessible
->NativeReleaseReference();
485 container_accessible
->NativeReleaseReference();
486 child2_accessible
->NativeReleaseReference();
487 child3_accessible
->NativeReleaseReference();
489 EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_
);
491 // Delete the manager and make sure all memory is cleaned up.
493 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
496 TEST(BrowserAccessibilityManagerTest
, TestMoveChildUp
) {
504 ui::AXNodeData tree1_4
;
508 ui::AXNodeData tree1_3
;
511 tree1_3
.child_ids
.push_back(4);
513 ui::AXNodeData tree1_2
;
517 ui::AXNodeData tree1_1
;
519 tree1_1
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
521 tree1_1
.child_ids
.push_back(2);
522 tree1_1
.child_ids
.push_back(3);
527 // 4 <-- moves up a level and gains child
531 ui::AXNodeData tree2_6
;
535 ui::AXNodeData tree2_5
;
539 ui::AXNodeData tree2_4
;
542 tree2_4
.child_ids
.push_back(6);
544 ui::AXNodeData tree2_1
;
547 tree2_1
.child_ids
.push_back(4);
548 tree2_1
.child_ids
.push_back(5);
550 // Construct a BrowserAccessibilityManager with tree1.
551 CountedBrowserAccessibility::global_obj_count_
= 0;
552 BrowserAccessibilityManager
* manager
=
553 BrowserAccessibilityManager::Create(
554 MakeAXTreeUpdate(tree1_1
, tree1_2
, tree1_3
, tree1_4
),
556 new CountedBrowserAccessibilityFactory());
557 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_
);
559 // Process a notification containing the changed subtree.
560 std::vector
<AccessibilityHostMsg_EventParams
> params
;
561 params
.push_back(AccessibilityHostMsg_EventParams());
562 AccessibilityHostMsg_EventParams
* msg
= ¶ms
[0];
563 msg
->event_type
= ui::AX_EVENT_CHILDREN_CHANGED
;
564 msg
->update
.nodes
.push_back(tree2_1
);
565 msg
->update
.nodes
.push_back(tree2_4
);
566 msg
->update
.nodes
.push_back(tree2_5
);
567 msg
->update
.nodes
.push_back(tree2_6
);
568 msg
->id
= tree2_1
.id
;
569 manager
->OnAccessibilityEvents(params
);
571 // There should be 4 objects now.
572 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_
);
574 // Delete the manager and make sure all memory is cleaned up.
576 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_
);
579 TEST(BrowserAccessibilityManagerTest
, TestFatalError
) {
580 // Test that BrowserAccessibilityManager raises a fatal error
581 // (which will crash the renderer) if the same id is used in
582 // two places in the tree.
586 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
587 root
.child_ids
.push_back(2);
588 root
.child_ids
.push_back(2);
590 CountedBrowserAccessibilityFactory
* factory
=
591 new CountedBrowserAccessibilityFactory();
592 scoped_ptr
<TestBrowserAccessibilityDelegate
> delegate(
593 new TestBrowserAccessibilityDelegate());
594 scoped_ptr
<BrowserAccessibilityManager
> manager
;
595 ASSERT_FALSE(delegate
->got_fatal_error());
596 manager
.reset(BrowserAccessibilityManager::Create(
597 MakeAXTreeUpdate(root
),
600 ASSERT_TRUE(delegate
->got_fatal_error());
602 ui::AXNodeData root2
;
604 root2
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
605 root2
.child_ids
.push_back(2);
606 root2
.child_ids
.push_back(3);
608 ui::AXNodeData child1
;
610 child1
.child_ids
.push_back(4);
611 child1
.child_ids
.push_back(5);
613 ui::AXNodeData child2
;
615 child2
.child_ids
.push_back(6);
616 child2
.child_ids
.push_back(5); // Duplicate
618 ui::AXNodeData grandchild4
;
621 ui::AXNodeData grandchild5
;
624 ui::AXNodeData grandchild6
;
627 delegate
->reset_got_fatal_error();
628 factory
= new CountedBrowserAccessibilityFactory();
629 manager
.reset(BrowserAccessibilityManager::Create(
630 MakeAXTreeUpdate(root2
, child1
, child2
,
631 grandchild4
, grandchild5
, grandchild6
),
634 ASSERT_TRUE(delegate
->got_fatal_error());
637 TEST(BrowserAccessibilityManagerTest
, BoundsForRange
) {
640 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
642 ui::AXNodeData static_text
;
644 static_text
.SetValue("Hello, world.");
645 static_text
.role
= ui::AX_ROLE_STATIC_TEXT
;
646 static_text
.location
= gfx::Rect(100, 100, 29, 18);
647 root
.child_ids
.push_back(2);
649 ui::AXNodeData inline_text1
;
651 inline_text1
.SetValue("Hello, ");
652 inline_text1
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
653 inline_text1
.location
= gfx::Rect(100, 100, 29, 9);
654 inline_text1
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
655 ui::AX_TEXT_DIRECTION_LTR
);
656 std::vector
<int32
> character_offsets1
;
657 character_offsets1
.push_back(6); // 0
658 character_offsets1
.push_back(11); // 1
659 character_offsets1
.push_back(16); // 2
660 character_offsets1
.push_back(21); // 3
661 character_offsets1
.push_back(26); // 4
662 character_offsets1
.push_back(29); // 5
663 character_offsets1
.push_back(29); // 6 (note that the space has no width)
664 inline_text1
.AddIntListAttribute(
665 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets1
);
666 static_text
.child_ids
.push_back(3);
668 ui::AXNodeData inline_text2
;
670 inline_text2
.SetValue("world.");
671 inline_text2
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
672 inline_text2
.location
= gfx::Rect(100, 109, 28, 9);
673 inline_text2
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
674 ui::AX_TEXT_DIRECTION_LTR
);
675 std::vector
<int32
> character_offsets2
;
676 character_offsets2
.push_back(5);
677 character_offsets2
.push_back(10);
678 character_offsets2
.push_back(15);
679 character_offsets2
.push_back(20);
680 character_offsets2
.push_back(25);
681 character_offsets2
.push_back(28);
682 inline_text2
.AddIntListAttribute(
683 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets2
);
684 static_text
.child_ids
.push_back(4);
686 scoped_ptr
<BrowserAccessibilityManager
> manager(
687 BrowserAccessibilityManager::Create(
688 MakeAXTreeUpdate(root
, static_text
, inline_text1
, inline_text2
),
690 new CountedBrowserAccessibilityFactory()));
692 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
693 BrowserAccessibility
* static_text_accessible
=
694 root_accessible
->PlatformGetChild(0);
696 EXPECT_EQ(gfx::Rect(100, 100, 6, 9).ToString(),
697 static_text_accessible
->GetLocalBoundsForRange(0, 1).ToString());
699 EXPECT_EQ(gfx::Rect(100, 100, 26, 9).ToString(),
700 static_text_accessible
->GetLocalBoundsForRange(0, 5).ToString());
702 EXPECT_EQ(gfx::Rect(100, 109, 5, 9).ToString(),
703 static_text_accessible
->GetLocalBoundsForRange(7, 1).ToString());
705 EXPECT_EQ(gfx::Rect(100, 109, 25, 9).ToString(),
706 static_text_accessible
->GetLocalBoundsForRange(7, 5).ToString());
708 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
709 static_text_accessible
->GetLocalBoundsForRange(5, 3).ToString());
711 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
712 static_text_accessible
->GetLocalBoundsForRange(0, 13).ToString());
714 // Test range that's beyond the text.
715 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
716 static_text_accessible
->GetLocalBoundsForRange(-1, 999).ToString());
718 // Test that we can call bounds for range on the parent element, too,
719 // and it still works.
720 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
721 root_accessible
->GetLocalBoundsForRange(0, 13).ToString());
724 TEST(BrowserAccessibilityManagerTest
, BoundsForRangeBiDi
) {
725 // In this example, we assume that the string "123abc" is rendered with
726 // "123" going left-to-right and "abc" going right-to-left. In other
727 // words, on-screen it would look like "123cba". This is possible to
728 // acheive if the source string had unicode control characters
729 // to switch directions. This test doesn't worry about how, though - it just
730 // tests that if something like that were to occur, GetLocalBoundsForRange
731 // returns the correct bounds for different ranges.
735 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
737 ui::AXNodeData static_text
;
739 static_text
.SetValue("123abc");
740 static_text
.role
= ui::AX_ROLE_STATIC_TEXT
;
741 static_text
.location
= gfx::Rect(100, 100, 60, 20);
742 root
.child_ids
.push_back(2);
744 ui::AXNodeData inline_text1
;
746 inline_text1
.SetValue("123");
747 inline_text1
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
748 inline_text1
.location
= gfx::Rect(100, 100, 30, 20);
749 inline_text1
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
750 ui::AX_TEXT_DIRECTION_LTR
);
751 std::vector
<int32
> character_offsets1
;
752 character_offsets1
.push_back(10); // 0
753 character_offsets1
.push_back(20); // 1
754 character_offsets1
.push_back(30); // 2
755 inline_text1
.AddIntListAttribute(
756 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets1
);
757 static_text
.child_ids
.push_back(3);
759 ui::AXNodeData inline_text2
;
761 inline_text2
.SetValue("abc");
762 inline_text2
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
763 inline_text2
.location
= gfx::Rect(130, 100, 30, 20);
764 inline_text2
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
765 ui::AX_TEXT_DIRECTION_RTL
);
766 std::vector
<int32
> character_offsets2
;
767 character_offsets2
.push_back(10);
768 character_offsets2
.push_back(20);
769 character_offsets2
.push_back(30);
770 inline_text2
.AddIntListAttribute(
771 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets2
);
772 static_text
.child_ids
.push_back(4);
774 scoped_ptr
<BrowserAccessibilityManager
> manager(
775 BrowserAccessibilityManager::Create(
776 MakeAXTreeUpdate(root
, static_text
, inline_text1
, inline_text2
),
778 new CountedBrowserAccessibilityFactory()));
780 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
781 BrowserAccessibility
* static_text_accessible
=
782 root_accessible
->PlatformGetChild(0);
784 EXPECT_EQ(gfx::Rect(100, 100, 60, 20).ToString(),
785 static_text_accessible
->GetLocalBoundsForRange(0, 6).ToString());
787 EXPECT_EQ(gfx::Rect(100, 100, 10, 20).ToString(),
788 static_text_accessible
->GetLocalBoundsForRange(0, 1).ToString());
790 EXPECT_EQ(gfx::Rect(100, 100, 30, 20).ToString(),
791 static_text_accessible
->GetLocalBoundsForRange(0, 3).ToString());
793 EXPECT_EQ(gfx::Rect(150, 100, 10, 20).ToString(),
794 static_text_accessible
->GetLocalBoundsForRange(3, 1).ToString());
796 EXPECT_EQ(gfx::Rect(130, 100, 30, 20).ToString(),
797 static_text_accessible
->GetLocalBoundsForRange(3, 3).ToString());
799 // This range is only two characters, but because of the direction switch
800 // the bounds are as wide as four characters.
801 EXPECT_EQ(gfx::Rect(120, 100, 40, 20).ToString(),
802 static_text_accessible
->GetLocalBoundsForRange(2, 2).ToString());
805 TEST(BrowserAccessibilityManagerTest
, BoundsForRangeScrolledWindow
) {
808 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
809 root
.AddIntAttribute(ui::AX_ATTR_SCROLL_X
, 25);
810 root
.AddIntAttribute(ui::AX_ATTR_SCROLL_Y
, 50);
812 ui::AXNodeData static_text
;
814 static_text
.SetValue("ABC");
815 static_text
.role
= ui::AX_ROLE_STATIC_TEXT
;
816 static_text
.location
= gfx::Rect(100, 100, 16, 9);
817 root
.child_ids
.push_back(2);
819 ui::AXNodeData inline_text
;
821 inline_text
.SetValue("ABC");
822 inline_text
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
823 inline_text
.location
= gfx::Rect(100, 100, 16, 9);
824 inline_text
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
825 ui::AX_TEXT_DIRECTION_LTR
);
826 std::vector
<int32
> character_offsets1
;
827 character_offsets1
.push_back(6); // 0
828 character_offsets1
.push_back(11); // 1
829 character_offsets1
.push_back(16); // 2
830 inline_text
.AddIntListAttribute(
831 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets1
);
832 static_text
.child_ids
.push_back(3);
834 scoped_ptr
<BrowserAccessibilityManager
> manager(
835 BrowserAccessibilityManager::Create(
836 MakeAXTreeUpdate(root
, static_text
, inline_text
),
838 new CountedBrowserAccessibilityFactory()));
840 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
841 BrowserAccessibility
* static_text_accessible
=
842 root_accessible
->PlatformGetChild(0);
844 if (manager
->UseRootScrollOffsetsWhenComputingBounds()) {
845 EXPECT_EQ(gfx::Rect(75, 50, 16, 9).ToString(),
846 static_text_accessible
->GetLocalBoundsForRange(0, 3).ToString());
848 EXPECT_EQ(gfx::Rect(100, 100, 16, 9).ToString(),
849 static_text_accessible
->GetLocalBoundsForRange(0, 3).ToString());
854 #define MAYBE_BoundsForRangeOnParentElement \
855 DISABLED_BoundsForRangeOnParentElement
857 #define MAYBE_BoundsForRangeOnParentElement BoundsForRangeOnParentElement
859 TEST(BrowserAccessibilityManagerTest
, MAYBE_BoundsForRangeOnParentElement
) {
862 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
863 root
.child_ids
.push_back(2);
867 div
.role
= ui::AX_ROLE_DIV
;
868 div
.location
= gfx::Rect(100, 100, 100, 20);
869 div
.child_ids
.push_back(3);
870 div
.child_ids
.push_back(4);
871 div
.child_ids
.push_back(5);
873 ui::AXNodeData static_text1
;
875 static_text1
.SetValue("AB");
876 static_text1
.role
= ui::AX_ROLE_STATIC_TEXT
;
877 static_text1
.location
= gfx::Rect(100, 100, 40, 20);
878 static_text1
.child_ids
.push_back(6);
882 img
.role
= ui::AX_ROLE_IMAGE
;
883 img
.location
= gfx::Rect(140, 100, 20, 20);
885 ui::AXNodeData static_text2
;
887 static_text2
.SetValue("CD");
888 static_text2
.role
= ui::AX_ROLE_STATIC_TEXT
;
889 static_text2
.location
= gfx::Rect(160, 100, 40, 20);
890 static_text2
.child_ids
.push_back(7);
892 ui::AXNodeData inline_text1
;
894 inline_text1
.SetValue("AB");
895 inline_text1
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
896 inline_text1
.location
= gfx::Rect(100, 100, 40, 20);
897 inline_text1
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
898 ui::AX_TEXT_DIRECTION_LTR
);
899 std::vector
<int32
> character_offsets1
;
900 character_offsets1
.push_back(20); // 0
901 character_offsets1
.push_back(40); // 1
902 inline_text1
.AddIntListAttribute(
903 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets1
);
905 ui::AXNodeData inline_text2
;
907 inline_text2
.SetValue("CD");
908 inline_text2
.role
= ui::AX_ROLE_INLINE_TEXT_BOX
;
909 inline_text2
.location
= gfx::Rect(160, 100, 40, 20);
910 inline_text2
.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
911 ui::AX_TEXT_DIRECTION_LTR
);
912 std::vector
<int32
> character_offsets2
;
913 character_offsets2
.push_back(20); // 0
914 character_offsets2
.push_back(40); // 1
915 inline_text2
.AddIntListAttribute(
916 ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets2
);
918 scoped_ptr
<BrowserAccessibilityManager
> manager(
919 BrowserAccessibilityManager::Create(
921 root
, div
, static_text1
, img
,
922 static_text2
, inline_text1
, inline_text2
),
924 new CountedBrowserAccessibilityFactory()));
925 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
927 EXPECT_EQ(gfx::Rect(100, 100, 20, 20).ToString(),
928 root_accessible
->GetLocalBoundsForRange(0, 1).ToString());
930 EXPECT_EQ(gfx::Rect(100, 100, 40, 20).ToString(),
931 root_accessible
->GetLocalBoundsForRange(0, 2).ToString());
933 EXPECT_EQ(gfx::Rect(100, 100, 80, 20).ToString(),
934 root_accessible
->GetLocalBoundsForRange(0, 3).ToString());
936 EXPECT_EQ(gfx::Rect(120, 100, 60, 20).ToString(),
937 root_accessible
->GetLocalBoundsForRange(1, 2).ToString());
939 EXPECT_EQ(gfx::Rect(120, 100, 80, 20).ToString(),
940 root_accessible
->GetLocalBoundsForRange(1, 3).ToString());
942 EXPECT_EQ(gfx::Rect(100, 100, 100, 20).ToString(),
943 root_accessible
->GetLocalBoundsForRange(0, 4).ToString());
946 TEST(BrowserAccessibilityManagerTest
, NextPreviousInTreeOrder
) {
949 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
951 ui::AXNodeData node2
;
953 root
.child_ids
.push_back(2);
955 ui::AXNodeData node3
;
957 root
.child_ids
.push_back(3);
959 ui::AXNodeData node4
;
961 node3
.child_ids
.push_back(4);
963 ui::AXNodeData node5
;
965 root
.child_ids
.push_back(5);
967 scoped_ptr
<BrowserAccessibilityManager
> manager(
968 BrowserAccessibilityManager::Create(
969 MakeAXTreeUpdate(root
, node2
, node3
, node4
, node5
),
971 new CountedBrowserAccessibilityFactory()));
973 BrowserAccessibility
* root_accessible
= manager
->GetRoot();
974 BrowserAccessibility
* node2_accessible
= root_accessible
->PlatformGetChild(0);
975 BrowserAccessibility
* node3_accessible
= root_accessible
->PlatformGetChild(1);
976 BrowserAccessibility
* node4_accessible
=
977 node3_accessible
->PlatformGetChild(0);
978 BrowserAccessibility
* node5_accessible
= root_accessible
->PlatformGetChild(2);
980 ASSERT_EQ(nullptr, manager
->NextInTreeOrder(nullptr));
981 ASSERT_EQ(node2_accessible
, manager
->NextInTreeOrder(root_accessible
));
982 ASSERT_EQ(node3_accessible
, manager
->NextInTreeOrder(node2_accessible
));
983 ASSERT_EQ(node4_accessible
, manager
->NextInTreeOrder(node3_accessible
));
984 ASSERT_EQ(node5_accessible
, manager
->NextInTreeOrder(node4_accessible
));
985 ASSERT_EQ(nullptr, manager
->NextInTreeOrder(node5_accessible
));
987 ASSERT_EQ(nullptr, manager
->PreviousInTreeOrder(nullptr));
988 ASSERT_EQ(node4_accessible
, manager
->PreviousInTreeOrder(node5_accessible
));
989 ASSERT_EQ(node3_accessible
, manager
->PreviousInTreeOrder(node4_accessible
));
990 ASSERT_EQ(node2_accessible
, manager
->PreviousInTreeOrder(node3_accessible
));
991 ASSERT_EQ(root_accessible
, manager
->PreviousInTreeOrder(node2_accessible
));
994 TEST(BrowserAccessibilityManagerTest
, DeletingFocusedNodeDoesNotCrash
) {
995 // Create a really simple tree with one root node and one focused child.
998 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
1000 root
.child_ids
.push_back(2);
1002 ui::AXNodeData node2
;
1004 node2
.state
= 1 << ui::AX_STATE_FOCUSED
;
1006 scoped_ptr
<BrowserAccessibilityManager
> manager(
1007 BrowserAccessibilityManager::Create(
1008 MakeAXTreeUpdate(root
, node2
),
1010 new CountedBrowserAccessibilityFactory()));
1012 ASSERT_EQ(1, manager
->GetRoot()->GetId());
1013 ASSERT_EQ(1, manager
->GetFocus(manager
->GetRoot())->GetId());
1015 // Send the focus event for node 2.
1016 std::vector
<AccessibilityHostMsg_EventParams
> events
;
1017 events
.push_back(AccessibilityHostMsg_EventParams());
1018 events
[0].update
= MakeAXTreeUpdate(node2
);
1020 events
[0].event_type
= ui::AX_EVENT_FOCUS
;
1021 manager
->OnAccessibilityEvents(events
);
1023 ASSERT_EQ(1, manager
->GetRoot()->GetId());
1024 ASSERT_EQ(2, manager
->GetFocus(manager
->GetRoot())->GetId());
1026 // Now replace the tree with a new tree consisting of a single root.
1027 ui::AXNodeData root2
;
1029 root2
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
1032 std::vector
<AccessibilityHostMsg_EventParams
> events2
;
1033 events2
.push_back(AccessibilityHostMsg_EventParams());
1034 events2
[0].update
= MakeAXTreeUpdate(root2
);
1036 events2
[0].event_type
= ui::AX_EVENT_NONE
;
1037 manager
->OnAccessibilityEvents(events2
);
1039 // Make sure that the focused node was updated to the new root and
1040 // that this doesn't crash.
1041 ASSERT_EQ(3, manager
->GetRoot()->GetId());
1042 ASSERT_EQ(3, manager
->GetFocus(manager
->GetRoot())->GetId());
1045 TEST(BrowserAccessibilityManagerTest
, DeletingFocusedNodeDoesNotCrash2
) {
1046 // Create a really simple tree with one root node and one focused child.
1047 ui::AXNodeData root
;
1049 root
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
1051 root
.child_ids
.push_back(2);
1052 root
.child_ids
.push_back(3);
1053 root
.child_ids
.push_back(4);
1055 ui::AXNodeData node2
;
1057 node2
.state
= 1 << ui::AX_STATE_FOCUSED
;
1059 ui::AXNodeData node3
;
1063 ui::AXNodeData node4
;
1067 scoped_ptr
<BrowserAccessibilityManager
> manager(
1068 BrowserAccessibilityManager::Create(
1069 MakeAXTreeUpdate(root
, node2
, node3
, node4
),
1071 new CountedBrowserAccessibilityFactory()));
1073 ASSERT_EQ(1, manager
->GetRoot()->GetId());
1074 ASSERT_EQ(1, manager
->GetFocus(manager
->GetRoot())->GetId());
1076 // Send the focus event for node 2.
1077 std::vector
<AccessibilityHostMsg_EventParams
> events
;
1078 events
.push_back(AccessibilityHostMsg_EventParams());
1079 events
[0].update
= MakeAXTreeUpdate(node2
);
1081 events
[0].event_type
= ui::AX_EVENT_FOCUS
;
1082 manager
->OnAccessibilityEvents(events
);
1084 ASSERT_EQ(1, manager
->GetRoot()->GetId());
1085 ASSERT_EQ(2, manager
->GetFocus(manager
->GetRoot())->GetId());
1087 // Now replace the tree with a new tree consisting of a single root.
1088 ui::AXNodeData root2
;
1090 root2
.role
= ui::AX_ROLE_ROOT_WEB_AREA
;
1093 // Make an update the explicitly clears the previous root.
1094 std::vector
<AccessibilityHostMsg_EventParams
> events2
;
1095 events2
.push_back(AccessibilityHostMsg_EventParams());
1096 events2
[0].update
= MakeAXTreeUpdate(root2
);
1097 events2
[0].update
.node_id_to_clear
= 1;
1099 events2
[0].event_type
= ui::AX_EVENT_NONE
;
1100 manager
->OnAccessibilityEvents(events2
);
1102 // Make sure that the focused node was updated to the new root and
1103 // that this doesn't crash.
1104 ASSERT_EQ(3, manager
->GetRoot()->GetId());
1105 ASSERT_EQ(3, manager
->GetFocus(manager
->GetRoot())->GetId());
1108 } // namespace content