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