Add ICU message format support
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_manager_unittest.cc
blob1be3c64f2ea80630b3c8ade13658b40bda76f4b8
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/public/browser/ax_event_notification_details.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 nullptr;
96 bool got_fatal_error() const { return got_fatal_error_; }
97 void reset_got_fatal_error() { got_fatal_error_ = false; }
99 private:
100 bool got_fatal_error_;
103 } // anonymous namespace
105 TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
106 // Create ui::AXNodeData objects for a simple document tree,
107 // representing the accessibility information used to initialize
108 // BrowserAccessibilityManager.
109 ui::AXNodeData button;
110 button.id = 2;
111 button.SetName("Button");
112 button.role = ui::AX_ROLE_BUTTON;
113 button.state = 0;
115 ui::AXNodeData checkbox;
116 checkbox.id = 3;
117 checkbox.SetName("Checkbox");
118 checkbox.role = ui::AX_ROLE_CHECK_BOX;
119 checkbox.state = 0;
121 ui::AXNodeData root;
122 root.id = 1;
123 root.SetName("Document");
124 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
125 root.state = 0;
126 root.child_ids.push_back(2);
127 root.child_ids.push_back(3);
129 // Construct a BrowserAccessibilityManager with this
130 // ui::AXNodeData tree and a factory for an instance-counting
131 // BrowserAccessibility, and ensure that exactly 3 instances were
132 // created. Note that the manager takes ownership of the factory.
133 CountedBrowserAccessibility::global_obj_count_ = 0;
134 BrowserAccessibilityManager* manager =
135 BrowserAccessibilityManager::Create(
136 MakeAXTreeUpdate(root, button, checkbox),
137 nullptr,
138 new CountedBrowserAccessibilityFactory());
140 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
142 // Delete the manager and test that all 3 instances are deleted.
143 delete manager;
144 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
146 // Construct a manager again, and this time save references to two of
147 // the three nodes in the tree.
148 manager =
149 BrowserAccessibilityManager::Create(
150 MakeAXTreeUpdate(root, button, checkbox),
151 nullptr,
152 new CountedBrowserAccessibilityFactory());
153 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
155 CountedBrowserAccessibility* root_accessible =
156 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
157 root_accessible->NativeAddReference();
158 CountedBrowserAccessibility* child1_accessible =
159 static_cast<CountedBrowserAccessibility*>(
160 root_accessible->PlatformGetChild(1));
161 child1_accessible->NativeAddReference();
163 // Now delete the manager, and only one of the three nodes in the tree
164 // should be released.
165 delete manager;
166 ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_);
168 // Release each of our references and make sure that each one results in
169 // the instance being deleted as its reference count hits zero.
170 root_accessible->NativeReleaseReference();
171 ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_);
172 child1_accessible->NativeReleaseReference();
173 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
176 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
177 // Make sure that changes to a subtree reuse as many objects as possible.
179 // Tree 1:
181 // root
182 // child1
183 // child2
184 // child3
186 ui::AXNodeData tree1_child1;
187 tree1_child1.id = 2;
188 tree1_child1.SetName("Child1");
189 tree1_child1.role = ui::AX_ROLE_BUTTON;
190 tree1_child1.state = 0;
192 ui::AXNodeData tree1_child2;
193 tree1_child2.id = 3;
194 tree1_child2.SetName("Child2");
195 tree1_child2.role = ui::AX_ROLE_BUTTON;
196 tree1_child2.state = 0;
198 ui::AXNodeData tree1_child3;
199 tree1_child3.id = 4;
200 tree1_child3.SetName("Child3");
201 tree1_child3.role = ui::AX_ROLE_BUTTON;
202 tree1_child3.state = 0;
204 ui::AXNodeData tree1_root;
205 tree1_root.id = 1;
206 tree1_root.SetName("Document");
207 tree1_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
208 tree1_root.state = 0;
209 tree1_root.child_ids.push_back(2);
210 tree1_root.child_ids.push_back(3);
211 tree1_root.child_ids.push_back(4);
213 // Tree 2:
215 // root
216 // child0 <-- inserted
217 // child1
218 // child2
219 // <-- child3 deleted
221 ui::AXNodeData tree2_child0;
222 tree2_child0.id = 5;
223 tree2_child0.SetName("Child0");
224 tree2_child0.role = ui::AX_ROLE_BUTTON;
225 tree2_child0.state = 0;
227 ui::AXNodeData tree2_root;
228 tree2_root.id = 1;
229 tree2_root.SetName("DocumentChanged");
230 tree2_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
231 tree2_root.state = 0;
232 tree2_root.child_ids.push_back(5);
233 tree2_root.child_ids.push_back(2);
234 tree2_root.child_ids.push_back(3);
236 // Construct a BrowserAccessibilityManager with tree1.
237 CountedBrowserAccessibility::global_obj_count_ = 0;
238 BrowserAccessibilityManager* manager =
239 BrowserAccessibilityManager::Create(
240 MakeAXTreeUpdate(tree1_root,
241 tree1_child1, tree1_child2, tree1_child3),
242 nullptr,
243 new CountedBrowserAccessibilityFactory());
244 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
246 // Save references to all of the objects.
247 CountedBrowserAccessibility* root_accessible =
248 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
249 root_accessible->NativeAddReference();
250 CountedBrowserAccessibility* child1_accessible =
251 static_cast<CountedBrowserAccessibility*>(
252 root_accessible->PlatformGetChild(0));
253 child1_accessible->NativeAddReference();
254 CountedBrowserAccessibility* child2_accessible =
255 static_cast<CountedBrowserAccessibility*>(
256 root_accessible->PlatformGetChild(1));
257 child2_accessible->NativeAddReference();
258 CountedBrowserAccessibility* child3_accessible =
259 static_cast<CountedBrowserAccessibility*>(
260 root_accessible->PlatformGetChild(2));
261 child3_accessible->NativeAddReference();
263 // Check the index in parent.
264 EXPECT_EQ(0, child1_accessible->GetIndexInParent());
265 EXPECT_EQ(1, child2_accessible->GetIndexInParent());
266 EXPECT_EQ(2, child3_accessible->GetIndexInParent());
268 // Process a notification containing the changed subtree.
269 std::vector<AXEventNotificationDetails> params;
270 params.push_back(AXEventNotificationDetails());
271 AXEventNotificationDetails* msg = &params[0];
272 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
273 msg->update.nodes.push_back(tree2_root);
274 msg->update.nodes.push_back(tree2_child0);
275 msg->id = tree2_root.id;
276 manager->OnAccessibilityEvents(params);
278 // There should be 5 objects now: the 4 from the new tree, plus the
279 // reference to child3 we kept.
280 EXPECT_EQ(5, CountedBrowserAccessibility::global_obj_count_);
282 // Check that our references to the root, child1, and child2 are still valid,
283 // but that the reference to child3 is now invalid.
284 EXPECT_TRUE(root_accessible->instance_active());
285 EXPECT_TRUE(child1_accessible->instance_active());
286 EXPECT_TRUE(child2_accessible->instance_active());
287 EXPECT_FALSE(child3_accessible->instance_active());
289 // Check that the index in parent has been updated.
290 EXPECT_EQ(1, child1_accessible->GetIndexInParent());
291 EXPECT_EQ(2, child2_accessible->GetIndexInParent());
293 // Release our references. The object count should only decrease by 1
294 // for child3.
295 root_accessible->NativeReleaseReference();
296 child1_accessible->NativeReleaseReference();
297 child2_accessible->NativeReleaseReference();
298 child3_accessible->NativeReleaseReference();
300 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
302 // Delete the manager and make sure all memory is cleaned up.
303 delete manager;
304 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
307 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
308 // Similar to the test above, but with a more complicated tree.
310 // Tree 1:
312 // root
313 // container
314 // child1
315 // grandchild1
316 // child2
317 // grandchild2
318 // child3
319 // grandchild3
321 ui::AXNodeData tree1_grandchild1;
322 tree1_grandchild1.id = 4;
323 tree1_grandchild1.SetName("GrandChild1");
324 tree1_grandchild1.role = ui::AX_ROLE_BUTTON;
325 tree1_grandchild1.state = 0;
327 ui::AXNodeData tree1_child1;
328 tree1_child1.id = 3;
329 tree1_child1.SetName("Child1");
330 tree1_child1.role = ui::AX_ROLE_BUTTON;
331 tree1_child1.state = 0;
332 tree1_child1.child_ids.push_back(4);
334 ui::AXNodeData tree1_grandchild2;
335 tree1_grandchild2.id = 6;
336 tree1_grandchild2.SetName("GrandChild1");
337 tree1_grandchild2.role = ui::AX_ROLE_BUTTON;
338 tree1_grandchild2.state = 0;
340 ui::AXNodeData tree1_child2;
341 tree1_child2.id = 5;
342 tree1_child2.SetName("Child2");
343 tree1_child2.role = ui::AX_ROLE_BUTTON;
344 tree1_child2.state = 0;
345 tree1_child2.child_ids.push_back(6);
347 ui::AXNodeData tree1_grandchild3;
348 tree1_grandchild3.id = 8;
349 tree1_grandchild3.SetName("GrandChild3");
350 tree1_grandchild3.role = ui::AX_ROLE_BUTTON;
351 tree1_grandchild3.state = 0;
353 ui::AXNodeData tree1_child3;
354 tree1_child3.id = 7;
355 tree1_child3.SetName("Child3");
356 tree1_child3.role = ui::AX_ROLE_BUTTON;
357 tree1_child3.state = 0;
358 tree1_child3.child_ids.push_back(8);
360 ui::AXNodeData tree1_container;
361 tree1_container.id = 2;
362 tree1_container.SetName("Container");
363 tree1_container.role = ui::AX_ROLE_GROUP;
364 tree1_container.state = 0;
365 tree1_container.child_ids.push_back(3);
366 tree1_container.child_ids.push_back(5);
367 tree1_container.child_ids.push_back(7);
369 ui::AXNodeData tree1_root;
370 tree1_root.id = 1;
371 tree1_root.SetName("Document");
372 tree1_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
373 tree1_root.state = 0;
374 tree1_root.child_ids.push_back(2);
376 // Tree 2:
378 // root
379 // container
380 // child0 <-- inserted
381 // grandchild0 <--
382 // child1
383 // grandchild1
384 // child2
385 // grandchild2
386 // <-- child3 (and grandchild3) deleted
388 ui::AXNodeData tree2_grandchild0;
389 tree2_grandchild0.id = 9;
390 tree2_grandchild0.SetName("GrandChild0");
391 tree2_grandchild0.role = ui::AX_ROLE_BUTTON;
392 tree2_grandchild0.state = 0;
394 ui::AXNodeData tree2_child0;
395 tree2_child0.id = 10;
396 tree2_child0.SetName("Child0");
397 tree2_child0.role = ui::AX_ROLE_BUTTON;
398 tree2_child0.state = 0;
399 tree2_child0.child_ids.push_back(9);
401 ui::AXNodeData tree2_container;
402 tree2_container.id = 2;
403 tree2_container.SetName("Container");
404 tree2_container.role = ui::AX_ROLE_GROUP;
405 tree2_container.state = 0;
406 tree2_container.child_ids.push_back(10);
407 tree2_container.child_ids.push_back(3);
408 tree2_container.child_ids.push_back(5);
410 // Construct a BrowserAccessibilityManager with tree1.
411 CountedBrowserAccessibility::global_obj_count_ = 0;
412 BrowserAccessibilityManager* manager =
413 BrowserAccessibilityManager::Create(
414 MakeAXTreeUpdate(tree1_root, tree1_container,
415 tree1_child1, tree1_grandchild1,
416 tree1_child2, tree1_grandchild2,
417 tree1_child3, tree1_grandchild3),
418 nullptr,
419 new CountedBrowserAccessibilityFactory());
420 ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
422 // Save references to some objects.
423 CountedBrowserAccessibility* root_accessible =
424 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
425 root_accessible->NativeAddReference();
426 CountedBrowserAccessibility* container_accessible =
427 static_cast<CountedBrowserAccessibility*>(
428 root_accessible->PlatformGetChild(0));
429 container_accessible->NativeAddReference();
430 CountedBrowserAccessibility* child2_accessible =
431 static_cast<CountedBrowserAccessibility*>(
432 container_accessible->PlatformGetChild(1));
433 child2_accessible->NativeAddReference();
434 CountedBrowserAccessibility* child3_accessible =
435 static_cast<CountedBrowserAccessibility*>(
436 container_accessible->PlatformGetChild(2));
437 child3_accessible->NativeAddReference();
439 // Check the index in parent.
440 EXPECT_EQ(1, child2_accessible->GetIndexInParent());
441 EXPECT_EQ(2, child3_accessible->GetIndexInParent());
443 // Process a notification containing the changed subtree rooted at
444 // the container.
445 std::vector<AXEventNotificationDetails> params;
446 params.push_back(AXEventNotificationDetails());
447 AXEventNotificationDetails* msg = &params[0];
448 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
449 msg->update.nodes.push_back(tree2_container);
450 msg->update.nodes.push_back(tree2_child0);
451 msg->update.nodes.push_back(tree2_grandchild0);
452 msg->id = tree2_container.id;
453 manager->OnAccessibilityEvents(params);
455 // There should be 9 objects now: the 8 from the new tree, plus the
456 // reference to child3 we kept.
457 EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_);
459 // Check that our references to the root and container and child2 are
460 // still valid, but that the reference to child3 is now invalid.
461 EXPECT_TRUE(root_accessible->instance_active());
462 EXPECT_TRUE(container_accessible->instance_active());
463 EXPECT_TRUE(child2_accessible->instance_active());
464 EXPECT_FALSE(child3_accessible->instance_active());
466 // Ensure that we retain the parent of the detached subtree.
467 EXPECT_EQ(root_accessible, container_accessible->GetParent());
468 EXPECT_EQ(0, container_accessible->GetIndexInParent());
470 // Check that the index in parent has been updated.
471 EXPECT_EQ(2, child2_accessible->GetIndexInParent());
473 // Release our references. The object count should only decrease by 1
474 // for child3.
475 root_accessible->NativeReleaseReference();
476 container_accessible->NativeReleaseReference();
477 child2_accessible->NativeReleaseReference();
478 child3_accessible->NativeReleaseReference();
480 EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
482 // Delete the manager and make sure all memory is cleaned up.
483 delete manager;
484 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
487 TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
488 // Tree 1:
490 // 1
491 // 2
492 // 3
493 // 4
495 ui::AXNodeData tree1_4;
496 tree1_4.id = 4;
497 tree1_4.state = 0;
499 ui::AXNodeData tree1_3;
500 tree1_3.id = 3;
501 tree1_3.state = 0;
502 tree1_3.child_ids.push_back(4);
504 ui::AXNodeData tree1_2;
505 tree1_2.id = 2;
506 tree1_2.state = 0;
508 ui::AXNodeData tree1_1;
509 tree1_1.id = 1;
510 tree1_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
511 tree1_1.state = 0;
512 tree1_1.child_ids.push_back(2);
513 tree1_1.child_ids.push_back(3);
515 // Tree 2:
517 // 1
518 // 4 <-- moves up a level and gains child
519 // 6 <-- new
520 // 5 <-- new
522 ui::AXNodeData tree2_6;
523 tree2_6.id = 6;
524 tree2_6.state = 0;
526 ui::AXNodeData tree2_5;
527 tree2_5.id = 5;
528 tree2_5.state = 0;
530 ui::AXNodeData tree2_4;
531 tree2_4.id = 4;
532 tree2_4.state = 0;
533 tree2_4.child_ids.push_back(6);
535 ui::AXNodeData tree2_1;
536 tree2_1.id = 1;
537 tree2_1.state = 0;
538 tree2_1.child_ids.push_back(4);
539 tree2_1.child_ids.push_back(5);
541 // Construct a BrowserAccessibilityManager with tree1.
542 CountedBrowserAccessibility::global_obj_count_ = 0;
543 BrowserAccessibilityManager* manager =
544 BrowserAccessibilityManager::Create(
545 MakeAXTreeUpdate(tree1_1, tree1_2, tree1_3, tree1_4),
546 nullptr,
547 new CountedBrowserAccessibilityFactory());
548 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
550 // Process a notification containing the changed subtree.
551 std::vector<AXEventNotificationDetails> params;
552 params.push_back(AXEventNotificationDetails());
553 AXEventNotificationDetails* msg = &params[0];
554 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
555 msg->update.nodes.push_back(tree2_1);
556 msg->update.nodes.push_back(tree2_4);
557 msg->update.nodes.push_back(tree2_5);
558 msg->update.nodes.push_back(tree2_6);
559 msg->id = tree2_1.id;
560 manager->OnAccessibilityEvents(params);
562 // There should be 4 objects now.
563 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
565 // Delete the manager and make sure all memory is cleaned up.
566 delete manager;
567 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
570 TEST(BrowserAccessibilityManagerTest, TestFatalError) {
571 // Test that BrowserAccessibilityManager raises a fatal error
572 // (which will crash the renderer) if the same id is used in
573 // two places in the tree.
575 ui::AXNodeData root;
576 root.id = 1;
577 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
578 root.child_ids.push_back(2);
579 root.child_ids.push_back(2);
581 CountedBrowserAccessibilityFactory* factory =
582 new CountedBrowserAccessibilityFactory();
583 scoped_ptr<TestBrowserAccessibilityDelegate> delegate(
584 new TestBrowserAccessibilityDelegate());
585 scoped_ptr<BrowserAccessibilityManager> manager;
586 ASSERT_FALSE(delegate->got_fatal_error());
587 manager.reset(BrowserAccessibilityManager::Create(
588 MakeAXTreeUpdate(root),
589 delegate.get(),
590 factory));
591 ASSERT_TRUE(delegate->got_fatal_error());
593 ui::AXNodeData root2;
594 root2.id = 1;
595 root2.role = ui::AX_ROLE_ROOT_WEB_AREA;
596 root2.child_ids.push_back(2);
597 root2.child_ids.push_back(3);
599 ui::AXNodeData child1;
600 child1.id = 2;
601 child1.child_ids.push_back(4);
602 child1.child_ids.push_back(5);
604 ui::AXNodeData child2;
605 child2.id = 3;
606 child2.child_ids.push_back(6);
607 child2.child_ids.push_back(5); // Duplicate
609 ui::AXNodeData grandchild4;
610 grandchild4.id = 4;
612 ui::AXNodeData grandchild5;
613 grandchild5.id = 5;
615 ui::AXNodeData grandchild6;
616 grandchild6.id = 6;
618 delegate->reset_got_fatal_error();
619 factory = new CountedBrowserAccessibilityFactory();
620 manager.reset(BrowserAccessibilityManager::Create(
621 MakeAXTreeUpdate(root2, child1, child2,
622 grandchild4, grandchild5, grandchild6),
623 delegate.get(),
624 factory));
625 ASSERT_TRUE(delegate->got_fatal_error());
628 TEST(BrowserAccessibilityManagerTest, BoundsForRange) {
629 ui::AXNodeData root;
630 root.id = 1;
631 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
633 ui::AXNodeData static_text;
634 static_text.id = 2;
635 static_text.SetValue("Hello, world.");
636 static_text.role = ui::AX_ROLE_STATIC_TEXT;
637 static_text.location = gfx::Rect(100, 100, 29, 18);
638 root.child_ids.push_back(2);
640 ui::AXNodeData inline_text1;
641 inline_text1.id = 3;
642 inline_text1.SetValue("Hello, ");
643 inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
644 inline_text1.location = gfx::Rect(100, 100, 29, 9);
645 inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
646 ui::AX_TEXT_DIRECTION_LTR);
647 std::vector<int32> character_offsets1;
648 character_offsets1.push_back(6); // 0
649 character_offsets1.push_back(11); // 1
650 character_offsets1.push_back(16); // 2
651 character_offsets1.push_back(21); // 3
652 character_offsets1.push_back(26); // 4
653 character_offsets1.push_back(29); // 5
654 character_offsets1.push_back(29); // 6 (note that the space has no width)
655 inline_text1.AddIntListAttribute(
656 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
657 static_text.child_ids.push_back(3);
659 ui::AXNodeData inline_text2;
660 inline_text2.id = 4;
661 inline_text2.SetValue("world.");
662 inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
663 inline_text2.location = gfx::Rect(100, 109, 28, 9);
664 inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
665 ui::AX_TEXT_DIRECTION_LTR);
666 std::vector<int32> character_offsets2;
667 character_offsets2.push_back(5);
668 character_offsets2.push_back(10);
669 character_offsets2.push_back(15);
670 character_offsets2.push_back(20);
671 character_offsets2.push_back(25);
672 character_offsets2.push_back(28);
673 inline_text2.AddIntListAttribute(
674 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
675 static_text.child_ids.push_back(4);
677 scoped_ptr<BrowserAccessibilityManager> manager(
678 BrowserAccessibilityManager::Create(
679 MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
680 nullptr,
681 new CountedBrowserAccessibilityFactory()));
683 BrowserAccessibility* root_accessible = manager->GetRoot();
684 BrowserAccessibility* static_text_accessible =
685 root_accessible->PlatformGetChild(0);
687 EXPECT_EQ(gfx::Rect(100, 100, 6, 9).ToString(),
688 static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
690 EXPECT_EQ(gfx::Rect(100, 100, 26, 9).ToString(),
691 static_text_accessible->GetLocalBoundsForRange(0, 5).ToString());
693 EXPECT_EQ(gfx::Rect(100, 109, 5, 9).ToString(),
694 static_text_accessible->GetLocalBoundsForRange(7, 1).ToString());
696 EXPECT_EQ(gfx::Rect(100, 109, 25, 9).ToString(),
697 static_text_accessible->GetLocalBoundsForRange(7, 5).ToString());
699 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
700 static_text_accessible->GetLocalBoundsForRange(5, 3).ToString());
702 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
703 static_text_accessible->GetLocalBoundsForRange(0, 13).ToString());
705 // Test range that's beyond the text.
706 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
707 static_text_accessible->GetLocalBoundsForRange(-1, 999).ToString());
709 // Test that we can call bounds for range on the parent element, too,
710 // and it still works.
711 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
712 root_accessible->GetLocalBoundsForRange(0, 13).ToString());
715 TEST(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) {
716 // In this example, we assume that the string "123abc" is rendered with
717 // "123" going left-to-right and "abc" going right-to-left. In other
718 // words, on-screen it would look like "123cba". This is possible to
719 // acheive if the source string had unicode control characters
720 // to switch directions. This test doesn't worry about how, though - it just
721 // tests that if something like that were to occur, GetLocalBoundsForRange
722 // returns the correct bounds for different ranges.
724 ui::AXNodeData root;
725 root.id = 1;
726 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
728 ui::AXNodeData static_text;
729 static_text.id = 2;
730 static_text.SetValue("123abc");
731 static_text.role = ui::AX_ROLE_STATIC_TEXT;
732 static_text.location = gfx::Rect(100, 100, 60, 20);
733 root.child_ids.push_back(2);
735 ui::AXNodeData inline_text1;
736 inline_text1.id = 3;
737 inline_text1.SetValue("123");
738 inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
739 inline_text1.location = gfx::Rect(100, 100, 30, 20);
740 inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
741 ui::AX_TEXT_DIRECTION_LTR);
742 std::vector<int32> character_offsets1;
743 character_offsets1.push_back(10); // 0
744 character_offsets1.push_back(20); // 1
745 character_offsets1.push_back(30); // 2
746 inline_text1.AddIntListAttribute(
747 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
748 static_text.child_ids.push_back(3);
750 ui::AXNodeData inline_text2;
751 inline_text2.id = 4;
752 inline_text2.SetValue("abc");
753 inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
754 inline_text2.location = gfx::Rect(130, 100, 30, 20);
755 inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
756 ui::AX_TEXT_DIRECTION_RTL);
757 std::vector<int32> character_offsets2;
758 character_offsets2.push_back(10);
759 character_offsets2.push_back(20);
760 character_offsets2.push_back(30);
761 inline_text2.AddIntListAttribute(
762 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
763 static_text.child_ids.push_back(4);
765 scoped_ptr<BrowserAccessibilityManager> manager(
766 BrowserAccessibilityManager::Create(
767 MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
768 nullptr,
769 new CountedBrowserAccessibilityFactory()));
771 BrowserAccessibility* root_accessible = manager->GetRoot();
772 BrowserAccessibility* static_text_accessible =
773 root_accessible->PlatformGetChild(0);
775 EXPECT_EQ(gfx::Rect(100, 100, 60, 20).ToString(),
776 static_text_accessible->GetLocalBoundsForRange(0, 6).ToString());
778 EXPECT_EQ(gfx::Rect(100, 100, 10, 20).ToString(),
779 static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
781 EXPECT_EQ(gfx::Rect(100, 100, 30, 20).ToString(),
782 static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
784 EXPECT_EQ(gfx::Rect(150, 100, 10, 20).ToString(),
785 static_text_accessible->GetLocalBoundsForRange(3, 1).ToString());
787 EXPECT_EQ(gfx::Rect(130, 100, 30, 20).ToString(),
788 static_text_accessible->GetLocalBoundsForRange(3, 3).ToString());
790 // This range is only two characters, but because of the direction switch
791 // the bounds are as wide as four characters.
792 EXPECT_EQ(gfx::Rect(120, 100, 40, 20).ToString(),
793 static_text_accessible->GetLocalBoundsForRange(2, 2).ToString());
796 TEST(BrowserAccessibilityManagerTest, BoundsForRangeScrolledWindow) {
797 ui::AXNodeData root;
798 root.id = 1;
799 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
800 root.AddIntAttribute(ui::AX_ATTR_SCROLL_X, 25);
801 root.AddIntAttribute(ui::AX_ATTR_SCROLL_Y, 50);
803 ui::AXNodeData static_text;
804 static_text.id = 2;
805 static_text.SetValue("ABC");
806 static_text.role = ui::AX_ROLE_STATIC_TEXT;
807 static_text.location = gfx::Rect(100, 100, 16, 9);
808 root.child_ids.push_back(2);
810 ui::AXNodeData inline_text;
811 inline_text.id = 3;
812 inline_text.SetValue("ABC");
813 inline_text.role = ui::AX_ROLE_INLINE_TEXT_BOX;
814 inline_text.location = gfx::Rect(100, 100, 16, 9);
815 inline_text.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
816 ui::AX_TEXT_DIRECTION_LTR);
817 std::vector<int32> character_offsets1;
818 character_offsets1.push_back(6); // 0
819 character_offsets1.push_back(11); // 1
820 character_offsets1.push_back(16); // 2
821 inline_text.AddIntListAttribute(
822 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
823 static_text.child_ids.push_back(3);
825 scoped_ptr<BrowserAccessibilityManager> manager(
826 BrowserAccessibilityManager::Create(
827 MakeAXTreeUpdate(root, static_text, inline_text),
828 nullptr,
829 new CountedBrowserAccessibilityFactory()));
831 BrowserAccessibility* root_accessible = manager->GetRoot();
832 BrowserAccessibility* static_text_accessible =
833 root_accessible->PlatformGetChild(0);
835 if (manager->UseRootScrollOffsetsWhenComputingBounds()) {
836 EXPECT_EQ(gfx::Rect(75, 50, 16, 9).ToString(),
837 static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
838 } else {
839 EXPECT_EQ(gfx::Rect(100, 100, 16, 9).ToString(),
840 static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
844 #if defined(OS_WIN)
845 #define MAYBE_BoundsForRangeOnParentElement \
846 DISABLED_BoundsForRangeOnParentElement
847 #else
848 #define MAYBE_BoundsForRangeOnParentElement BoundsForRangeOnParentElement
849 #endif
850 TEST(BrowserAccessibilityManagerTest, MAYBE_BoundsForRangeOnParentElement) {
851 ui::AXNodeData root;
852 root.id = 1;
853 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
854 root.child_ids.push_back(2);
856 ui::AXNodeData div;
857 div.id = 2;
858 div.role = ui::AX_ROLE_DIV;
859 div.location = gfx::Rect(100, 100, 100, 20);
860 div.child_ids.push_back(3);
861 div.child_ids.push_back(4);
862 div.child_ids.push_back(5);
864 ui::AXNodeData static_text1;
865 static_text1.id = 3;
866 static_text1.SetValue("AB");
867 static_text1.role = ui::AX_ROLE_STATIC_TEXT;
868 static_text1.location = gfx::Rect(100, 100, 40, 20);
869 static_text1.child_ids.push_back(6);
871 ui::AXNodeData img;
872 img.id = 4;
873 img.role = ui::AX_ROLE_IMAGE;
874 img.location = gfx::Rect(140, 100, 20, 20);
876 ui::AXNodeData static_text2;
877 static_text2.id = 5;
878 static_text2.SetValue("CD");
879 static_text2.role = ui::AX_ROLE_STATIC_TEXT;
880 static_text2.location = gfx::Rect(160, 100, 40, 20);
881 static_text2.child_ids.push_back(7);
883 ui::AXNodeData inline_text1;
884 inline_text1.id = 6;
885 inline_text1.SetValue("AB");
886 inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
887 inline_text1.location = gfx::Rect(100, 100, 40, 20);
888 inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
889 ui::AX_TEXT_DIRECTION_LTR);
890 std::vector<int32> character_offsets1;
891 character_offsets1.push_back(20); // 0
892 character_offsets1.push_back(40); // 1
893 inline_text1.AddIntListAttribute(
894 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
896 ui::AXNodeData inline_text2;
897 inline_text2.id = 7;
898 inline_text2.SetValue("CD");
899 inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
900 inline_text2.location = gfx::Rect(160, 100, 40, 20);
901 inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
902 ui::AX_TEXT_DIRECTION_LTR);
903 std::vector<int32> character_offsets2;
904 character_offsets2.push_back(20); // 0
905 character_offsets2.push_back(40); // 1
906 inline_text2.AddIntListAttribute(
907 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
909 scoped_ptr<BrowserAccessibilityManager> manager(
910 BrowserAccessibilityManager::Create(
911 MakeAXTreeUpdate(
912 root, div, static_text1, img,
913 static_text2, inline_text1, inline_text2),
914 nullptr,
915 new CountedBrowserAccessibilityFactory()));
916 BrowserAccessibility* root_accessible = manager->GetRoot();
918 EXPECT_EQ(gfx::Rect(100, 100, 20, 20).ToString(),
919 root_accessible->GetLocalBoundsForRange(0, 1).ToString());
921 EXPECT_EQ(gfx::Rect(100, 100, 40, 20).ToString(),
922 root_accessible->GetLocalBoundsForRange(0, 2).ToString());
924 EXPECT_EQ(gfx::Rect(100, 100, 80, 20).ToString(),
925 root_accessible->GetLocalBoundsForRange(0, 3).ToString());
927 EXPECT_EQ(gfx::Rect(120, 100, 60, 20).ToString(),
928 root_accessible->GetLocalBoundsForRange(1, 2).ToString());
930 EXPECT_EQ(gfx::Rect(120, 100, 80, 20).ToString(),
931 root_accessible->GetLocalBoundsForRange(1, 3).ToString());
933 EXPECT_EQ(gfx::Rect(100, 100, 100, 20).ToString(),
934 root_accessible->GetLocalBoundsForRange(0, 4).ToString());
937 TEST(BrowserAccessibilityManagerTest, NextPreviousInTreeOrder) {
938 ui::AXNodeData root;
939 root.id = 1;
940 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
942 ui::AXNodeData node2;
943 node2.id = 2;
944 root.child_ids.push_back(2);
946 ui::AXNodeData node3;
947 node3.id = 3;
948 root.child_ids.push_back(3);
950 ui::AXNodeData node4;
951 node4.id = 4;
952 node3.child_ids.push_back(4);
954 ui::AXNodeData node5;
955 node5.id = 5;
956 root.child_ids.push_back(5);
958 scoped_ptr<BrowserAccessibilityManager> manager(
959 BrowserAccessibilityManager::Create(
960 MakeAXTreeUpdate(root, node2, node3, node4, node5),
961 nullptr,
962 new CountedBrowserAccessibilityFactory()));
964 BrowserAccessibility* root_accessible = manager->GetRoot();
965 BrowserAccessibility* node2_accessible = root_accessible->PlatformGetChild(0);
966 BrowserAccessibility* node3_accessible = root_accessible->PlatformGetChild(1);
967 BrowserAccessibility* node4_accessible =
968 node3_accessible->PlatformGetChild(0);
969 BrowserAccessibility* node5_accessible = root_accessible->PlatformGetChild(2);
971 ASSERT_EQ(nullptr, manager->NextInTreeOrder(nullptr));
972 ASSERT_EQ(node2_accessible, manager->NextInTreeOrder(root_accessible));
973 ASSERT_EQ(node3_accessible, manager->NextInTreeOrder(node2_accessible));
974 ASSERT_EQ(node4_accessible, manager->NextInTreeOrder(node3_accessible));
975 ASSERT_EQ(node5_accessible, manager->NextInTreeOrder(node4_accessible));
976 ASSERT_EQ(nullptr, manager->NextInTreeOrder(node5_accessible));
978 ASSERT_EQ(nullptr, manager->PreviousInTreeOrder(nullptr));
979 ASSERT_EQ(node4_accessible, manager->PreviousInTreeOrder(node5_accessible));
980 ASSERT_EQ(node3_accessible, manager->PreviousInTreeOrder(node4_accessible));
981 ASSERT_EQ(node2_accessible, manager->PreviousInTreeOrder(node3_accessible));
982 ASSERT_EQ(root_accessible, manager->PreviousInTreeOrder(node2_accessible));
985 TEST(BrowserAccessibilityManagerTest, DeletingFocusedNodeDoesNotCrash) {
986 // Create a really simple tree with one root node and one focused child.
987 ui::AXNodeData root;
988 root.id = 1;
989 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
990 root.state = 0;
991 root.child_ids.push_back(2);
993 ui::AXNodeData node2;
994 node2.id = 2;
995 node2.state = 1 << ui::AX_STATE_FOCUSED;
997 scoped_ptr<BrowserAccessibilityManager> manager(
998 BrowserAccessibilityManager::Create(
999 MakeAXTreeUpdate(root, node2),
1000 nullptr,
1001 new CountedBrowserAccessibilityFactory()));
1003 ASSERT_EQ(1, manager->GetRoot()->GetId());
1004 ASSERT_EQ(1, manager->GetFocus(manager->GetRoot())->GetId());
1006 // Send the focus event for node 2.
1007 std::vector<AXEventNotificationDetails> events;
1008 events.push_back(AXEventNotificationDetails());
1009 events[0].update = MakeAXTreeUpdate(node2);
1010 events[0].id = 2;
1011 events[0].event_type = ui::AX_EVENT_FOCUS;
1012 manager->OnAccessibilityEvents(events);
1014 ASSERT_EQ(1, manager->GetRoot()->GetId());
1015 ASSERT_EQ(2, manager->GetFocus(manager->GetRoot())->GetId());
1017 // Now replace the tree with a new tree consisting of a single root.
1018 ui::AXNodeData root2;
1019 root2.id = 3;
1020 root2.role = ui::AX_ROLE_ROOT_WEB_AREA;
1021 root2.state = 0;
1023 std::vector<AXEventNotificationDetails> events2;
1024 events2.push_back(AXEventNotificationDetails());
1025 events2[0].update = MakeAXTreeUpdate(root2);
1026 events2[0].id = -1;
1027 events2[0].event_type = ui::AX_EVENT_NONE;
1028 manager->OnAccessibilityEvents(events2);
1030 // Make sure that the focused node was updated to the new root and
1031 // that this doesn't crash.
1032 ASSERT_EQ(3, manager->GetRoot()->GetId());
1033 ASSERT_EQ(3, manager->GetFocus(manager->GetRoot())->GetId());
1036 TEST(BrowserAccessibilityManagerTest, DeletingFocusedNodeDoesNotCrash2) {
1037 // Create a really simple tree with one root node and one focused child.
1038 ui::AXNodeData root;
1039 root.id = 1;
1040 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
1041 root.state = 0;
1042 root.child_ids.push_back(2);
1043 root.child_ids.push_back(3);
1044 root.child_ids.push_back(4);
1046 ui::AXNodeData node2;
1047 node2.id = 2;
1048 node2.state = 1 << ui::AX_STATE_FOCUSED;
1050 ui::AXNodeData node3;
1051 node3.id = 3;
1052 node3.state = 0;
1054 ui::AXNodeData node4;
1055 node4.id = 4;
1056 node4.state = 0;
1058 scoped_ptr<BrowserAccessibilityManager> manager(
1059 BrowserAccessibilityManager::Create(
1060 MakeAXTreeUpdate(root, node2, node3, node4),
1061 nullptr,
1062 new CountedBrowserAccessibilityFactory()));
1064 ASSERT_EQ(1, manager->GetRoot()->GetId());
1065 ASSERT_EQ(1, manager->GetFocus(manager->GetRoot())->GetId());
1067 // Send the focus event for node 2.
1068 std::vector<AXEventNotificationDetails> events;
1069 events.push_back(AXEventNotificationDetails());
1070 events[0].update = MakeAXTreeUpdate(node2);
1071 events[0].id = 2;
1072 events[0].event_type = ui::AX_EVENT_FOCUS;
1073 manager->OnAccessibilityEvents(events);
1075 ASSERT_EQ(1, manager->GetRoot()->GetId());
1076 ASSERT_EQ(2, manager->GetFocus(manager->GetRoot())->GetId());
1078 // Now replace the tree with a new tree consisting of a single root.
1079 ui::AXNodeData root2;
1080 root2.id = 3;
1081 root2.role = ui::AX_ROLE_ROOT_WEB_AREA;
1082 root2.state = 0;
1084 // Make an update the explicitly clears the previous root.
1085 std::vector<AXEventNotificationDetails> events2;
1086 events2.push_back(AXEventNotificationDetails());
1087 events2[0].update = MakeAXTreeUpdate(root2);
1088 events2[0].update.node_id_to_clear = 1;
1089 events2[0].id = -1;
1090 events2[0].event_type = ui::AX_EVENT_NONE;
1091 manager->OnAccessibilityEvents(events2);
1093 // Make sure that the focused node was updated to the new root and
1094 // that this doesn't crash.
1095 ASSERT_EQ(3, manager->GetRoot()->GetId());
1096 ASSERT_EQ(3, manager->GetFocus(manager->GetRoot())->GetId());
1099 } // namespace content