Add long running gmail memory benchmark for background tab.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_manager_unittest.cc
bloba5f89ff5183a2ca6df51ad4549d3c02d8858a19a
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 nullptr;
95 BrowserAccessibilityManager* AccessibilityGetChildFrame(
96 int accessibility_node_id) override {
97 return nullptr;
99 BrowserAccessibility* AccessibilityGetParentFrame() override {
100 return nullptr;
102 void AccessibilityGetAllChildFrames(
103 std::vector<BrowserAccessibilityManager*>* child_frames) override {}
105 bool got_fatal_error() const { return got_fatal_error_; }
106 void reset_got_fatal_error() { got_fatal_error_ = false; }
108 private:
109 bool got_fatal_error_;
112 } // anonymous namespace
114 TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
115 // Create ui::AXNodeData objects for a simple document tree,
116 // representing the accessibility information used to initialize
117 // BrowserAccessibilityManager.
118 ui::AXNodeData button;
119 button.id = 2;
120 button.SetName("Button");
121 button.role = ui::AX_ROLE_BUTTON;
122 button.state = 0;
124 ui::AXNodeData checkbox;
125 checkbox.id = 3;
126 checkbox.SetName("Checkbox");
127 checkbox.role = ui::AX_ROLE_CHECK_BOX;
128 checkbox.state = 0;
130 ui::AXNodeData root;
131 root.id = 1;
132 root.SetName("Document");
133 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
134 root.state = 0;
135 root.child_ids.push_back(2);
136 root.child_ids.push_back(3);
138 // Construct a BrowserAccessibilityManager with this
139 // ui::AXNodeData tree and a factory for an instance-counting
140 // BrowserAccessibility, and ensure that exactly 3 instances were
141 // created. Note that the manager takes ownership of the factory.
142 CountedBrowserAccessibility::global_obj_count_ = 0;
143 BrowserAccessibilityManager* manager =
144 BrowserAccessibilityManager::Create(
145 MakeAXTreeUpdate(root, button, checkbox),
146 nullptr,
147 new CountedBrowserAccessibilityFactory());
149 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
151 // Delete the manager and test that all 3 instances are deleted.
152 delete manager;
153 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
155 // Construct a manager again, and this time save references to two of
156 // the three nodes in the tree.
157 manager =
158 BrowserAccessibilityManager::Create(
159 MakeAXTreeUpdate(root, button, checkbox),
160 nullptr,
161 new CountedBrowserAccessibilityFactory());
162 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
164 CountedBrowserAccessibility* root_accessible =
165 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
166 root_accessible->NativeAddReference();
167 CountedBrowserAccessibility* child1_accessible =
168 static_cast<CountedBrowserAccessibility*>(
169 root_accessible->PlatformGetChild(1));
170 child1_accessible->NativeAddReference();
172 // Now delete the manager, and only one of the three nodes in the tree
173 // should be released.
174 delete manager;
175 ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_);
177 // Release each of our references and make sure that each one results in
178 // the instance being deleted as its reference count hits zero.
179 root_accessible->NativeReleaseReference();
180 ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_);
181 child1_accessible->NativeReleaseReference();
182 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
185 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
186 // Make sure that changes to a subtree reuse as many objects as possible.
188 // Tree 1:
190 // root
191 // child1
192 // child2
193 // child3
195 ui::AXNodeData tree1_child1;
196 tree1_child1.id = 2;
197 tree1_child1.SetName("Child1");
198 tree1_child1.role = ui::AX_ROLE_BUTTON;
199 tree1_child1.state = 0;
201 ui::AXNodeData tree1_child2;
202 tree1_child2.id = 3;
203 tree1_child2.SetName("Child2");
204 tree1_child2.role = ui::AX_ROLE_BUTTON;
205 tree1_child2.state = 0;
207 ui::AXNodeData tree1_child3;
208 tree1_child3.id = 4;
209 tree1_child3.SetName("Child3");
210 tree1_child3.role = ui::AX_ROLE_BUTTON;
211 tree1_child3.state = 0;
213 ui::AXNodeData tree1_root;
214 tree1_root.id = 1;
215 tree1_root.SetName("Document");
216 tree1_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
217 tree1_root.state = 0;
218 tree1_root.child_ids.push_back(2);
219 tree1_root.child_ids.push_back(3);
220 tree1_root.child_ids.push_back(4);
222 // Tree 2:
224 // root
225 // child0 <-- inserted
226 // child1
227 // child2
228 // <-- child3 deleted
230 ui::AXNodeData tree2_child0;
231 tree2_child0.id = 5;
232 tree2_child0.SetName("Child0");
233 tree2_child0.role = ui::AX_ROLE_BUTTON;
234 tree2_child0.state = 0;
236 ui::AXNodeData tree2_root;
237 tree2_root.id = 1;
238 tree2_root.SetName("DocumentChanged");
239 tree2_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
240 tree2_root.state = 0;
241 tree2_root.child_ids.push_back(5);
242 tree2_root.child_ids.push_back(2);
243 tree2_root.child_ids.push_back(3);
245 // Construct a BrowserAccessibilityManager with tree1.
246 CountedBrowserAccessibility::global_obj_count_ = 0;
247 BrowserAccessibilityManager* manager =
248 BrowserAccessibilityManager::Create(
249 MakeAXTreeUpdate(tree1_root,
250 tree1_child1, tree1_child2, tree1_child3),
251 nullptr,
252 new CountedBrowserAccessibilityFactory());
253 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
255 // Save references to all of the objects.
256 CountedBrowserAccessibility* root_accessible =
257 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
258 root_accessible->NativeAddReference();
259 CountedBrowserAccessibility* child1_accessible =
260 static_cast<CountedBrowserAccessibility*>(
261 root_accessible->PlatformGetChild(0));
262 child1_accessible->NativeAddReference();
263 CountedBrowserAccessibility* child2_accessible =
264 static_cast<CountedBrowserAccessibility*>(
265 root_accessible->PlatformGetChild(1));
266 child2_accessible->NativeAddReference();
267 CountedBrowserAccessibility* child3_accessible =
268 static_cast<CountedBrowserAccessibility*>(
269 root_accessible->PlatformGetChild(2));
270 child3_accessible->NativeAddReference();
272 // Check the index in parent.
273 EXPECT_EQ(0, child1_accessible->GetIndexInParent());
274 EXPECT_EQ(1, child2_accessible->GetIndexInParent());
275 EXPECT_EQ(2, child3_accessible->GetIndexInParent());
277 // Process a notification containing the changed subtree.
278 std::vector<AccessibilityHostMsg_EventParams> params;
279 params.push_back(AccessibilityHostMsg_EventParams());
280 AccessibilityHostMsg_EventParams* msg = &params[0];
281 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
282 msg->update.nodes.push_back(tree2_root);
283 msg->update.nodes.push_back(tree2_child0);
284 msg->id = tree2_root.id;
285 manager->OnAccessibilityEvents(params);
287 // There should be 5 objects now: the 4 from the new tree, plus the
288 // reference to child3 we kept.
289 EXPECT_EQ(5, CountedBrowserAccessibility::global_obj_count_);
291 // Check that our references to the root, child1, and child2 are still valid,
292 // but that the reference to child3 is now invalid.
293 EXPECT_TRUE(root_accessible->instance_active());
294 EXPECT_TRUE(child1_accessible->instance_active());
295 EXPECT_TRUE(child2_accessible->instance_active());
296 EXPECT_FALSE(child3_accessible->instance_active());
298 // Check that the index in parent has been updated.
299 EXPECT_EQ(1, child1_accessible->GetIndexInParent());
300 EXPECT_EQ(2, child2_accessible->GetIndexInParent());
302 // Release our references. The object count should only decrease by 1
303 // for child3.
304 root_accessible->NativeReleaseReference();
305 child1_accessible->NativeReleaseReference();
306 child2_accessible->NativeReleaseReference();
307 child3_accessible->NativeReleaseReference();
309 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
311 // Delete the manager and make sure all memory is cleaned up.
312 delete manager;
313 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
316 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
317 // Similar to the test above, but with a more complicated tree.
319 // Tree 1:
321 // root
322 // container
323 // child1
324 // grandchild1
325 // child2
326 // grandchild2
327 // child3
328 // grandchild3
330 ui::AXNodeData tree1_grandchild1;
331 tree1_grandchild1.id = 4;
332 tree1_grandchild1.SetName("GrandChild1");
333 tree1_grandchild1.role = ui::AX_ROLE_BUTTON;
334 tree1_grandchild1.state = 0;
336 ui::AXNodeData tree1_child1;
337 tree1_child1.id = 3;
338 tree1_child1.SetName("Child1");
339 tree1_child1.role = ui::AX_ROLE_BUTTON;
340 tree1_child1.state = 0;
341 tree1_child1.child_ids.push_back(4);
343 ui::AXNodeData tree1_grandchild2;
344 tree1_grandchild2.id = 6;
345 tree1_grandchild2.SetName("GrandChild1");
346 tree1_grandchild2.role = ui::AX_ROLE_BUTTON;
347 tree1_grandchild2.state = 0;
349 ui::AXNodeData tree1_child2;
350 tree1_child2.id = 5;
351 tree1_child2.SetName("Child2");
352 tree1_child2.role = ui::AX_ROLE_BUTTON;
353 tree1_child2.state = 0;
354 tree1_child2.child_ids.push_back(6);
356 ui::AXNodeData tree1_grandchild3;
357 tree1_grandchild3.id = 8;
358 tree1_grandchild3.SetName("GrandChild3");
359 tree1_grandchild3.role = ui::AX_ROLE_BUTTON;
360 tree1_grandchild3.state = 0;
362 ui::AXNodeData tree1_child3;
363 tree1_child3.id = 7;
364 tree1_child3.SetName("Child3");
365 tree1_child3.role = ui::AX_ROLE_BUTTON;
366 tree1_child3.state = 0;
367 tree1_child3.child_ids.push_back(8);
369 ui::AXNodeData tree1_container;
370 tree1_container.id = 2;
371 tree1_container.SetName("Container");
372 tree1_container.role = ui::AX_ROLE_GROUP;
373 tree1_container.state = 0;
374 tree1_container.child_ids.push_back(3);
375 tree1_container.child_ids.push_back(5);
376 tree1_container.child_ids.push_back(7);
378 ui::AXNodeData tree1_root;
379 tree1_root.id = 1;
380 tree1_root.SetName("Document");
381 tree1_root.role = ui::AX_ROLE_ROOT_WEB_AREA;
382 tree1_root.state = 0;
383 tree1_root.child_ids.push_back(2);
385 // Tree 2:
387 // root
388 // container
389 // child0 <-- inserted
390 // grandchild0 <--
391 // child1
392 // grandchild1
393 // child2
394 // grandchild2
395 // <-- child3 (and grandchild3) deleted
397 ui::AXNodeData tree2_grandchild0;
398 tree2_grandchild0.id = 9;
399 tree2_grandchild0.SetName("GrandChild0");
400 tree2_grandchild0.role = ui::AX_ROLE_BUTTON;
401 tree2_grandchild0.state = 0;
403 ui::AXNodeData tree2_child0;
404 tree2_child0.id = 10;
405 tree2_child0.SetName("Child0");
406 tree2_child0.role = ui::AX_ROLE_BUTTON;
407 tree2_child0.state = 0;
408 tree2_child0.child_ids.push_back(9);
410 ui::AXNodeData tree2_container;
411 tree2_container.id = 2;
412 tree2_container.SetName("Container");
413 tree2_container.role = ui::AX_ROLE_GROUP;
414 tree2_container.state = 0;
415 tree2_container.child_ids.push_back(10);
416 tree2_container.child_ids.push_back(3);
417 tree2_container.child_ids.push_back(5);
419 // Construct a BrowserAccessibilityManager with tree1.
420 CountedBrowserAccessibility::global_obj_count_ = 0;
421 BrowserAccessibilityManager* manager =
422 BrowserAccessibilityManager::Create(
423 MakeAXTreeUpdate(tree1_root, tree1_container,
424 tree1_child1, tree1_grandchild1,
425 tree1_child2, tree1_grandchild2,
426 tree1_child3, tree1_grandchild3),
427 nullptr,
428 new CountedBrowserAccessibilityFactory());
429 ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
431 // Save references to some objects.
432 CountedBrowserAccessibility* root_accessible =
433 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
434 root_accessible->NativeAddReference();
435 CountedBrowserAccessibility* container_accessible =
436 static_cast<CountedBrowserAccessibility*>(
437 root_accessible->PlatformGetChild(0));
438 container_accessible->NativeAddReference();
439 CountedBrowserAccessibility* child2_accessible =
440 static_cast<CountedBrowserAccessibility*>(
441 container_accessible->PlatformGetChild(1));
442 child2_accessible->NativeAddReference();
443 CountedBrowserAccessibility* child3_accessible =
444 static_cast<CountedBrowserAccessibility*>(
445 container_accessible->PlatformGetChild(2));
446 child3_accessible->NativeAddReference();
448 // Check the index in parent.
449 EXPECT_EQ(1, child2_accessible->GetIndexInParent());
450 EXPECT_EQ(2, child3_accessible->GetIndexInParent());
452 // Process a notification containing the changed subtree rooted at
453 // the container.
454 std::vector<AccessibilityHostMsg_EventParams> params;
455 params.push_back(AccessibilityHostMsg_EventParams());
456 AccessibilityHostMsg_EventParams* msg = &params[0];
457 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
458 msg->update.nodes.push_back(tree2_container);
459 msg->update.nodes.push_back(tree2_child0);
460 msg->update.nodes.push_back(tree2_grandchild0);
461 msg->id = tree2_container.id;
462 manager->OnAccessibilityEvents(params);
464 // There should be 9 objects now: the 8 from the new tree, plus the
465 // reference to child3 we kept.
466 EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_);
468 // Check that our references to the root and container and child2 are
469 // still valid, but that the reference to child3 is now invalid.
470 EXPECT_TRUE(root_accessible->instance_active());
471 EXPECT_TRUE(container_accessible->instance_active());
472 EXPECT_TRUE(child2_accessible->instance_active());
473 EXPECT_FALSE(child3_accessible->instance_active());
475 // Ensure that we retain the parent of the detached subtree.
476 EXPECT_EQ(root_accessible, container_accessible->GetParent());
477 EXPECT_EQ(0, container_accessible->GetIndexInParent());
479 // Check that the index in parent has been updated.
480 EXPECT_EQ(2, child2_accessible->GetIndexInParent());
482 // Release our references. The object count should only decrease by 1
483 // for child3.
484 root_accessible->NativeReleaseReference();
485 container_accessible->NativeReleaseReference();
486 child2_accessible->NativeReleaseReference();
487 child3_accessible->NativeReleaseReference();
489 EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
491 // Delete the manager and make sure all memory is cleaned up.
492 delete manager;
493 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
496 TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
497 // Tree 1:
499 // 1
500 // 2
501 // 3
502 // 4
504 ui::AXNodeData tree1_4;
505 tree1_4.id = 4;
506 tree1_4.state = 0;
508 ui::AXNodeData tree1_3;
509 tree1_3.id = 3;
510 tree1_3.state = 0;
511 tree1_3.child_ids.push_back(4);
513 ui::AXNodeData tree1_2;
514 tree1_2.id = 2;
515 tree1_2.state = 0;
517 ui::AXNodeData tree1_1;
518 tree1_1.id = 1;
519 tree1_1.role = ui::AX_ROLE_ROOT_WEB_AREA;
520 tree1_1.state = 0;
521 tree1_1.child_ids.push_back(2);
522 tree1_1.child_ids.push_back(3);
524 // Tree 2:
526 // 1
527 // 4 <-- moves up a level and gains child
528 // 6 <-- new
529 // 5 <-- new
531 ui::AXNodeData tree2_6;
532 tree2_6.id = 6;
533 tree2_6.state = 0;
535 ui::AXNodeData tree2_5;
536 tree2_5.id = 5;
537 tree2_5.state = 0;
539 ui::AXNodeData tree2_4;
540 tree2_4.id = 4;
541 tree2_4.state = 0;
542 tree2_4.child_ids.push_back(6);
544 ui::AXNodeData tree2_1;
545 tree2_1.id = 1;
546 tree2_1.state = 0;
547 tree2_1.child_ids.push_back(4);
548 tree2_1.child_ids.push_back(5);
550 // Construct a BrowserAccessibilityManager with tree1.
551 CountedBrowserAccessibility::global_obj_count_ = 0;
552 BrowserAccessibilityManager* manager =
553 BrowserAccessibilityManager::Create(
554 MakeAXTreeUpdate(tree1_1, tree1_2, tree1_3, tree1_4),
555 nullptr,
556 new CountedBrowserAccessibilityFactory());
557 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
559 // Process a notification containing the changed subtree.
560 std::vector<AccessibilityHostMsg_EventParams> params;
561 params.push_back(AccessibilityHostMsg_EventParams());
562 AccessibilityHostMsg_EventParams* msg = &params[0];
563 msg->event_type = ui::AX_EVENT_CHILDREN_CHANGED;
564 msg->update.nodes.push_back(tree2_1);
565 msg->update.nodes.push_back(tree2_4);
566 msg->update.nodes.push_back(tree2_5);
567 msg->update.nodes.push_back(tree2_6);
568 msg->id = tree2_1.id;
569 manager->OnAccessibilityEvents(params);
571 // There should be 4 objects now.
572 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
574 // Delete the manager and make sure all memory is cleaned up.
575 delete manager;
576 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
579 TEST(BrowserAccessibilityManagerTest, TestFatalError) {
580 // Test that BrowserAccessibilityManager raises a fatal error
581 // (which will crash the renderer) if the same id is used in
582 // two places in the tree.
584 ui::AXNodeData root;
585 root.id = 1;
586 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
587 root.child_ids.push_back(2);
588 root.child_ids.push_back(2);
590 CountedBrowserAccessibilityFactory* factory =
591 new CountedBrowserAccessibilityFactory();
592 scoped_ptr<TestBrowserAccessibilityDelegate> delegate(
593 new TestBrowserAccessibilityDelegate());
594 scoped_ptr<BrowserAccessibilityManager> manager;
595 ASSERT_FALSE(delegate->got_fatal_error());
596 manager.reset(BrowserAccessibilityManager::Create(
597 MakeAXTreeUpdate(root),
598 delegate.get(),
599 factory));
600 ASSERT_TRUE(delegate->got_fatal_error());
602 ui::AXNodeData root2;
603 root2.id = 1;
604 root2.role = ui::AX_ROLE_ROOT_WEB_AREA;
605 root2.child_ids.push_back(2);
606 root2.child_ids.push_back(3);
608 ui::AXNodeData child1;
609 child1.id = 2;
610 child1.child_ids.push_back(4);
611 child1.child_ids.push_back(5);
613 ui::AXNodeData child2;
614 child2.id = 3;
615 child2.child_ids.push_back(6);
616 child2.child_ids.push_back(5); // Duplicate
618 ui::AXNodeData grandchild4;
619 grandchild4.id = 4;
621 ui::AXNodeData grandchild5;
622 grandchild5.id = 5;
624 ui::AXNodeData grandchild6;
625 grandchild6.id = 6;
627 delegate->reset_got_fatal_error();
628 factory = new CountedBrowserAccessibilityFactory();
629 manager.reset(BrowserAccessibilityManager::Create(
630 MakeAXTreeUpdate(root2, child1, child2,
631 grandchild4, grandchild5, grandchild6),
632 delegate.get(),
633 factory));
634 ASSERT_TRUE(delegate->got_fatal_error());
637 TEST(BrowserAccessibilityManagerTest, BoundsForRange) {
638 ui::AXNodeData root;
639 root.id = 1;
640 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
642 ui::AXNodeData static_text;
643 static_text.id = 2;
644 static_text.SetValue("Hello, world.");
645 static_text.role = ui::AX_ROLE_STATIC_TEXT;
646 static_text.location = gfx::Rect(100, 100, 29, 18);
647 root.child_ids.push_back(2);
649 ui::AXNodeData inline_text1;
650 inline_text1.id = 3;
651 inline_text1.SetValue("Hello, ");
652 inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
653 inline_text1.location = gfx::Rect(100, 100, 29, 9);
654 inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
655 ui::AX_TEXT_DIRECTION_LTR);
656 std::vector<int32> character_offsets1;
657 character_offsets1.push_back(6); // 0
658 character_offsets1.push_back(11); // 1
659 character_offsets1.push_back(16); // 2
660 character_offsets1.push_back(21); // 3
661 character_offsets1.push_back(26); // 4
662 character_offsets1.push_back(29); // 5
663 character_offsets1.push_back(29); // 6 (note that the space has no width)
664 inline_text1.AddIntListAttribute(
665 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
666 static_text.child_ids.push_back(3);
668 ui::AXNodeData inline_text2;
669 inline_text2.id = 4;
670 inline_text2.SetValue("world.");
671 inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
672 inline_text2.location = gfx::Rect(100, 109, 28, 9);
673 inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
674 ui::AX_TEXT_DIRECTION_LTR);
675 std::vector<int32> character_offsets2;
676 character_offsets2.push_back(5);
677 character_offsets2.push_back(10);
678 character_offsets2.push_back(15);
679 character_offsets2.push_back(20);
680 character_offsets2.push_back(25);
681 character_offsets2.push_back(28);
682 inline_text2.AddIntListAttribute(
683 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
684 static_text.child_ids.push_back(4);
686 scoped_ptr<BrowserAccessibilityManager> manager(
687 BrowserAccessibilityManager::Create(
688 MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
689 nullptr,
690 new CountedBrowserAccessibilityFactory()));
692 BrowserAccessibility* root_accessible = manager->GetRoot();
693 BrowserAccessibility* static_text_accessible =
694 root_accessible->PlatformGetChild(0);
696 EXPECT_EQ(gfx::Rect(100, 100, 6, 9).ToString(),
697 static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
699 EXPECT_EQ(gfx::Rect(100, 100, 26, 9).ToString(),
700 static_text_accessible->GetLocalBoundsForRange(0, 5).ToString());
702 EXPECT_EQ(gfx::Rect(100, 109, 5, 9).ToString(),
703 static_text_accessible->GetLocalBoundsForRange(7, 1).ToString());
705 EXPECT_EQ(gfx::Rect(100, 109, 25, 9).ToString(),
706 static_text_accessible->GetLocalBoundsForRange(7, 5).ToString());
708 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
709 static_text_accessible->GetLocalBoundsForRange(5, 3).ToString());
711 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
712 static_text_accessible->GetLocalBoundsForRange(0, 13).ToString());
714 // Test range that's beyond the text.
715 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
716 static_text_accessible->GetLocalBoundsForRange(-1, 999).ToString());
718 // Test that we can call bounds for range on the parent element, too,
719 // and it still works.
720 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
721 root_accessible->GetLocalBoundsForRange(0, 13).ToString());
724 TEST(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) {
725 // In this example, we assume that the string "123abc" is rendered with
726 // "123" going left-to-right and "abc" going right-to-left. In other
727 // words, on-screen it would look like "123cba". This is possible to
728 // acheive if the source string had unicode control characters
729 // to switch directions. This test doesn't worry about how, though - it just
730 // tests that if something like that were to occur, GetLocalBoundsForRange
731 // returns the correct bounds for different ranges.
733 ui::AXNodeData root;
734 root.id = 1;
735 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
737 ui::AXNodeData static_text;
738 static_text.id = 2;
739 static_text.SetValue("123abc");
740 static_text.role = ui::AX_ROLE_STATIC_TEXT;
741 static_text.location = gfx::Rect(100, 100, 60, 20);
742 root.child_ids.push_back(2);
744 ui::AXNodeData inline_text1;
745 inline_text1.id = 3;
746 inline_text1.SetValue("123");
747 inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
748 inline_text1.location = gfx::Rect(100, 100, 30, 20);
749 inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
750 ui::AX_TEXT_DIRECTION_LTR);
751 std::vector<int32> character_offsets1;
752 character_offsets1.push_back(10); // 0
753 character_offsets1.push_back(20); // 1
754 character_offsets1.push_back(30); // 2
755 inline_text1.AddIntListAttribute(
756 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
757 static_text.child_ids.push_back(3);
759 ui::AXNodeData inline_text2;
760 inline_text2.id = 4;
761 inline_text2.SetValue("abc");
762 inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
763 inline_text2.location = gfx::Rect(130, 100, 30, 20);
764 inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
765 ui::AX_TEXT_DIRECTION_RTL);
766 std::vector<int32> character_offsets2;
767 character_offsets2.push_back(10);
768 character_offsets2.push_back(20);
769 character_offsets2.push_back(30);
770 inline_text2.AddIntListAttribute(
771 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
772 static_text.child_ids.push_back(4);
774 scoped_ptr<BrowserAccessibilityManager> manager(
775 BrowserAccessibilityManager::Create(
776 MakeAXTreeUpdate(root, static_text, inline_text1, inline_text2),
777 nullptr,
778 new CountedBrowserAccessibilityFactory()));
780 BrowserAccessibility* root_accessible = manager->GetRoot();
781 BrowserAccessibility* static_text_accessible =
782 root_accessible->PlatformGetChild(0);
784 EXPECT_EQ(gfx::Rect(100, 100, 60, 20).ToString(),
785 static_text_accessible->GetLocalBoundsForRange(0, 6).ToString());
787 EXPECT_EQ(gfx::Rect(100, 100, 10, 20).ToString(),
788 static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
790 EXPECT_EQ(gfx::Rect(100, 100, 30, 20).ToString(),
791 static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
793 EXPECT_EQ(gfx::Rect(150, 100, 10, 20).ToString(),
794 static_text_accessible->GetLocalBoundsForRange(3, 1).ToString());
796 EXPECT_EQ(gfx::Rect(130, 100, 30, 20).ToString(),
797 static_text_accessible->GetLocalBoundsForRange(3, 3).ToString());
799 // This range is only two characters, but because of the direction switch
800 // the bounds are as wide as four characters.
801 EXPECT_EQ(gfx::Rect(120, 100, 40, 20).ToString(),
802 static_text_accessible->GetLocalBoundsForRange(2, 2).ToString());
805 TEST(BrowserAccessibilityManagerTest, BoundsForRangeScrolledWindow) {
806 ui::AXNodeData root;
807 root.id = 1;
808 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
809 root.AddIntAttribute(ui::AX_ATTR_SCROLL_X, 25);
810 root.AddIntAttribute(ui::AX_ATTR_SCROLL_Y, 50);
812 ui::AXNodeData static_text;
813 static_text.id = 2;
814 static_text.SetValue("ABC");
815 static_text.role = ui::AX_ROLE_STATIC_TEXT;
816 static_text.location = gfx::Rect(100, 100, 16, 9);
817 root.child_ids.push_back(2);
819 ui::AXNodeData inline_text;
820 inline_text.id = 3;
821 inline_text.SetValue("ABC");
822 inline_text.role = ui::AX_ROLE_INLINE_TEXT_BOX;
823 inline_text.location = gfx::Rect(100, 100, 16, 9);
824 inline_text.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
825 ui::AX_TEXT_DIRECTION_LTR);
826 std::vector<int32> character_offsets1;
827 character_offsets1.push_back(6); // 0
828 character_offsets1.push_back(11); // 1
829 character_offsets1.push_back(16); // 2
830 inline_text.AddIntListAttribute(
831 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
832 static_text.child_ids.push_back(3);
834 scoped_ptr<BrowserAccessibilityManager> manager(
835 BrowserAccessibilityManager::Create(
836 MakeAXTreeUpdate(root, static_text, inline_text),
837 nullptr,
838 new CountedBrowserAccessibilityFactory()));
840 BrowserAccessibility* root_accessible = manager->GetRoot();
841 BrowserAccessibility* static_text_accessible =
842 root_accessible->PlatformGetChild(0);
844 if (manager->UseRootScrollOffsetsWhenComputingBounds()) {
845 EXPECT_EQ(gfx::Rect(75, 50, 16, 9).ToString(),
846 static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
847 } else {
848 EXPECT_EQ(gfx::Rect(100, 100, 16, 9).ToString(),
849 static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
853 #if defined(OS_WIN)
854 #define MAYBE_BoundsForRangeOnParentElement \
855 DISABLED_BoundsForRangeOnParentElement
856 #else
857 #define MAYBE_BoundsForRangeOnParentElement BoundsForRangeOnParentElement
858 #endif
859 TEST(BrowserAccessibilityManagerTest, MAYBE_BoundsForRangeOnParentElement) {
860 ui::AXNodeData root;
861 root.id = 1;
862 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
863 root.child_ids.push_back(2);
865 ui::AXNodeData div;
866 div.id = 2;
867 div.role = ui::AX_ROLE_DIV;
868 div.location = gfx::Rect(100, 100, 100, 20);
869 div.child_ids.push_back(3);
870 div.child_ids.push_back(4);
871 div.child_ids.push_back(5);
873 ui::AXNodeData static_text1;
874 static_text1.id = 3;
875 static_text1.SetValue("AB");
876 static_text1.role = ui::AX_ROLE_STATIC_TEXT;
877 static_text1.location = gfx::Rect(100, 100, 40, 20);
878 static_text1.child_ids.push_back(6);
880 ui::AXNodeData img;
881 img.id = 4;
882 img.role = ui::AX_ROLE_IMAGE;
883 img.location = gfx::Rect(140, 100, 20, 20);
885 ui::AXNodeData static_text2;
886 static_text2.id = 5;
887 static_text2.SetValue("CD");
888 static_text2.role = ui::AX_ROLE_STATIC_TEXT;
889 static_text2.location = gfx::Rect(160, 100, 40, 20);
890 static_text2.child_ids.push_back(7);
892 ui::AXNodeData inline_text1;
893 inline_text1.id = 6;
894 inline_text1.SetValue("AB");
895 inline_text1.role = ui::AX_ROLE_INLINE_TEXT_BOX;
896 inline_text1.location = gfx::Rect(100, 100, 40, 20);
897 inline_text1.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
898 ui::AX_TEXT_DIRECTION_LTR);
899 std::vector<int32> character_offsets1;
900 character_offsets1.push_back(20); // 0
901 character_offsets1.push_back(40); // 1
902 inline_text1.AddIntListAttribute(
903 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets1);
905 ui::AXNodeData inline_text2;
906 inline_text2.id = 7;
907 inline_text2.SetValue("CD");
908 inline_text2.role = ui::AX_ROLE_INLINE_TEXT_BOX;
909 inline_text2.location = gfx::Rect(160, 100, 40, 20);
910 inline_text2.AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
911 ui::AX_TEXT_DIRECTION_LTR);
912 std::vector<int32> character_offsets2;
913 character_offsets2.push_back(20); // 0
914 character_offsets2.push_back(40); // 1
915 inline_text2.AddIntListAttribute(
916 ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets2);
918 scoped_ptr<BrowserAccessibilityManager> manager(
919 BrowserAccessibilityManager::Create(
920 MakeAXTreeUpdate(
921 root, div, static_text1, img,
922 static_text2, inline_text1, inline_text2),
923 nullptr,
924 new CountedBrowserAccessibilityFactory()));
925 BrowserAccessibility* root_accessible = manager->GetRoot();
927 EXPECT_EQ(gfx::Rect(100, 100, 20, 20).ToString(),
928 root_accessible->GetLocalBoundsForRange(0, 1).ToString());
930 EXPECT_EQ(gfx::Rect(100, 100, 40, 20).ToString(),
931 root_accessible->GetLocalBoundsForRange(0, 2).ToString());
933 EXPECT_EQ(gfx::Rect(100, 100, 80, 20).ToString(),
934 root_accessible->GetLocalBoundsForRange(0, 3).ToString());
936 EXPECT_EQ(gfx::Rect(120, 100, 60, 20).ToString(),
937 root_accessible->GetLocalBoundsForRange(1, 2).ToString());
939 EXPECT_EQ(gfx::Rect(120, 100, 80, 20).ToString(),
940 root_accessible->GetLocalBoundsForRange(1, 3).ToString());
942 EXPECT_EQ(gfx::Rect(100, 100, 100, 20).ToString(),
943 root_accessible->GetLocalBoundsForRange(0, 4).ToString());
946 TEST(BrowserAccessibilityManagerTest, NextPreviousInTreeOrder) {
947 ui::AXNodeData root;
948 root.id = 1;
949 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
951 ui::AXNodeData node2;
952 node2.id = 2;
953 root.child_ids.push_back(2);
955 ui::AXNodeData node3;
956 node3.id = 3;
957 root.child_ids.push_back(3);
959 ui::AXNodeData node4;
960 node4.id = 4;
961 node3.child_ids.push_back(4);
963 ui::AXNodeData node5;
964 node5.id = 5;
965 root.child_ids.push_back(5);
967 scoped_ptr<BrowserAccessibilityManager> manager(
968 BrowserAccessibilityManager::Create(
969 MakeAXTreeUpdate(root, node2, node3, node4, node5),
970 nullptr,
971 new CountedBrowserAccessibilityFactory()));
973 BrowserAccessibility* root_accessible = manager->GetRoot();
974 BrowserAccessibility* node2_accessible = root_accessible->PlatformGetChild(0);
975 BrowserAccessibility* node3_accessible = root_accessible->PlatformGetChild(1);
976 BrowserAccessibility* node4_accessible =
977 node3_accessible->PlatformGetChild(0);
978 BrowserAccessibility* node5_accessible = root_accessible->PlatformGetChild(2);
980 ASSERT_EQ(nullptr, manager->NextInTreeOrder(nullptr));
981 ASSERT_EQ(node2_accessible, manager->NextInTreeOrder(root_accessible));
982 ASSERT_EQ(node3_accessible, manager->NextInTreeOrder(node2_accessible));
983 ASSERT_EQ(node4_accessible, manager->NextInTreeOrder(node3_accessible));
984 ASSERT_EQ(node5_accessible, manager->NextInTreeOrder(node4_accessible));
985 ASSERT_EQ(nullptr, manager->NextInTreeOrder(node5_accessible));
987 ASSERT_EQ(nullptr, manager->PreviousInTreeOrder(nullptr));
988 ASSERT_EQ(node4_accessible, manager->PreviousInTreeOrder(node5_accessible));
989 ASSERT_EQ(node3_accessible, manager->PreviousInTreeOrder(node4_accessible));
990 ASSERT_EQ(node2_accessible, manager->PreviousInTreeOrder(node3_accessible));
991 ASSERT_EQ(root_accessible, manager->PreviousInTreeOrder(node2_accessible));
994 TEST(BrowserAccessibilityManagerTest, DeletingFocusedNodeDoesNotCrash) {
995 // Create a really simple tree with one root node and one focused child.
996 ui::AXNodeData root;
997 root.id = 1;
998 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
999 root.state = 0;
1000 root.child_ids.push_back(2);
1002 ui::AXNodeData node2;
1003 node2.id = 2;
1004 node2.state = 1 << ui::AX_STATE_FOCUSED;
1006 scoped_ptr<BrowserAccessibilityManager> manager(
1007 BrowserAccessibilityManager::Create(
1008 MakeAXTreeUpdate(root, node2),
1009 nullptr,
1010 new CountedBrowserAccessibilityFactory()));
1012 ASSERT_EQ(1, manager->GetRoot()->GetId());
1013 ASSERT_EQ(1, manager->GetFocus(manager->GetRoot())->GetId());
1015 // Send the focus event for node 2.
1016 std::vector<AccessibilityHostMsg_EventParams> events;
1017 events.push_back(AccessibilityHostMsg_EventParams());
1018 events[0].update = MakeAXTreeUpdate(node2);
1019 events[0].id = 2;
1020 events[0].event_type = ui::AX_EVENT_FOCUS;
1021 manager->OnAccessibilityEvents(events);
1023 ASSERT_EQ(1, manager->GetRoot()->GetId());
1024 ASSERT_EQ(2, manager->GetFocus(manager->GetRoot())->GetId());
1026 // Now replace the tree with a new tree consisting of a single root.
1027 ui::AXNodeData root2;
1028 root2.id = 3;
1029 root2.role = ui::AX_ROLE_ROOT_WEB_AREA;
1030 root2.state = 0;
1032 std::vector<AccessibilityHostMsg_EventParams> events2;
1033 events2.push_back(AccessibilityHostMsg_EventParams());
1034 events2[0].update = MakeAXTreeUpdate(root2);
1035 events2[0].id = -1;
1036 events2[0].event_type = ui::AX_EVENT_NONE;
1037 manager->OnAccessibilityEvents(events2);
1039 // Make sure that the focused node was updated to the new root and
1040 // that this doesn't crash.
1041 ASSERT_EQ(3, manager->GetRoot()->GetId());
1042 ASSERT_EQ(3, manager->GetFocus(manager->GetRoot())->GetId());
1045 TEST(BrowserAccessibilityManagerTest, DeletingFocusedNodeDoesNotCrash2) {
1046 // Create a really simple tree with one root node and one focused child.
1047 ui::AXNodeData root;
1048 root.id = 1;
1049 root.role = ui::AX_ROLE_ROOT_WEB_AREA;
1050 root.state = 0;
1051 root.child_ids.push_back(2);
1052 root.child_ids.push_back(3);
1053 root.child_ids.push_back(4);
1055 ui::AXNodeData node2;
1056 node2.id = 2;
1057 node2.state = 1 << ui::AX_STATE_FOCUSED;
1059 ui::AXNodeData node3;
1060 node3.id = 3;
1061 node3.state = 0;
1063 ui::AXNodeData node4;
1064 node4.id = 4;
1065 node4.state = 0;
1067 scoped_ptr<BrowserAccessibilityManager> manager(
1068 BrowserAccessibilityManager::Create(
1069 MakeAXTreeUpdate(root, node2, node3, node4),
1070 nullptr,
1071 new CountedBrowserAccessibilityFactory()));
1073 ASSERT_EQ(1, manager->GetRoot()->GetId());
1074 ASSERT_EQ(1, manager->GetFocus(manager->GetRoot())->GetId());
1076 // Send the focus event for node 2.
1077 std::vector<AccessibilityHostMsg_EventParams> events;
1078 events.push_back(AccessibilityHostMsg_EventParams());
1079 events[0].update = MakeAXTreeUpdate(node2);
1080 events[0].id = 2;
1081 events[0].event_type = ui::AX_EVENT_FOCUS;
1082 manager->OnAccessibilityEvents(events);
1084 ASSERT_EQ(1, manager->GetRoot()->GetId());
1085 ASSERT_EQ(2, manager->GetFocus(manager->GetRoot())->GetId());
1087 // Now replace the tree with a new tree consisting of a single root.
1088 ui::AXNodeData root2;
1089 root2.id = 3;
1090 root2.role = ui::AX_ROLE_ROOT_WEB_AREA;
1091 root2.state = 0;
1093 // Make an update the explicitly clears the previous root.
1094 std::vector<AccessibilityHostMsg_EventParams> events2;
1095 events2.push_back(AccessibilityHostMsg_EventParams());
1096 events2[0].update = MakeAXTreeUpdate(root2);
1097 events2[0].update.node_id_to_clear = 1;
1098 events2[0].id = -1;
1099 events2[0].event_type = ui::AX_EVENT_NONE;
1100 manager->OnAccessibilityEvents(events2);
1102 // Make sure that the focused node was updated to the new root and
1103 // that this doesn't crash.
1104 ASSERT_EQ(3, manager->GetRoot()->GetId());
1105 ASSERT_EQ(3, manager->GetFocus(manager->GetRoot())->GetId());
1108 } // namespace content