Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_manager_unittest.cc
blob1b831615879dbac25eed8a39e144c5055472b632
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 AccessibilitySetAccessibilityFocus(int acc_obj_id) override {}
86 void AccessibilityFatalError() override { got_fatal_error_ = true; }
87 gfx::AcceleratedWidget AccessibilityGetAcceleratedWidget() override {
88 return gfx::kNullAcceleratedWidget;
90 gfx::NativeViewAccessible AccessibilityGetNativeViewAccessible() override {
91 return NULL;
93 BrowserAccessibilityManager* AccessibilityGetChildFrame(
94 int accessibility_node_id) override {
95 return NULL;
97 BrowserAccessibility* AccessibilityGetParentFrame() override { return NULL; }
98 void AccessibilityGetAllChildFrames(
99 std::vector<BrowserAccessibilityManager*>* child_frames) override {}
101 bool got_fatal_error() const { return got_fatal_error_; }
102 void reset_got_fatal_error() { got_fatal_error_ = false; }
104 private:
105 bool got_fatal_error_;
108 } // anonymous namespace
110 TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
111 // Create ui::AXNodeData objects for a simple document tree,
112 // representing the accessibility information used to initialize
113 // BrowserAccessibilityManager.
114 ui::AXNodeData button;
115 button.id = 2;
116 button.SetName("Button");
117 button.role = ui::AX_ROLE_BUTTON;
118 button.state = 0;
120 ui::AXNodeData checkbox;
121 checkbox.id = 3;
122 checkbox.SetName("Checkbox");
123 checkbox.role = ui::AX_ROLE_CHECK_BOX;
124 checkbox.state = 0;
126 ui::AXNodeData root;
127 root.id = 1;
128 root.SetName("Document");
129 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
130 root.state = 0;
131 root.child_ids.push_back(2);
132 root.child_ids.push_back(3);
134 // Construct a BrowserAccessibilityManager with this
135 // ui::AXNodeData tree and a factory for an instance-counting
136 // BrowserAccessibility, and ensure that exactly 3 instances were
137 // created. Note that the manager takes ownership of the factory.
138 CountedBrowserAccessibility::global_obj_count_ = 0;
139 BrowserAccessibilityManager* manager =
140 BrowserAccessibilityManager::Create(
141 MakeAXTreeUpdate(root, button, checkbox),
142 NULL,
143 new CountedBrowserAccessibilityFactory());
145 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
147 // Delete the manager and test that all 3 instances are deleted.
148 delete manager;
149 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
151 // Construct a manager again, and this time save references to two of
152 // the three nodes in the tree.
153 manager =
154 BrowserAccessibilityManager::Create(
155 MakeAXTreeUpdate(root, button, checkbox),
156 NULL,
157 new CountedBrowserAccessibilityFactory());
158 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
160 CountedBrowserAccessibility* root_accessible =
161 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
162 root_accessible->NativeAddReference();
163 CountedBrowserAccessibility* child1_accessible =
164 static_cast<CountedBrowserAccessibility*>(
165 root_accessible->PlatformGetChild(1));
166 child1_accessible->NativeAddReference();
168 // Now delete the manager, and only one of the three nodes in the tree
169 // should be released.
170 delete manager;
171 ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_);
173 // Release each of our references and make sure that each one results in
174 // the instance being deleted as its reference count hits zero.
175 root_accessible->NativeReleaseReference();
176 ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_);
177 child1_accessible->NativeReleaseReference();
178 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
181 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
182 // Make sure that changes to a subtree reuse as many objects as possible.
184 // Tree 1:
186 // root
187 // child1
188 // child2
189 // child3
191 ui::AXNodeData tree1_child1;
192 tree1_child1.id = 2;
193 tree1_child1.SetName("Child1");
194 tree1_child1.role = ui::AX_ROLE_BUTTON;
195 tree1_child1.state = 0;
197 ui::AXNodeData tree1_child2;
198 tree1_child2.id = 3;
199 tree1_child2.SetName("Child2");
200 tree1_child2.role = ui::AX_ROLE_BUTTON;
201 tree1_child2.state = 0;
203 ui::AXNodeData tree1_child3;
204 tree1_child3.id = 4;
205 tree1_child3.SetName("Child3");
206 tree1_child3.role = ui::AX_ROLE_BUTTON;
207 tree1_child3.state = 0;
209 ui::AXNodeData tree1_root;
210 tree1_root.id = 1;
211 tree1_root.SetName("Document");
212 tree1_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
213 tree1_root.state = 0;
214 tree1_root.child_ids.push_back(2);
215 tree1_root.child_ids.push_back(3);
216 tree1_root.child_ids.push_back(4);
218 // Tree 2:
220 // root
221 // child0 <-- inserted
222 // child1
223 // child2
224 // <-- child3 deleted
226 ui::AXNodeData tree2_child0;
227 tree2_child0.id = 5;
228 tree2_child0.SetName("Child0");
229 tree2_child0.role = ui::AX_ROLE_BUTTON;
230 tree2_child0.state = 0;
232 ui::AXNodeData tree2_root;
233 tree2_root.id = 1;
234 tree2_root.SetName("DocumentChanged");
235 tree2_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
236 tree2_root.state = 0;
237 tree2_root.child_ids.push_back(5);
238 tree2_root.child_ids.push_back(2);
239 tree2_root.child_ids.push_back(3);
241 // Construct a BrowserAccessibilityManager with tree1.
242 CountedBrowserAccessibility::global_obj_count_ = 0;
243 BrowserAccessibilityManager* manager =
244 BrowserAccessibilityManager::Create(
245 MakeAXTreeUpdate(tree1_root,
246 tree1_child1, tree1_child2, tree1_child3),
247 NULL,
248 new CountedBrowserAccessibilityFactory());
249 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
251 // Save references to all of the objects.
252 CountedBrowserAccessibility* root_accessible =
253 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
254 root_accessible->NativeAddReference();
255 CountedBrowserAccessibility* child1_accessible =
256 static_cast<CountedBrowserAccessibility*>(
257 root_accessible->PlatformGetChild(0));
258 child1_accessible->NativeAddReference();
259 CountedBrowserAccessibility* child2_accessible =
260 static_cast<CountedBrowserAccessibility*>(
261 root_accessible->PlatformGetChild(1));
262 child2_accessible->NativeAddReference();
263 CountedBrowserAccessibility* child3_accessible =
264 static_cast<CountedBrowserAccessibility*>(
265 root_accessible->PlatformGetChild(2));
266 child3_accessible->NativeAddReference();
268 // Check the index in parent.
269 EXPECT_EQ(0, child1_accessible->GetIndexInParent());
270 EXPECT_EQ(1, child2_accessible->GetIndexInParent());
271 EXPECT_EQ(2, child3_accessible->GetIndexInParent());
273 // Process a notification containing the changed subtree.
274 std::vector<AccessibilityHostMsg_EventParams> params;
275 params.push_back(AccessibilityHostMsg_EventParams());
276 AccessibilityHostMsg_EventParams* msg = &params[0];
277 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
278 msg->update.nodes.push_back(tree2_root);
279 msg->update.nodes.push_back(tree2_child0);
280 msg->id = tree2_root.id;
281 manager->OnAccessibilityEvents(params);
283 // There should be 5 objects now: the 4 from the new tree, plus the
284 // reference to child3 we kept.
285 EXPECT_EQ(5, CountedBrowserAccessibility::global_obj_count_);
287 // Check that our references to the root, child1, and child2 are still valid,
288 // but that the reference to child3 is now invalid.
289 EXPECT_TRUE(root_accessible->instance_active());
290 EXPECT_TRUE(child1_accessible->instance_active());
291 EXPECT_TRUE(child2_accessible->instance_active());
292 EXPECT_FALSE(child3_accessible->instance_active());
294 // Check that the index in parent has been updated.
295 EXPECT_EQ(1, child1_accessible->GetIndexInParent());
296 EXPECT_EQ(2, child2_accessible->GetIndexInParent());
298 // Release our references. The object count should only decrease by 1
299 // for child3.
300 root_accessible->NativeReleaseReference();
301 child1_accessible->NativeReleaseReference();
302 child2_accessible->NativeReleaseReference();
303 child3_accessible->NativeReleaseReference();
305 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
307 // Delete the manager and make sure all memory is cleaned up.
308 delete manager;
309 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
312 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
313 // Similar to the test above, but with a more complicated tree.
315 // Tree 1:
317 // root
318 // container
319 // child1
320 // grandchild1
321 // child2
322 // grandchild2
323 // child3
324 // grandchild3
326 ui::AXNodeData tree1_grandchild1;
327 tree1_grandchild1.id = 4;
328 tree1_grandchild1.SetName("GrandChild1");
329 tree1_grandchild1.role = ui::AX_ROLE_BUTTON;
330 tree1_grandchild1.state = 0;
332 ui::AXNodeData tree1_child1;
333 tree1_child1.id = 3;
334 tree1_child1.SetName("Child1");
335 tree1_child1.role = ui::AX_ROLE_BUTTON;
336 tree1_child1.state = 0;
337 tree1_child1.child_ids.push_back(4);
339 ui::AXNodeData tree1_grandchild2;
340 tree1_grandchild2.id = 6;
341 tree1_grandchild2.SetName("GrandChild1");
342 tree1_grandchild2.role = ui::AX_ROLE_BUTTON;
343 tree1_grandchild2.state = 0;
345 ui::AXNodeData tree1_child2;
346 tree1_child2.id = 5;
347 tree1_child2.SetName("Child2");
348 tree1_child2.role = ui::AX_ROLE_BUTTON;
349 tree1_child2.state = 0;
350 tree1_child2.child_ids.push_back(6);
352 ui::AXNodeData tree1_grandchild3;
353 tree1_grandchild3.id = 8;
354 tree1_grandchild3.SetName("GrandChild3");
355 tree1_grandchild3.role = ui::AX_ROLE_BUTTON;
356 tree1_grandchild3.state = 0;
358 ui::AXNodeData tree1_child3;
359 tree1_child3.id = 7;
360 tree1_child3.SetName("Child3");
361 tree1_child3.role = ui::AX_ROLE_BUTTON;
362 tree1_child3.state = 0;
363 tree1_child3.child_ids.push_back(8);
365 ui::AXNodeData tree1_container;
366 tree1_container.id = 2;
367 tree1_container.SetName("Container");
368 tree1_container.role = ui::AX_ROLE_GROUP;
369 tree1_container.state = 0;
370 tree1_container.child_ids.push_back(3);
371 tree1_container.child_ids.push_back(5);
372 tree1_container.child_ids.push_back(7);
374 ui::AXNodeData tree1_root;
375 tree1_root.id = 1;
376 tree1_root.SetName("Document");
377 tree1_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
378 tree1_root.state = 0;
379 tree1_root.child_ids.push_back(2);
381 // Tree 2:
383 // root
384 // container
385 // child0 <-- inserted
386 // grandchild0 <--
387 // child1
388 // grandchild1
389 // child2
390 // grandchild2
391 // <-- child3 (and grandchild3) deleted
393 ui::AXNodeData tree2_grandchild0;
394 tree2_grandchild0.id = 9;
395 tree2_grandchild0.SetName("GrandChild0");
396 tree2_grandchild0.role = ui::AX_ROLE_BUTTON;
397 tree2_grandchild0.state = 0;
399 ui::AXNodeData tree2_child0;
400 tree2_child0.id = 10;
401 tree2_child0.SetName("Child0");
402 tree2_child0.role = ui::AX_ROLE_BUTTON;
403 tree2_child0.state = 0;
404 tree2_child0.child_ids.push_back(9);
406 ui::AXNodeData tree2_container;
407 tree2_container.id = 2;
408 tree2_container.SetName("Container");
409 tree2_container.role = ui::AX_ROLE_GROUP;
410 tree2_container.state = 0;
411 tree2_container.child_ids.push_back(10);
412 tree2_container.child_ids.push_back(3);
413 tree2_container.child_ids.push_back(5);
415 // Construct a BrowserAccessibilityManager with tree1.
416 CountedBrowserAccessibility::global_obj_count_ = 0;
417 BrowserAccessibilityManager* manager =
418 BrowserAccessibilityManager::Create(
419 MakeAXTreeUpdate(tree1_root, tree1_container,
420 tree1_child1, tree1_grandchild1,
421 tree1_child2, tree1_grandchild2,
422 tree1_child3, tree1_grandchild3),
423 NULL,
424 new CountedBrowserAccessibilityFactory());
425 ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
427 // Save references to some objects.
428 CountedBrowserAccessibility* root_accessible =
429 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
430 root_accessible->NativeAddReference();
431 CountedBrowserAccessibility* container_accessible =
432 static_cast<CountedBrowserAccessibility*>(
433 root_accessible->PlatformGetChild(0));
434 container_accessible->NativeAddReference();
435 CountedBrowserAccessibility* child2_accessible =
436 static_cast<CountedBrowserAccessibility*>(
437 container_accessible->PlatformGetChild(1));
438 child2_accessible->NativeAddReference();
439 CountedBrowserAccessibility* child3_accessible =
440 static_cast<CountedBrowserAccessibility*>(
441 container_accessible->PlatformGetChild(2));
442 child3_accessible->NativeAddReference();
444 // Check the index in parent.
445 EXPECT_EQ(1, child2_accessible->GetIndexInParent());
446 EXPECT_EQ(2, child3_accessible->GetIndexInParent());
448 // Process a notification containing the changed subtree rooted at
449 // the container.
450 std::vector<AccessibilityHostMsg_EventParams> params;
451 params.push_back(AccessibilityHostMsg_EventParams());
452 AccessibilityHostMsg_EventParams* msg = &params[0];
453 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
454 msg->update.nodes.push_back(tree2_container);
455 msg->update.nodes.push_back(tree2_child0);
456 msg->update.nodes.push_back(tree2_grandchild0);
457 msg->id = tree2_container.id;
458 manager->OnAccessibilityEvents(params);
460 // There should be 9 objects now: the 8 from the new tree, plus the
461 // reference to child3 we kept.
462 EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_);
464 // Check that our references to the root and container and child2 are
465 // still valid, but that the reference to child3 is now invalid.
466 EXPECT_TRUE(root_accessible->instance_active());
467 EXPECT_TRUE(container_accessible->instance_active());
468 EXPECT_TRUE(child2_accessible->instance_active());
469 EXPECT_FALSE(child3_accessible->instance_active());
471 // Ensure that we retain the parent of the detached subtree.
472 EXPECT_EQ(root_accessible, container_accessible->GetParent());
473 EXPECT_EQ(0, container_accessible->GetIndexInParent());
475 // Check that the index in parent has been updated.
476 EXPECT_EQ(2, child2_accessible->GetIndexInParent());
478 // Release our references. The object count should only decrease by 1
479 // for child3.
480 root_accessible->NativeReleaseReference();
481 container_accessible->NativeReleaseReference();
482 child2_accessible->NativeReleaseReference();
483 child3_accessible->NativeReleaseReference();
485 EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
487 // Delete the manager and make sure all memory is cleaned up.
488 delete manager;
489 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
492 TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
493 // Tree 1:
495 // 1
496 // 2
497 // 3
498 // 4
500 ui::AXNodeData tree1_4;
501 tree1_4.id = 4;
502 tree1_4.state = 0;
504 ui::AXNodeData tree1_3;
505 tree1_3.id = 3;
506 tree1_3.state = 0;
507 tree1_3.child_ids.push_back(4);
509 ui::AXNodeData tree1_2;
510 tree1_2.id = 2;
511 tree1_2.state = 0;
513 ui::AXNodeData tree1_1;
514 tree1_1.id = 1;
515 tree1_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
516 tree1_1.state = 0;
517 tree1_1.child_ids.push_back(2);
518 tree1_1.child_ids.push_back(3);
520 // Tree 2:
522 // 1
523 // 4 <-- moves up a level and gains child
524 // 6 <-- new
525 // 5 <-- new
527 ui::AXNodeData tree2_6;
528 tree2_6.id = 6;
529 tree2_6.state = 0;
531 ui::AXNodeData tree2_5;
532 tree2_5.id = 5;
533 tree2_5.state = 0;
535 ui::AXNodeData tree2_4;
536 tree2_4.id = 4;
537 tree2_4.state = 0;
538 tree2_4.child_ids.push_back(6);
540 ui::AXNodeData tree2_1;
541 tree2_1.id = 1;
542 tree2_1.state = 0;
543 tree2_1.child_ids.push_back(4);
544 tree2_1.child_ids.push_back(5);
546 // Construct a BrowserAccessibilityManager with tree1.
547 CountedBrowserAccessibility::global_obj_count_ = 0;
548 BrowserAccessibilityManager* manager =
549 BrowserAccessibilityManager::Create(
550 MakeAXTreeUpdate(tree1_1, tree1_2, tree1_3, tree1_4),
551 NULL,
552 new CountedBrowserAccessibilityFactory());
553 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
555 // Process a notification containing the changed subtree.
556 std::vector<AccessibilityHostMsg_EventParams> params;
557 params.push_back(AccessibilityHostMsg_EventParams());
558 AccessibilityHostMsg_EventParams* msg = &params[0];
559 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
560 msg->update.nodes.push_back(tree2_1);
561 msg->update.nodes.push_back(tree2_4);
562 msg->update.nodes.push_back(tree2_5);
563 msg->update.nodes.push_back(tree2_6);
564 msg->id = tree2_1.id;
565 manager->OnAccessibilityEvents(params);
567 // There should be 4 objects now.
568 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
570 // Delete the manager and make sure all memory is cleaned up.
571 delete manager;
572 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
575 TEST(BrowserAccessibilityManagerTest, TestFatalError) {
576 // Test that BrowserAccessibilityManager raises a fatal error
577 // (which will crash the renderer) if the same id is used in
578 // two places in the tree.
580 ui::AXNodeData root;
581 root.id = 1;
582 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
583 root.child_ids.push_back(2);
584 root.child_ids.push_back(2);
586 CountedBrowserAccessibilityFactory* factory =
587 new CountedBrowserAccessibilityFactory();
588 scoped_ptr<TestBrowserAccessibilityDelegate> delegate(
589 new TestBrowserAccessibilityDelegate());
590 scoped_ptr<BrowserAccessibilityManager> manager;
591 ASSERT_FALSE(delegate->got_fatal_error());
592 manager.reset(BrowserAccessibilityManager::Create(
593 MakeAXTreeUpdate(root),
594 delegate.get(),
595 factory));
596 ASSERT_TRUE(delegate->got_fatal_error());
598 ui::AXNodeData root2;
599 root2.id = 1;
600 root2.role = ui::AX_ROLE_ROOT_WEB_AREA;
601 root2.child_ids.push_back(2);
602 root2.child_ids.push_back(3);
604 ui::AXNodeData child1;
605 child1.id = 2;
606 child1.child_ids.push_back(4);
607 child1.child_ids.push_back(5);
609 ui::AXNodeData child2;
610 child2.id = 3;
611 child2.child_ids.push_back(6);
612 child2.child_ids.push_back(5); // Duplicate
614 ui::AXNodeData grandchild4;
615 grandchild4.id = 4;
617 ui::AXNodeData grandchild5;
618 grandchild5.id = 5;
620 ui::AXNodeData grandchild6;
621 grandchild6.id = 6;
623 delegate->reset_got_fatal_error();
624 factory = new CountedBrowserAccessibilityFactory();
625 manager.reset(BrowserAccessibilityManager::Create(
626 MakeAXTreeUpdate(root2, child1, child2,
627 grandchild4, grandchild5, grandchild6),
628 delegate.get(),
629 factory));
630 ASSERT_TRUE(delegate->got_fatal_error());
633 TEST(BrowserAccessibilityManagerTest, BoundsForRange) {
634 ui::AXNodeData root;
635 root.id = 1;
636 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
638 ui::AXNodeData static_text;
639 static_text.id = 2;
640 static_text.SetValue("Hello, world.");
641 static_text.role = ui::AX_ROLE_STATIC_TEXT;
642 static_text.location = gfx::Rect(100, 100, 29, 18);
643 root.child_ids.push_back(2);
645 ui::AXNodeData inline_text1;
646 inline_text1.id = 3;
647 inline_text1.SetValue("Hello, ");
648 inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
649 inline_text1.location = gfx::Rect(100, 100, 29, 9);
650 inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
651 ui::AX_TEXT_DIRECTION_LR);
652 std::vector<int32> character_offsets1;
653 character_offsets1.push_back(6); // 0
654 character_offsets1.push_back(11); // 1
655 character_offsets1.push_back(16); // 2
656 character_offsets1.push_back(21); // 3
657 character_offsets1.push_back(26); // 4
658 character_offsets1.push_back(29); // 5
659 character_offsets1.push_back(29); // 6 (note that the space has no width)
660 inline_text1.AddIntListAttribute(
661 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
662 static_text.child_ids.push_back(3);
664 ui::AXNodeData inline_text2;
665 inline_text2.id = 4;
666 inline_text2.SetValue("world.");
667 inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
668 inline_text2.location = gfx::Rect(100, 109, 28, 9);
669 inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
670 ui::AX_TEXT_DIRECTION_LR);
671 std::vector<int32> character_offsets2;
672 character_offsets2.push_back(5);
673 character_offsets2.push_back(10);
674 character_offsets2.push_back(15);
675 character_offsets2.push_back(20);
676 character_offsets2.push_back(25);
677 character_offsets2.push_back(28);
678 inline_text2.AddIntListAttribute(
679 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
680 static_text.child_ids.push_back(4);
682 scoped_ptr<BrowserAccessibilityManager> manager(
683 BrowserAccessibilityManager::Create(
684 MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
685 NULL,
686 new CountedBrowserAccessibilityFactory()));
688 BrowserAccessibility* root_accessible = manager->GetRoot();
689 BrowserAccessibility* static_text_accessible =
690 root_accessible->PlatformGetChild(0);
692 EXPECT_EQ(gfx::Rect(100, 100, 6, 9).ToString(),
693 static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
695 EXPECT_EQ(gfx::Rect(100, 100, 26, 9).ToString(),
696 static_text_accessible->GetLocalBoundsForRange(0, 5).ToString());
698 EXPECT_EQ(gfx::Rect(100, 109, 5, 9).ToString(),
699 static_text_accessible->GetLocalBoundsForRange(7, 1).ToString());
701 EXPECT_EQ(gfx::Rect(100, 109, 25, 9).ToString(),
702 static_text_accessible->GetLocalBoundsForRange(7, 5).ToString());
704 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
705 static_text_accessible->GetLocalBoundsForRange(5, 3).ToString());
707 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
708 static_text_accessible->GetLocalBoundsForRange(0, 13).ToString());
710 // Test range that's beyond the text.
711 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
712 static_text_accessible->GetLocalBoundsForRange(-1, 999).ToString());
714 // Test that we can call bounds for range on the parent element, too,
715 // and it still works.
716 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
717 root_accessible->GetLocalBoundsForRange(0, 13).ToString());
720 TEST(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) {
721 // In this example, we assume that the string "123abc" is rendered with
722 // "123" going left-to-right and "abc" going right-to-left. In other
723 // words, on-screen it would look like "123cba". This is possible to
724 // acheive if the source string had unicode control characters
725 // to switch directions. This test doesn't worry about how, though - it just
726 // tests that if something like that were to occur, GetLocalBoundsForRange
727 // returns the correct bounds for different ranges.
729 ui::AXNodeData root;
730 root.id = 1;
731 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
733 ui::AXNodeData static_text;
734 static_text.id = 2;
735 static_text.SetValue("123abc");
736 static_text.role = ui::AX_ROLE_STATIC_TEXT;
737 static_text.location = gfx::Rect(100, 100, 60, 20);
738 root.child_ids.push_back(2);
740 ui::AXNodeData inline_text1;
741 inline_text1.id = 3;
742 inline_text1.SetValue("123");
743 inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
744 inline_text1.location = gfx::Rect(100, 100, 30, 20);
745 inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
746 ui::AX_TEXT_DIRECTION_LR);
747 std::vector<int32> character_offsets1;
748 character_offsets1.push_back(10); // 0
749 character_offsets1.push_back(20); // 1
750 character_offsets1.push_back(30); // 2
751 inline_text1.AddIntListAttribute(
752 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
753 static_text.child_ids.push_back(3);
755 ui::AXNodeData inline_text2;
756 inline_text2.id = 4;
757 inline_text2.SetValue("abc");
758 inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
759 inline_text2.location = gfx::Rect(130, 100, 30, 20);
760 inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
761 ui::AX_TEXT_DIRECTION_RL);
762 std::vector<int32> character_offsets2;
763 character_offsets2.push_back(10);
764 character_offsets2.push_back(20);
765 character_offsets2.push_back(30);
766 inline_text2.AddIntListAttribute(
767 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
768 static_text.child_ids.push_back(4);
770 scoped_ptr<BrowserAccessibilityManager> manager(
771 BrowserAccessibilityManager::Create(
772 MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
773 NULL,
774 new CountedBrowserAccessibilityFactory()));
776 BrowserAccessibility* root_accessible = manager->GetRoot();
777 BrowserAccessibility* static_text_accessible =
778 root_accessible->PlatformGetChild(0);
780 EXPECT_EQ(gfx::Rect(100, 100, 60, 20).ToString(),
781 static_text_accessible->GetLocalBoundsForRange(0, 6).ToString());
783 EXPECT_EQ(gfx::Rect(100, 100, 10, 20).ToString(),
784 static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
786 EXPECT_EQ(gfx::Rect(100, 100, 30, 20).ToString(),
787 static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
789 EXPECT_EQ(gfx::Rect(150, 100, 10, 20).ToString(),
790 static_text_accessible->GetLocalBoundsForRange(3, 1).ToString());
792 EXPECT_EQ(gfx::Rect(130, 100, 30, 20).ToString(),
793 static_text_accessible->GetLocalBoundsForRange(3, 3).ToString());
795 // This range is only two characters, but because of the direction switch
796 // the bounds are as wide as four characters.
797 EXPECT_EQ(gfx::Rect(120, 100, 40, 20).ToString(),
798 static_text_accessible->GetLocalBoundsForRange(2, 2).ToString());
801 TEST(BrowserAccessibilityManagerTest, BoundsForRangeScrolledWindow) {
802 ui::AXNodeData root;
803 root.id = 1;
804 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
805 root.AddIntAttribute(ui::AX_ATTR_SCROLL_X, 25);
806 root.AddIntAttribute(ui::AX_ATTR_SCROLL_Y, 50);
808 ui::AXNodeData static_text;
809 static_text.id = 2;
810 static_text.SetValue("ABC");
811 static_text.role = ui::AX_ROLE_STATIC_TEXT;
812 static_text.location = gfx::Rect(100, 100, 16, 9);
813 root.child_ids.push_back(2);
815 ui::AXNodeData inline_text;
816 inline_text.id = 3;
817 inline_text.SetValue("ABC");
818 inline_text.role = ui::AX_ROLE_INLINE_TEXT_BOX;
819 inline_text.location = gfx::Rect(100, 100, 16, 9);
820 inline_text.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
821 ui::AX_TEXT_DIRECTION_LR);
822 std::vector<int32> character_offsets1;
823 character_offsets1.push_back(6); // 0
824 character_offsets1.push_back(11); // 1
825 character_offsets1.push_back(16); // 2
826 inline_text.AddIntListAttribute(
827 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
828 static_text.child_ids.push_back(3);
830 scoped_ptr<BrowserAccessibilityManager> manager(
831 BrowserAccessibilityManager::Create(
832 MakeAXTreeUpdate(root, static_text, inline_text),
833 NULL,
834 new CountedBrowserAccessibilityFactory()));
836 BrowserAccessibility* root_accessible = manager->GetRoot();
837 BrowserAccessibility* static_text_accessible =
838 root_accessible->PlatformGetChild(0);
840 if (manager->UseRootScrollOffsetsWhenComputingBounds()) {
841 EXPECT_EQ(gfx::Rect(75, 50, 16, 9).ToString(),
842 static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
843 } else {
844 EXPECT_EQ(gfx::Rect(100, 100, 16, 9).ToString(),
845 static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
849 #if defined(OS_WIN)
850 #define MAYBE_BoundsForRangeOnParentElement \
851 DISABLED_BoundsForRangeOnParentElement
852 #else
853 #define MAYBE_BoundsForRangeOnParentElement BoundsForRangeOnParentElement
854 #endif
855 TEST(BrowserAccessibilityManagerTest, MAYBE_BoundsForRangeOnParentElement) {
856 ui::AXNodeData root;
857 root.id = 1;
858 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
859 root.child_ids.push_back(2);
861 ui::AXNodeData div;
862 div.id = 2;
863 div.role = ui::AX_ROLE_DIV;
864 div.location = gfx::Rect(100, 100, 100, 20);
865 div.child_ids.push_back(3);
866 div.child_ids.push_back(4);
867 div.child_ids.push_back(5);
869 ui::AXNodeData static_text1;
870 static_text1.id = 3;
871 static_text1.SetValue("AB");
872 static_text1.role = ui::AX_ROLE_STATIC_TEXT;
873 static_text1.location = gfx::Rect(100, 100, 40, 20);
874 static_text1.child_ids.push_back(6);
876 ui::AXNodeData img;
877 img.id = 4;
878 img.role = ui::AX_ROLE_IMAGE;
879 img.location = gfx::Rect(140, 100, 20, 20);
881 ui::AXNodeData static_text2;
882 static_text2.id = 5;
883 static_text2.SetValue("CD");
884 static_text2.role = ui::AX_ROLE_STATIC_TEXT;
885 static_text2.location = gfx::Rect(160, 100, 40, 20);
886 static_text2.child_ids.push_back(7);
888 ui::AXNodeData inline_text1;
889 inline_text1.id = 6;
890 inline_text1.SetValue("AB");
891 inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
892 inline_text1.location = gfx::Rect(100, 100, 40, 20);
893 inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
894 ui::AX_TEXT_DIRECTION_LR);
895 std::vector<int32> character_offsets1;
896 character_offsets1.push_back(20); // 0
897 character_offsets1.push_back(40); // 1
898 inline_text1.AddIntListAttribute(
899 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
901 ui::AXNodeData inline_text2;
902 inline_text2.id = 7;
903 inline_text2.SetValue("CD");
904 inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
905 inline_text2.location = gfx::Rect(160, 100, 40, 20);
906 inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
907 ui::AX_TEXT_DIRECTION_LR);
908 std::vector<int32> character_offsets2;
909 character_offsets2.push_back(20); // 0
910 character_offsets2.push_back(40); // 1
911 inline_text2.AddIntListAttribute(
912 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
914 scoped_ptr<BrowserAccessibilityManager> manager(
915 BrowserAccessibilityManager::Create(
916 MakeAXTreeUpdate(
917 root, div, static_text1, img,
918 static_text2, inline_text1, inline_text2),
919 NULL,
920 new CountedBrowserAccessibilityFactory()));
921 BrowserAccessibility* root_accessible = manager->GetRoot();
923 EXPECT_EQ(gfx::Rect(100, 100, 20, 20).ToString(),
924 root_accessible->GetLocalBoundsForRange(0, 1).ToString());
926 EXPECT_EQ(gfx::Rect(100, 100, 40, 20).ToString(),
927 root_accessible->GetLocalBoundsForRange(0, 2).ToString());
929 EXPECT_EQ(gfx::Rect(100, 100, 80, 20).ToString(),
930 root_accessible->GetLocalBoundsForRange(0, 3).ToString());
932 EXPECT_EQ(gfx::Rect(120, 100, 60, 20).ToString(),
933 root_accessible->GetLocalBoundsForRange(1, 2).ToString());
935 EXPECT_EQ(gfx::Rect(120, 100, 80, 20).ToString(),
936 root_accessible->GetLocalBoundsForRange(1, 3).ToString());
938 EXPECT_EQ(gfx::Rect(100, 100, 100, 20).ToString(),
939 root_accessible->GetLocalBoundsForRange(0, 4).ToString());
942 TEST(BrowserAccessibilityManagerTest, NextPreviousInTreeOrder) {
943 ui::AXNodeData root;
944 root.id = 1;
945 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
947 ui::AXNodeData node2;
948 node2.id = 2;
949 root.child_ids.push_back(2);
951 ui::AXNodeData node3;
952 node3.id = 3;
953 root.child_ids.push_back(3);
955 ui::AXNodeData node4;
956 node4.id = 4;
957 node3.child_ids.push_back(4);
959 ui::AXNodeData node5;
960 node5.id = 5;
961 root.child_ids.push_back(5);
963 scoped_ptr<BrowserAccessibilityManager> manager(
964 BrowserAccessibilityManager::Create(
965 MakeAXTreeUpdate(root, node2, node3, node4, node5),
966 NULL,
967 new CountedBrowserAccessibilityFactory()));
969 BrowserAccessibility* root_accessible = manager->GetRoot();
970 BrowserAccessibility* node2_accessible = root_accessible->PlatformGetChild(0);
971 BrowserAccessibility* node3_accessible = root_accessible->PlatformGetChild(1);
972 BrowserAccessibility* node4_accessible =
973 node3_accessible->PlatformGetChild(0);
974 BrowserAccessibility* node5_accessible = root_accessible->PlatformGetChild(2);
976 ASSERT_EQ(NULL, manager->NextInTreeOrder(NULL));
977 ASSERT_EQ(node2_accessible, manager->NextInTreeOrder(root_accessible));
978 ASSERT_EQ(node3_accessible, manager->NextInTreeOrder(node2_accessible));
979 ASSERT_EQ(node4_accessible, manager->NextInTreeOrder(node3_accessible));
980 ASSERT_EQ(node5_accessible, manager->NextInTreeOrder(node4_accessible));
981 ASSERT_EQ(NULL, manager->NextInTreeOrder(node5_accessible));
983 ASSERT_EQ(NULL, manager->PreviousInTreeOrder(NULL));
984 ASSERT_EQ(node4_accessible, manager->PreviousInTreeOrder(node5_accessible));
985 ASSERT_EQ(node3_accessible, manager->PreviousInTreeOrder(node4_accessible));
986 ASSERT_EQ(node2_accessible, manager->PreviousInTreeOrder(node3_accessible));
987 ASSERT_EQ(root_accessible, manager->PreviousInTreeOrder(node2_accessible));
990 } // namespace content