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