Cross-process iframe accessibility.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_manager_unittest.cc
blob28bbf9d376b2ec5612c757fd257889ad8d18fed7
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"
9 #if defined(OS_WIN)
10 #include "content/browser/accessibility/browser_accessibility_win.h"
11 #endif
12 #include "content/common/accessibility_messages.h"
13 #include "testing/gtest/include/gtest/gtest.h"
15 namespace content {
16 namespace {
18 // Subclass of BrowserAccessibility that counts the number of instances.
19 class CountedBrowserAccessibility : public BrowserAccessibility {
20 public:
21 CountedBrowserAccessibility() {
22 global_obj_count_++;
23 native_ref_count_ = 1;
25 virtual ~CountedBrowserAccessibility() {
26 global_obj_count_--;
29 virtual void NativeAddReference() OVERRIDE {
30 native_ref_count_++;
33 virtual void NativeReleaseReference() OVERRIDE {
34 native_ref_count_--;
35 if (native_ref_count_ == 0)
36 delete this;
39 int native_ref_count_;
40 static int global_obj_count_;
42 #if defined(OS_WIN)
43 // Adds some padding to prevent a heap-buffer-overflow when an instance of
44 // this class is casted into a BrowserAccessibilityWin pointer.
45 // http://crbug.com/235508
46 // TODO(dmazzoni): Fix this properly.
47 static const size_t kDataSize = sizeof(int) + sizeof(BrowserAccessibility);
48 uint8 padding_[sizeof(BrowserAccessibilityWin) - kDataSize];
49 #endif
52 int CountedBrowserAccessibility::global_obj_count_ = 0;
54 // Factory that creates a CountedBrowserAccessibility.
55 class CountedBrowserAccessibilityFactory
56 : public BrowserAccessibilityFactory {
57 public:
58 virtual ~CountedBrowserAccessibilityFactory() {}
59 virtual BrowserAccessibility* Create() OVERRIDE {
60 return new CountedBrowserAccessibility();
64 class TestBrowserAccessibilityDelegate
65 : public BrowserAccessibilityDelegate {
66 public:
67 TestBrowserAccessibilityDelegate()
68 : got_fatal_error_(false) {}
70 virtual void AccessibilitySetFocus(int acc_obj_id) OVERRIDE {}
71 virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE {}
72 virtual void AccessibilityShowMenu(const gfx::Point& point) OVERRIDE {}
73 virtual void AccessibilityScrollToMakeVisible(
74 int acc_obj_id, const gfx::Rect& subfocus) OVERRIDE {}
75 virtual void AccessibilityScrollToPoint(
76 int acc_obj_id, const gfx::Point& point) OVERRIDE {}
77 virtual void AccessibilitySetTextSelection(
78 int acc_obj_id, int start_offset, int end_offset) OVERRIDE {}
79 virtual bool AccessibilityViewHasFocus() const OVERRIDE {
80 return false;
82 virtual gfx::Rect AccessibilityGetViewBounds() const OVERRIDE {
83 return gfx::Rect();
85 virtual gfx::Point AccessibilityOriginInScreen(
86 const gfx::Rect& bounds) const OVERRIDE {
87 return gfx::Point();
89 virtual void AccessibilityHitTest(const gfx::Point& point) OVERRIDE {}
90 virtual void AccessibilityFatalError() OVERRIDE {
91 got_fatal_error_ = true;
93 virtual gfx::AcceleratedWidget AccessibilityGetAcceleratedWidget() OVERRIDE {
94 return gfx::kNullAcceleratedWidget;
96 virtual gfx::NativeViewAccessible AccessibilityGetNativeViewAccessible()
97 OVERRIDE {
98 return NULL;
100 virtual BrowserAccessibilityManager* AccessibilityGetChildFrame(
101 int64 frame_tree_node_id) OVERRIDE {
102 return NULL;
104 virtual BrowserAccessibilityManager* AccessibilityGetParentFrame() OVERRIDE {
105 return NULL;
108 bool got_fatal_error() const { return got_fatal_error_; }
109 void reset_got_fatal_error() { got_fatal_error_ = false; }
111 private:
112 bool got_fatal_error_;
115 } // anonymous namespace
117 TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
118 // Create ui::AXNodeData objects for a simple document tree,
119 // representing the accessibility information used to initialize
120 // BrowserAccessibilityManager.
121 ui::AXNodeData button;
122 button.id = 2;
123 button.SetName("Button");
124 button.role = ui::AX_ROLE_BUTTON;
125 button.state = 0;
127 ui::AXNodeData checkbox;
128 checkbox.id = 3;
129 checkbox.SetName("Checkbox");
130 checkbox.role = ui::AX_ROLE_CHECK_BOX;
131 checkbox.state = 0;
133 ui::AXNodeData root;
134 root.id = 1;
135 root.SetName("Document");
136 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
137 root.state = 0;
138 root.child_ids.push_back(2);
139 root.child_ids.push_back(3);
141 // Construct a BrowserAccessibilityManager with this
142 // ui::AXNodeData tree and a factory for an instance-counting
143 // BrowserAccessibility, and ensure that exactly 3 instances were
144 // created. Note that the manager takes ownership of the factory.
145 CountedBrowserAccessibility::global_obj_count_ = 0;
146 BrowserAccessibilityManager* manager =
147 BrowserAccessibilityManager::Create(
148 MakeAXTreeUpdate(root, button, checkbox),
149 NULL,
150 new CountedBrowserAccessibilityFactory());
152 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
154 // Delete the manager and test that all 3 instances are deleted.
155 delete manager;
156 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
158 // Construct a manager again, and this time save references to two of
159 // the three nodes in the tree.
160 manager =
161 BrowserAccessibilityManager::Create(
162 MakeAXTreeUpdate(root, button, checkbox),
163 NULL,
164 new CountedBrowserAccessibilityFactory());
165 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
167 CountedBrowserAccessibility* root_accessible =
168 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
169 root_accessible->NativeAddReference();
170 CountedBrowserAccessibility* child1_accessible =
171 static_cast<CountedBrowserAccessibility*>(
172 root_accessible->PlatformGetChild(1));
173 child1_accessible->NativeAddReference();
175 // Now delete the manager, and only one of the three nodes in the tree
176 // should be released.
177 delete manager;
178 ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_);
180 // Release each of our references and make sure that each one results in
181 // the instance being deleted as its reference count hits zero.
182 root_accessible->NativeReleaseReference();
183 ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_);
184 child1_accessible->NativeReleaseReference();
185 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
188 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
189 // Make sure that changes to a subtree reuse as many objects as possible.
191 // Tree 1:
193 // root
194 // child1
195 // child2
196 // child3
198 ui::AXNodeData tree1_child1;
199 tree1_child1.id = 2;
200 tree1_child1.SetName("Child1");
201 tree1_child1.role = ui::AX_ROLE_BUTTON;
202 tree1_child1.state = 0;
204 ui::AXNodeData tree1_child2;
205 tree1_child2.id = 3;
206 tree1_child2.SetName("Child2");
207 tree1_child2.role = ui::AX_ROLE_BUTTON;
208 tree1_child2.state = 0;
210 ui::AXNodeData tree1_child3;
211 tree1_child3.id = 4;
212 tree1_child3.SetName("Child3");
213 tree1_child3.role = ui::AX_ROLE_BUTTON;
214 tree1_child3.state = 0;
216 ui::AXNodeData tree1_root;
217 tree1_root.id = 1;
218 tree1_root.SetName("Document");
219 tree1_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
220 tree1_root.state = 0;
221 tree1_root.child_ids.push_back(2);
222 tree1_root.child_ids.push_back(3);
223 tree1_root.child_ids.push_back(4);
225 // Tree 2:
227 // root
228 // child0 <-- inserted
229 // child1
230 // child2
231 // <-- child3 deleted
233 ui::AXNodeData tree2_child0;
234 tree2_child0.id = 5;
235 tree2_child0.SetName("Child0");
236 tree2_child0.role = ui::AX_ROLE_BUTTON;
237 tree2_child0.state = 0;
239 ui::AXNodeData tree2_root;
240 tree2_root.id = 1;
241 tree2_root.SetName("DocumentChanged");
242 tree2_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
243 tree2_root.state = 0;
244 tree2_root.child_ids.push_back(5);
245 tree2_root.child_ids.push_back(2);
246 tree2_root.child_ids.push_back(3);
248 // Construct a BrowserAccessibilityManager with tree1.
249 CountedBrowserAccessibility::global_obj_count_ = 0;
250 BrowserAccessibilityManager* manager =
251 BrowserAccessibilityManager::Create(
252 MakeAXTreeUpdate(tree1_root,
253 tree1_child1, tree1_child2, tree1_child3),
254 NULL,
255 new CountedBrowserAccessibilityFactory());
256 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
258 // Save references to all of the objects.
259 CountedBrowserAccessibility* root_accessible =
260 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
261 root_accessible->NativeAddReference();
262 CountedBrowserAccessibility* child1_accessible =
263 static_cast<CountedBrowserAccessibility*>(
264 root_accessible->PlatformGetChild(0));
265 child1_accessible->NativeAddReference();
266 CountedBrowserAccessibility* child2_accessible =
267 static_cast<CountedBrowserAccessibility*>(
268 root_accessible->PlatformGetChild(1));
269 child2_accessible->NativeAddReference();
270 CountedBrowserAccessibility* child3_accessible =
271 static_cast<CountedBrowserAccessibility*>(
272 root_accessible->PlatformGetChild(2));
273 child3_accessible->NativeAddReference();
275 // Check the index in parent.
276 EXPECT_EQ(0, child1_accessible->GetIndexInParent());
277 EXPECT_EQ(1, child2_accessible->GetIndexInParent());
278 EXPECT_EQ(2, child3_accessible->GetIndexInParent());
280 // Process a notification containing the changed subtree.
281 std::vector<AccessibilityHostMsg_EventParams> params;
282 params.push_back(AccessibilityHostMsg_EventParams());
283 AccessibilityHostMsg_EventParams* msg = &params[0];
284 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
285 msg->update.nodes.push_back(tree2_root);
286 msg->update.nodes.push_back(tree2_child0);
287 msg->id = tree2_root.id;
288 manager->OnAccessibilityEvents(params);
290 // There should be 5 objects now: the 4 from the new tree, plus the
291 // reference to child3 we kept.
292 EXPECT_EQ(5, CountedBrowserAccessibility::global_obj_count_);
294 // Check that our references to the root, child1, and child2 are still valid,
295 // but that the reference to child3 is now invalid.
296 EXPECT_TRUE(root_accessible->instance_active());
297 EXPECT_TRUE(child1_accessible->instance_active());
298 EXPECT_TRUE(child2_accessible->instance_active());
299 EXPECT_FALSE(child3_accessible->instance_active());
301 // Check that the index in parent has been updated.
302 EXPECT_EQ(1, child1_accessible->GetIndexInParent());
303 EXPECT_EQ(2, child2_accessible->GetIndexInParent());
305 // Release our references. The object count should only decrease by 1
306 // for child3.
307 root_accessible->NativeReleaseReference();
308 child1_accessible->NativeReleaseReference();
309 child2_accessible->NativeReleaseReference();
310 child3_accessible->NativeReleaseReference();
312 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
314 // Delete the manager and make sure all memory is cleaned up.
315 delete manager;
316 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
319 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
320 // Similar to the test above, but with a more complicated tree.
322 // Tree 1:
324 // root
325 // container
326 // child1
327 // grandchild1
328 // child2
329 // grandchild2
330 // child3
331 // grandchild3
333 ui::AXNodeData tree1_grandchild1;
334 tree1_grandchild1.id = 4;
335 tree1_grandchild1.SetName("GrandChild1");
336 tree1_grandchild1.role = ui::AX_ROLE_BUTTON;
337 tree1_grandchild1.state = 0;
339 ui::AXNodeData tree1_child1;
340 tree1_child1.id = 3;
341 tree1_child1.SetName("Child1");
342 tree1_child1.role = ui::AX_ROLE_BUTTON;
343 tree1_child1.state = 0;
344 tree1_child1.child_ids.push_back(4);
346 ui::AXNodeData tree1_grandchild2;
347 tree1_grandchild2.id = 6;
348 tree1_grandchild2.SetName("GrandChild1");
349 tree1_grandchild2.role = ui::AX_ROLE_BUTTON;
350 tree1_grandchild2.state = 0;
352 ui::AXNodeData tree1_child2;
353 tree1_child2.id = 5;
354 tree1_child2.SetName("Child2");
355 tree1_child2.role = ui::AX_ROLE_BUTTON;
356 tree1_child2.state = 0;
357 tree1_child2.child_ids.push_back(6);
359 ui::AXNodeData tree1_grandchild3;
360 tree1_grandchild3.id = 8;
361 tree1_grandchild3.SetName("GrandChild3");
362 tree1_grandchild3.role = ui::AX_ROLE_BUTTON;
363 tree1_grandchild3.state = 0;
365 ui::AXNodeData tree1_child3;
366 tree1_child3.id = 7;
367 tree1_child3.SetName("Child3");
368 tree1_child3.role = ui::AX_ROLE_BUTTON;
369 tree1_child3.state = 0;
370 tree1_child3.child_ids.push_back(8);
372 ui::AXNodeData tree1_container;
373 tree1_container.id = 2;
374 tree1_container.SetName("Container");
375 tree1_container.role = ui::AX_ROLE_GROUP;
376 tree1_container.state = 0;
377 tree1_container.child_ids.push_back(3);
378 tree1_container.child_ids.push_back(5);
379 tree1_container.child_ids.push_back(7);
381 ui::AXNodeData tree1_root;
382 tree1_root.id = 1;
383 tree1_root.SetName("Document");
384 tree1_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
385 tree1_root.state = 0;
386 tree1_root.child_ids.push_back(2);
388 // Tree 2:
390 // root
391 // container
392 // child0 <-- inserted
393 // grandchild0 <--
394 // child1
395 // grandchild1
396 // child2
397 // grandchild2
398 // <-- child3 (and grandchild3) deleted
400 ui::AXNodeData tree2_grandchild0;
401 tree2_grandchild0.id = 9;
402 tree2_grandchild0.SetName("GrandChild0");
403 tree2_grandchild0.role = ui::AX_ROLE_BUTTON;
404 tree2_grandchild0.state = 0;
406 ui::AXNodeData tree2_child0;
407 tree2_child0.id = 10;
408 tree2_child0.SetName("Child0");
409 tree2_child0.role = ui::AX_ROLE_BUTTON;
410 tree2_child0.state = 0;
411 tree2_child0.child_ids.push_back(9);
413 ui::AXNodeData tree2_container;
414 tree2_container.id = 2;
415 tree2_container.SetName("Container");
416 tree2_container.role = ui::AX_ROLE_GROUP;
417 tree2_container.state = 0;
418 tree2_container.child_ids.push_back(10);
419 tree2_container.child_ids.push_back(3);
420 tree2_container.child_ids.push_back(5);
422 // Construct a BrowserAccessibilityManager with tree1.
423 CountedBrowserAccessibility::global_obj_count_ = 0;
424 BrowserAccessibilityManager* manager =
425 BrowserAccessibilityManager::Create(
426 MakeAXTreeUpdate(tree1_root, tree1_container,
427 tree1_child1, tree1_grandchild1,
428 tree1_child2, tree1_grandchild2,
429 tree1_child3, tree1_grandchild3),
430 NULL,
431 new CountedBrowserAccessibilityFactory());
432 ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
434 // Save references to some objects.
435 CountedBrowserAccessibility* root_accessible =
436 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
437 root_accessible->NativeAddReference();
438 CountedBrowserAccessibility* container_accessible =
439 static_cast<CountedBrowserAccessibility*>(
440 root_accessible->PlatformGetChild(0));
441 container_accessible->NativeAddReference();
442 CountedBrowserAccessibility* child2_accessible =
443 static_cast<CountedBrowserAccessibility*>(
444 container_accessible->PlatformGetChild(1));
445 child2_accessible->NativeAddReference();
446 CountedBrowserAccessibility* child3_accessible =
447 static_cast<CountedBrowserAccessibility*>(
448 container_accessible->PlatformGetChild(2));
449 child3_accessible->NativeAddReference();
451 // Check the index in parent.
452 EXPECT_EQ(1, child2_accessible->GetIndexInParent());
453 EXPECT_EQ(2, child3_accessible->GetIndexInParent());
455 // Process a notification containing the changed subtree rooted at
456 // the container.
457 std::vector<AccessibilityHostMsg_EventParams> params;
458 params.push_back(AccessibilityHostMsg_EventParams());
459 AccessibilityHostMsg_EventParams* msg = &params[0];
460 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
461 msg->update.nodes.push_back(tree2_container);
462 msg->update.nodes.push_back(tree2_child0);
463 msg->update.nodes.push_back(tree2_grandchild0);
464 msg->id = tree2_container.id;
465 manager->OnAccessibilityEvents(params);
467 // There should be 9 objects now: the 8 from the new tree, plus the
468 // reference to child3 we kept.
469 EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_);
471 // Check that our references to the root and container and child2 are
472 // still valid, but that the reference to child3 is now invalid.
473 EXPECT_TRUE(root_accessible->instance_active());
474 EXPECT_TRUE(container_accessible->instance_active());
475 EXPECT_TRUE(child2_accessible->instance_active());
476 EXPECT_FALSE(child3_accessible->instance_active());
478 // Ensure that we retain the parent of the detached subtree.
479 EXPECT_EQ(root_accessible, container_accessible->GetParent());
480 EXPECT_EQ(0, container_accessible->GetIndexInParent());
482 // Check that the index in parent has been updated.
483 EXPECT_EQ(2, child2_accessible->GetIndexInParent());
485 // Release our references. The object count should only decrease by 1
486 // for child3.
487 root_accessible->NativeReleaseReference();
488 container_accessible->NativeReleaseReference();
489 child2_accessible->NativeReleaseReference();
490 child3_accessible->NativeReleaseReference();
492 EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
494 // Delete the manager and make sure all memory is cleaned up.
495 delete manager;
496 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
499 TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
500 // Tree 1:
502 // 1
503 // 2
504 // 3
505 // 4
507 ui::AXNodeData tree1_4;
508 tree1_4.id = 4;
509 tree1_4.state = 0;
511 ui::AXNodeData tree1_3;
512 tree1_3.id = 3;
513 tree1_3.state = 0;
514 tree1_3.child_ids.push_back(4);
516 ui::AXNodeData tree1_2;
517 tree1_2.id = 2;
518 tree1_2.state = 0;
520 ui::AXNodeData tree1_1;
521 tree1_1.id = 1;
522 tree1_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
523 tree1_1.state = 0;
524 tree1_1.child_ids.push_back(2);
525 tree1_1.child_ids.push_back(3);
527 // Tree 2:
529 // 1
530 // 4 <-- moves up a level and gains child
531 // 6 <-- new
532 // 5 <-- new
534 ui::AXNodeData tree2_6;
535 tree2_6.id = 6;
536 tree2_6.state = 0;
538 ui::AXNodeData tree2_5;
539 tree2_5.id = 5;
540 tree2_5.state = 0;
542 ui::AXNodeData tree2_4;
543 tree2_4.id = 4;
544 tree2_4.state = 0;
545 tree2_4.child_ids.push_back(6);
547 ui::AXNodeData tree2_1;
548 tree2_1.id = 1;
549 tree2_1.state = 0;
550 tree2_1.child_ids.push_back(4);
551 tree2_1.child_ids.push_back(5);
553 // Construct a BrowserAccessibilityManager with tree1.
554 CountedBrowserAccessibility::global_obj_count_ = 0;
555 BrowserAccessibilityManager* manager =
556 BrowserAccessibilityManager::Create(
557 MakeAXTreeUpdate(tree1_1, tree1_2, tree1_3, tree1_4),
558 NULL,
559 new CountedBrowserAccessibilityFactory());
560 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
562 // Process a notification containing the changed subtree.
563 std::vector<AccessibilityHostMsg_EventParams> params;
564 params.push_back(AccessibilityHostMsg_EventParams());
565 AccessibilityHostMsg_EventParams* msg = &params[0];
566 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
567 msg->update.nodes.push_back(tree2_1);
568 msg->update.nodes.push_back(tree2_4);
569 msg->update.nodes.push_back(tree2_5);
570 msg->update.nodes.push_back(tree2_6);
571 msg->id = tree2_1.id;
572 manager->OnAccessibilityEvents(params);
574 // There should be 4 objects now.
575 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
577 // Delete the manager and make sure all memory is cleaned up.
578 delete manager;
579 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
582 TEST(BrowserAccessibilityManagerTest, TestFatalError) {
583 // Test that BrowserAccessibilityManager raises a fatal error
584 // (which will crash the renderer) if the same id is used in
585 // two places in the tree.
587 ui::AXNodeData root;
588 root.id = 1;
589 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
590 root.child_ids.push_back(2);
591 root.child_ids.push_back(2);
593 CountedBrowserAccessibilityFactory* factory =
594 new CountedBrowserAccessibilityFactory();
595 scoped_ptr<TestBrowserAccessibilityDelegate> delegate(
596 new TestBrowserAccessibilityDelegate());
597 scoped_ptr<BrowserAccessibilityManager> manager;
598 ASSERT_FALSE(delegate->got_fatal_error());
599 manager.reset(BrowserAccessibilityManager::Create(
600 MakeAXTreeUpdate(root),
601 delegate.get(),
602 factory));
603 ASSERT_TRUE(delegate->got_fatal_error());
605 ui::AXNodeData root2;
606 root2.id = 1;
607 root2.role = ui::AX_ROLE_ROOT_WEB_AREA;
608 root2.child_ids.push_back(2);
609 root2.child_ids.push_back(3);
611 ui::AXNodeData child1;
612 child1.id = 2;
613 child1.child_ids.push_back(4);
614 child1.child_ids.push_back(5);
616 ui::AXNodeData child2;
617 child2.id = 3;
618 child2.child_ids.push_back(6);
619 child2.child_ids.push_back(5); // Duplicate
621 ui::AXNodeData grandchild4;
622 grandchild4.id = 4;
624 ui::AXNodeData grandchild5;
625 grandchild5.id = 5;
627 ui::AXNodeData grandchild6;
628 grandchild6.id = 6;
630 delegate->reset_got_fatal_error();
631 factory = new CountedBrowserAccessibilityFactory();
632 manager.reset(BrowserAccessibilityManager::Create(
633 MakeAXTreeUpdate(root2, child1, child2,
634 grandchild4, grandchild5, grandchild6),
635 delegate.get(),
636 factory));
637 ASSERT_TRUE(delegate->got_fatal_error());
640 TEST(BrowserAccessibilityManagerTest, BoundsForRange) {
641 ui::AXNodeData root;
642 root.id = 1;
643 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
645 ui::AXNodeData static_text;
646 static_text.id = 2;
647 static_text.SetValue("Hello, world.");
648 static_text.role = ui::AX_ROLE_STATIC_TEXT;
649 static_text.location = gfx::Rect(100, 100, 29, 18);
650 root.child_ids.push_back(2);
652 ui::AXNodeData inline_text1;
653 inline_text1.id = 3;
654 inline_text1.SetValue("Hello, ");
655 inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
656 inline_text1.location = gfx::Rect(100, 100, 29, 9);
657 inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
658 ui::AX_TEXT_DIRECTION_LR);
659 std::vector<int32> character_offsets1;
660 character_offsets1.push_back(6); // 0
661 character_offsets1.push_back(11); // 1
662 character_offsets1.push_back(16); // 2
663 character_offsets1.push_back(21); // 3
664 character_offsets1.push_back(26); // 4
665 character_offsets1.push_back(29); // 5
666 character_offsets1.push_back(29); // 6 (note that the space has no width)
667 inline_text1.AddIntListAttribute(
668 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
669 static_text.child_ids.push_back(3);
671 ui::AXNodeData inline_text2;
672 inline_text2.id = 4;
673 inline_text2.SetValue("world.");
674 inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
675 inline_text2.location = gfx::Rect(100, 109, 28, 9);
676 inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
677 ui::AX_TEXT_DIRECTION_LR);
678 std::vector<int32> character_offsets2;
679 character_offsets2.push_back(5);
680 character_offsets2.push_back(10);
681 character_offsets2.push_back(15);
682 character_offsets2.push_back(20);
683 character_offsets2.push_back(25);
684 character_offsets2.push_back(28);
685 inline_text2.AddIntListAttribute(
686 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
687 static_text.child_ids.push_back(4);
689 scoped_ptr<BrowserAccessibilityManager> manager(
690 BrowserAccessibilityManager::Create(
691 MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
692 NULL,
693 new CountedBrowserAccessibilityFactory()));
695 BrowserAccessibility* root_accessible = manager->GetRoot();
696 BrowserAccessibility* static_text_accessible =
697 root_accessible->PlatformGetChild(0);
699 EXPECT_EQ(gfx::Rect(100, 100, 6, 9).ToString(),
700 static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
702 EXPECT_EQ(gfx::Rect(100, 100, 26, 9).ToString(),
703 static_text_accessible->GetLocalBoundsForRange(0, 5).ToString());
705 EXPECT_EQ(gfx::Rect(100, 109, 5, 9).ToString(),
706 static_text_accessible->GetLocalBoundsForRange(7, 1).ToString());
708 EXPECT_EQ(gfx::Rect(100, 109, 25, 9).ToString(),
709 static_text_accessible->GetLocalBoundsForRange(7, 5).ToString());
711 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
712 static_text_accessible->GetLocalBoundsForRange(5, 3).ToString());
714 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
715 static_text_accessible->GetLocalBoundsForRange(0, 13).ToString());
717 // Test range that's beyond the text.
718 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
719 static_text_accessible->GetLocalBoundsForRange(-1, 999).ToString());
721 // Test that we can call bounds for range on the parent element, too,
722 // and it still works.
723 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
724 root_accessible->GetLocalBoundsForRange(0, 13).ToString());
727 TEST(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) {
728 // In this example, we assume that the string "123abc" is rendered with
729 // "123" going left-to-right and "abc" going right-to-left. In other
730 // words, on-screen it would look like "123cba". This is possible to
731 // acheive if the source string had unicode control characters
732 // to switch directions. This test doesn't worry about how, though - it just
733 // tests that if something like that were to occur, GetLocalBoundsForRange
734 // returns the correct bounds for different ranges.
736 ui::AXNodeData root;
737 root.id = 1;
738 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
740 ui::AXNodeData static_text;
741 static_text.id = 2;
742 static_text.SetValue("123abc");
743 static_text.role = ui::AX_ROLE_STATIC_TEXT;
744 static_text.location = gfx::Rect(100, 100, 60, 20);
745 root.child_ids.push_back(2);
747 ui::AXNodeData inline_text1;
748 inline_text1.id = 3;
749 inline_text1.SetValue("123");
750 inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
751 inline_text1.location = gfx::Rect(100, 100, 30, 20);
752 inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
753 ui::AX_TEXT_DIRECTION_LR);
754 std::vector<int32> character_offsets1;
755 character_offsets1.push_back(10); // 0
756 character_offsets1.push_back(20); // 1
757 character_offsets1.push_back(30); // 2
758 inline_text1.AddIntListAttribute(
759 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
760 static_text.child_ids.push_back(3);
762 ui::AXNodeData inline_text2;
763 inline_text2.id = 4;
764 inline_text2.SetValue("abc");
765 inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
766 inline_text2.location = gfx::Rect(130, 100, 30, 20);
767 inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
768 ui::AX_TEXT_DIRECTION_RL);
769 std::vector<int32> character_offsets2;
770 character_offsets2.push_back(10);
771 character_offsets2.push_back(20);
772 character_offsets2.push_back(30);
773 inline_text2.AddIntListAttribute(
774 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
775 static_text.child_ids.push_back(4);
777 scoped_ptr<BrowserAccessibilityManager> manager(
778 BrowserAccessibilityManager::Create(
779 MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
780 NULL,
781 new CountedBrowserAccessibilityFactory()));
783 BrowserAccessibility* root_accessible = manager->GetRoot();
784 BrowserAccessibility* static_text_accessible =
785 root_accessible->PlatformGetChild(0);
787 EXPECT_EQ(gfx::Rect(100, 100, 60, 20).ToString(),
788 static_text_accessible->GetLocalBoundsForRange(0, 6).ToString());
790 EXPECT_EQ(gfx::Rect(100, 100, 10, 20).ToString(),
791 static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
793 EXPECT_EQ(gfx::Rect(100, 100, 30, 20).ToString(),
794 static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
796 EXPECT_EQ(gfx::Rect(150, 100, 10, 20).ToString(),
797 static_text_accessible->GetLocalBoundsForRange(3, 1).ToString());
799 EXPECT_EQ(gfx::Rect(130, 100, 30, 20).ToString(),
800 static_text_accessible->GetLocalBoundsForRange(3, 3).ToString());
802 // This range is only two characters, but because of the direction switch
803 // the bounds are as wide as four characters.
804 EXPECT_EQ(gfx::Rect(120, 100, 40, 20).ToString(),
805 static_text_accessible->GetLocalBoundsForRange(2, 2).ToString());
808 #if defined(OS_WIN)
809 #define MAYBE_BoundsForRangeOnParentElement \
810 DISABLED_BoundsForRangeOnParentElement
811 #else
812 #define MAYBE_BoundsForRangeOnParentElement BoundsForRangeOnParentElement
813 #endif
814 TEST(BrowserAccessibilityManagerTest, MAYBE_BoundsForRangeOnParentElement) {
815 ui::AXNodeData root;
816 root.id = 1;
817 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
818 root.child_ids.push_back(2);
820 ui::AXNodeData div;
821 div.id = 2;
822 div.role = ui::AX_ROLE_DIV;
823 div.location = gfx::Rect(100, 100, 100, 20);
824 div.child_ids.push_back(3);
825 div.child_ids.push_back(4);
826 div.child_ids.push_back(5);
828 ui::AXNodeData static_text1;
829 static_text1.id = 3;
830 static_text1.SetValue("AB");
831 static_text1.role = ui::AX_ROLE_STATIC_TEXT;
832 static_text1.location = gfx::Rect(100, 100, 40, 20);
833 static_text1.child_ids.push_back(6);
835 ui::AXNodeData img;
836 img.id = 4;
837 img.role = ui::AX_ROLE_IMAGE;
838 img.location = gfx::Rect(140, 100, 20, 20);
840 ui::AXNodeData static_text2;
841 static_text2.id = 5;
842 static_text2.SetValue("CD");
843 static_text2.role = ui::AX_ROLE_STATIC_TEXT;
844 static_text2.location = gfx::Rect(160, 100, 40, 20);
845 static_text2.child_ids.push_back(7);
847 ui::AXNodeData inline_text1;
848 inline_text1.id = 6;
849 inline_text1.SetValue("AB");
850 inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
851 inline_text1.location = gfx::Rect(100, 100, 40, 20);
852 inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
853 ui::AX_TEXT_DIRECTION_LR);
854 std::vector<int32> character_offsets1;
855 character_offsets1.push_back(20); // 0
856 character_offsets1.push_back(40); // 1
857 inline_text1.AddIntListAttribute(
858 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
860 ui::AXNodeData inline_text2;
861 inline_text2.id = 7;
862 inline_text2.SetValue("CD");
863 inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
864 inline_text2.location = gfx::Rect(160, 100, 40, 20);
865 inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
866 ui::AX_TEXT_DIRECTION_LR);
867 std::vector<int32> character_offsets2;
868 character_offsets2.push_back(20); // 0
869 character_offsets2.push_back(40); // 1
870 inline_text2.AddIntListAttribute(
871 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
873 scoped_ptr<BrowserAccessibilityManager> manager(
874 BrowserAccessibilityManager::Create(
875 MakeAXTreeUpdate(
876 root, div, static_text1, img,
877 static_text2, inline_text1, inline_text2),
878 NULL,
879 new CountedBrowserAccessibilityFactory()));
880 BrowserAccessibility* root_accessible = manager->GetRoot();
882 EXPECT_EQ(gfx::Rect(100, 100, 20, 20).ToString(),
883 root_accessible->GetLocalBoundsForRange(0, 1).ToString());
885 EXPECT_EQ(gfx::Rect(100, 100, 40, 20).ToString(),
886 root_accessible->GetLocalBoundsForRange(0, 2).ToString());
888 EXPECT_EQ(gfx::Rect(100, 100, 80, 20).ToString(),
889 root_accessible->GetLocalBoundsForRange(0, 3).ToString());
891 EXPECT_EQ(gfx::Rect(120, 100, 60, 20).ToString(),
892 root_accessible->GetLocalBoundsForRange(1, 2).ToString());
894 EXPECT_EQ(gfx::Rect(120, 100, 80, 20).ToString(),
895 root_accessible->GetLocalBoundsForRange(1, 3).ToString());
897 EXPECT_EQ(gfx::Rect(100, 100, 100, 20).ToString(),
898 root_accessible->GetLocalBoundsForRange(0, 4).ToString());
901 TEST(BrowserAccessibilityManagerTest, NextPreviousInTreeOrder) {
902 ui::AXNodeData root;
903 root.id = 1;
904 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
906 ui::AXNodeData node2;
907 node2.id = 2;
908 root.child_ids.push_back(2);
910 ui::AXNodeData node3;
911 node3.id = 3;
912 root.child_ids.push_back(3);
914 ui::AXNodeData node4;
915 node4.id = 4;
916 node3.child_ids.push_back(4);
918 ui::AXNodeData node5;
919 node5.id = 5;
920 root.child_ids.push_back(5);
922 scoped_ptr<BrowserAccessibilityManager> manager(
923 BrowserAccessibilityManager::Create(
924 MakeAXTreeUpdate(root, node2, node3, node4, node5),
925 NULL,
926 new CountedBrowserAccessibilityFactory()));
928 BrowserAccessibility* root_accessible = manager->GetRoot();
929 BrowserAccessibility* node2_accessible = root_accessible->PlatformGetChild(0);
930 BrowserAccessibility* node3_accessible = root_accessible->PlatformGetChild(1);
931 BrowserAccessibility* node4_accessible =
932 node3_accessible->PlatformGetChild(0);
933 BrowserAccessibility* node5_accessible = root_accessible->PlatformGetChild(2);
935 ASSERT_EQ(NULL, manager->NextInTreeOrder(NULL));
936 ASSERT_EQ(node2_accessible, manager->NextInTreeOrder(root_accessible));
937 ASSERT_EQ(node3_accessible, manager->NextInTreeOrder(node2_accessible));
938 ASSERT_EQ(node4_accessible, manager->NextInTreeOrder(node3_accessible));
939 ASSERT_EQ(node5_accessible, manager->NextInTreeOrder(node4_accessible));
940 ASSERT_EQ(NULL, manager->NextInTreeOrder(node5_accessible));
942 ASSERT_EQ(NULL, manager->PreviousInTreeOrder(NULL));
943 ASSERT_EQ(node4_accessible, manager->PreviousInTreeOrder(node5_accessible));
944 ASSERT_EQ(node3_accessible, manager->PreviousInTreeOrder(node4_accessible));
945 ASSERT_EQ(node2_accessible, manager->PreviousInTreeOrder(node3_accessible));
946 ASSERT_EQ(root_accessible, manager->PreviousInTreeOrder(node2_accessible));
949 } // namespace content