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.
4
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 #include "content/common/accessibility_messages.h"
10 #include "content/common/accessibility_node_data.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12
13 namespace content {
14 namespace {
15
16 // Subclass of BrowserAccessibility that counts the number of instances.
17 class CountedBrowserAccessibility : public BrowserAccessibility {
18 public:
CountedBrowserAccessibility()19 CountedBrowserAccessibility() {
20 global_obj_count_++;
21 native_ref_count_ = 1;
22 }
~CountedBrowserAccessibility()23 virtual ~CountedBrowserAccessibility() {
24 global_obj_count_--;
25 }
26
NativeAddReference()27 virtual void NativeAddReference() OVERRIDE {
28 native_ref_count_++;
29 }
30
NativeReleaseReference()31 virtual void NativeReleaseReference() OVERRIDE {
32 native_ref_count_--;
33 if (native_ref_count_ == 0)
34 delete this;
35 }
36
37 int native_ref_count_;
38 static int global_obj_count_;
39 };
40
41 int CountedBrowserAccessibility::global_obj_count_ = 0;
42
43 // Factory that creates a CountedBrowserAccessibility.
44 class CountedBrowserAccessibilityFactory
45 : public BrowserAccessibilityFactory {
46 public:
~CountedBrowserAccessibilityFactory()47 virtual ~CountedBrowserAccessibilityFactory() {}
Create()48 virtual BrowserAccessibility* Create() OVERRIDE {
49 return new CountedBrowserAccessibility();
50 }
51 };
52
53 class TestBrowserAccessibilityDelegate
54 : public BrowserAccessibilityDelegate {
55 public:
TestBrowserAccessibilityDelegate()56 TestBrowserAccessibilityDelegate()
57 : got_fatal_error_(false) {}
58
SetAccessibilityFocus(int acc_obj_id)59 virtual void SetAccessibilityFocus(int acc_obj_id) OVERRIDE {}
AccessibilityDoDefaultAction(int acc_obj_id)60 virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE {}
AccessibilityScrollToMakeVisible(int acc_obj_id,gfx::Rect subfocus)61 virtual void AccessibilityScrollToMakeVisible(
62 int acc_obj_id, gfx::Rect subfocus) OVERRIDE {}
AccessibilityScrollToPoint(int acc_obj_id,gfx::Point point)63 virtual void AccessibilityScrollToPoint(
64 int acc_obj_id, gfx::Point point) OVERRIDE {}
AccessibilitySetTextSelection(int acc_obj_id,int start_offset,int end_offset)65 virtual void AccessibilitySetTextSelection(
66 int acc_obj_id, int start_offset, int end_offset) OVERRIDE {}
HasFocus() const67 virtual bool HasFocus() const OVERRIDE {
68 return false;
69 }
GetViewBounds() const70 virtual gfx::Rect GetViewBounds() const OVERRIDE {
71 return gfx::Rect();
72 }
GetLastTouchEventLocation() const73 virtual gfx::Point GetLastTouchEventLocation() const OVERRIDE {
74 return gfx::Point();
75 }
FatalAccessibilityTreeError()76 virtual void FatalAccessibilityTreeError() OVERRIDE {
77 got_fatal_error_ = true;
78 }
79
got_fatal_error() const80 bool got_fatal_error() const { return got_fatal_error_; }
reset_got_fatal_error()81 void reset_got_fatal_error() { got_fatal_error_ = false; }
82
83 private:
84 bool got_fatal_error_;
85 };
86
87 } // anonymous namespace
88
TEST(BrowserAccessibilityManagerTest,TestNoLeaks)89 TEST(BrowserAccessibilityManagerTest, TestNoLeaks) {
90 // Create AccessibilityNodeData objects for a simple document tree,
91 // representing the accessibility information used to initialize
92 // BrowserAccessibilityManager.
93 AccessibilityNodeData button;
94 button.id = 2;
95 button.SetName("Button");
96 button.role = blink::WebAXRoleButton;
97 button.state = 0;
98
99 AccessibilityNodeData checkbox;
100 checkbox.id = 3;
101 checkbox.SetName("Checkbox");
102 checkbox.role = blink::WebAXRoleCheckBox;
103 checkbox.state = 0;
104
105 AccessibilityNodeData root;
106 root.id = 1;
107 root.SetName("Document");
108 root.role = blink::WebAXRoleRootWebArea;
109 root.state = 0;
110 root.child_ids.push_back(2);
111 root.child_ids.push_back(3);
112
113 // Construct a BrowserAccessibilityManager with this
114 // AccessibilityNodeData tree and a factory for an instance-counting
115 // BrowserAccessibility, and ensure that exactly 3 instances were
116 // created. Note that the manager takes ownership of the factory.
117 CountedBrowserAccessibility::global_obj_count_ = 0;
118 BrowserAccessibilityManager* manager =
119 BrowserAccessibilityManager::Create(
120 root,
121 NULL,
122 new CountedBrowserAccessibilityFactory());
123 manager->UpdateNodesForTesting(button, checkbox);
124
125 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
126
127 // Delete the manager and test that all 3 instances are deleted.
128 delete manager;
129 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
130
131 // Construct a manager again, and this time save references to two of
132 // the three nodes in the tree.
133 manager =
134 BrowserAccessibilityManager::Create(
135 root,
136 NULL,
137 new CountedBrowserAccessibilityFactory());
138 manager->UpdateNodesForTesting(button, checkbox);
139 ASSERT_EQ(3, CountedBrowserAccessibility::global_obj_count_);
140
141 CountedBrowserAccessibility* root_accessible =
142 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
143 root_accessible->NativeAddReference();
144 CountedBrowserAccessibility* child1_accessible =
145 static_cast<CountedBrowserAccessibility*>(
146 root_accessible->PlatformGetChild(1));
147 child1_accessible->NativeAddReference();
148
149 // Now delete the manager, and only one of the three nodes in the tree
150 // should be released.
151 delete manager;
152 ASSERT_EQ(2, CountedBrowserAccessibility::global_obj_count_);
153
154 // Release each of our references and make sure that each one results in
155 // the instance being deleted as its reference count hits zero.
156 root_accessible->NativeReleaseReference();
157 ASSERT_EQ(1, CountedBrowserAccessibility::global_obj_count_);
158 child1_accessible->NativeReleaseReference();
159 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
160 }
161
TEST(BrowserAccessibilityManagerTest,TestReuseBrowserAccessibilityObjects)162 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects) {
163 // Make sure that changes to a subtree reuse as many objects as possible.
164
165 // Tree 1:
166 //
167 // root
168 // child1
169 // child2
170 // child3
171
172 AccessibilityNodeData tree1_child1;
173 tree1_child1.id = 2;
174 tree1_child1.SetName("Child1");
175 tree1_child1.role = blink::WebAXRoleButton;
176 tree1_child1.state = 0;
177
178 AccessibilityNodeData tree1_child2;
179 tree1_child2.id = 3;
180 tree1_child2.SetName("Child2");
181 tree1_child2.role = blink::WebAXRoleButton;
182 tree1_child2.state = 0;
183
184 AccessibilityNodeData tree1_child3;
185 tree1_child3.id = 4;
186 tree1_child3.SetName("Child3");
187 tree1_child3.role = blink::WebAXRoleButton;
188 tree1_child3.state = 0;
189
190 AccessibilityNodeData tree1_root;
191 tree1_root.id = 1;
192 tree1_root.SetName("Document");
193 tree1_root.role = blink::WebAXRoleRootWebArea;
194 tree1_root.state = 0;
195 tree1_root.child_ids.push_back(2);
196 tree1_root.child_ids.push_back(3);
197 tree1_root.child_ids.push_back(4);
198
199 // Tree 2:
200 //
201 // root
202 // child0 <-- inserted
203 // child1
204 // child2
205 // <-- child3 deleted
206
207 AccessibilityNodeData tree2_child0;
208 tree2_child0.id = 5;
209 tree2_child0.SetName("Child0");
210 tree2_child0.role = blink::WebAXRoleButton;
211 tree2_child0.state = 0;
212
213 AccessibilityNodeData tree2_root;
214 tree2_root.id = 1;
215 tree2_root.SetName("DocumentChanged");
216 tree2_root.role = blink::WebAXRoleRootWebArea;
217 tree2_root.state = 0;
218 tree2_root.child_ids.push_back(5);
219 tree2_root.child_ids.push_back(2);
220 tree2_root.child_ids.push_back(3);
221
222 // Construct a BrowserAccessibilityManager with tree1.
223 CountedBrowserAccessibility::global_obj_count_ = 0;
224 BrowserAccessibilityManager* manager =
225 BrowserAccessibilityManager::Create(
226 tree1_root,
227 NULL,
228 new CountedBrowserAccessibilityFactory());
229 manager->UpdateNodesForTesting(tree1_child1, tree1_child2, tree1_child3);
230 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
231
232 // Save references to all of the objects.
233 CountedBrowserAccessibility* root_accessible =
234 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
235 root_accessible->NativeAddReference();
236 CountedBrowserAccessibility* child1_accessible =
237 static_cast<CountedBrowserAccessibility*>(
238 root_accessible->PlatformGetChild(0));
239 child1_accessible->NativeAddReference();
240 CountedBrowserAccessibility* child2_accessible =
241 static_cast<CountedBrowserAccessibility*>(
242 root_accessible->PlatformGetChild(1));
243 child2_accessible->NativeAddReference();
244 CountedBrowserAccessibility* child3_accessible =
245 static_cast<CountedBrowserAccessibility*>(
246 root_accessible->PlatformGetChild(2));
247 child3_accessible->NativeAddReference();
248
249 // Check the index in parent.
250 EXPECT_EQ(0, child1_accessible->index_in_parent());
251 EXPECT_EQ(1, child2_accessible->index_in_parent());
252 EXPECT_EQ(2, child3_accessible->index_in_parent());
253
254 // Process a notification containing the changed subtree.
255 std::vector<AccessibilityHostMsg_EventParams> params;
256 params.push_back(AccessibilityHostMsg_EventParams());
257 AccessibilityHostMsg_EventParams* msg = ¶ms[0];
258 msg->event_type = blink::WebAXEventChildrenChanged;
259 msg->nodes.push_back(tree2_root);
260 msg->nodes.push_back(tree2_child0);
261 msg->id = tree2_root.id;
262 manager->OnAccessibilityEvents(params);
263
264 // There should be 5 objects now: the 4 from the new tree, plus the
265 // reference to child3 we kept.
266 EXPECT_EQ(5, CountedBrowserAccessibility::global_obj_count_);
267
268 // Check that our references to the root, child1, and child2 are still valid,
269 // but that the reference to child3 is now invalid.
270 EXPECT_TRUE(root_accessible->instance_active());
271 EXPECT_TRUE(child1_accessible->instance_active());
272 EXPECT_TRUE(child2_accessible->instance_active());
273 EXPECT_FALSE(child3_accessible->instance_active());
274
275 // Check that the index in parent has been updated.
276 EXPECT_EQ(1, child1_accessible->index_in_parent());
277 EXPECT_EQ(2, child2_accessible->index_in_parent());
278
279 // Release our references. The object count should only decrease by 1
280 // for child3.
281 root_accessible->NativeReleaseReference();
282 child1_accessible->NativeReleaseReference();
283 child2_accessible->NativeReleaseReference();
284 child3_accessible->NativeReleaseReference();
285
286 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
287
288 // Delete the manager and make sure all memory is cleaned up.
289 delete manager;
290 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
291 }
292
TEST(BrowserAccessibilityManagerTest,TestReuseBrowserAccessibilityObjects2)293 TEST(BrowserAccessibilityManagerTest, TestReuseBrowserAccessibilityObjects2) {
294 // Similar to the test above, but with a more complicated tree.
295
296 // Tree 1:
297 //
298 // root
299 // container
300 // child1
301 // grandchild1
302 // child2
303 // grandchild2
304 // child3
305 // grandchild3
306
307 AccessibilityNodeData tree1_grandchild1;
308 tree1_grandchild1.id = 4;
309 tree1_grandchild1.SetName("GrandChild1");
310 tree1_grandchild1.role = blink::WebAXRoleButton;
311 tree1_grandchild1.state = 0;
312
313 AccessibilityNodeData tree1_child1;
314 tree1_child1.id = 3;
315 tree1_child1.SetName("Child1");
316 tree1_child1.role = blink::WebAXRoleButton;
317 tree1_child1.state = 0;
318 tree1_child1.child_ids.push_back(4);
319
320 AccessibilityNodeData tree1_grandchild2;
321 tree1_grandchild2.id = 6;
322 tree1_grandchild2.SetName("GrandChild1");
323 tree1_grandchild2.role = blink::WebAXRoleButton;
324 tree1_grandchild2.state = 0;
325
326 AccessibilityNodeData tree1_child2;
327 tree1_child2.id = 5;
328 tree1_child2.SetName("Child2");
329 tree1_child2.role = blink::WebAXRoleButton;
330 tree1_child2.state = 0;
331 tree1_child2.child_ids.push_back(6);
332
333 AccessibilityNodeData tree1_grandchild3;
334 tree1_grandchild3.id = 8;
335 tree1_grandchild3.SetName("GrandChild3");
336 tree1_grandchild3.role = blink::WebAXRoleButton;
337 tree1_grandchild3.state = 0;
338
339 AccessibilityNodeData tree1_child3;
340 tree1_child3.id = 7;
341 tree1_child3.SetName("Child3");
342 tree1_child3.role = blink::WebAXRoleButton;
343 tree1_child3.state = 0;
344 tree1_child3.child_ids.push_back(8);
345
346 AccessibilityNodeData tree1_container;
347 tree1_container.id = 2;
348 tree1_container.SetName("Container");
349 tree1_container.role = blink::WebAXRoleGroup;
350 tree1_container.state = 0;
351 tree1_container.child_ids.push_back(3);
352 tree1_container.child_ids.push_back(5);
353 tree1_container.child_ids.push_back(7);
354
355 AccessibilityNodeData tree1_root;
356 tree1_root.id = 1;
357 tree1_root.SetName("Document");
358 tree1_root.role = blink::WebAXRoleRootWebArea;
359 tree1_root.state = 0;
360 tree1_root.child_ids.push_back(2);
361
362 // Tree 2:
363 //
364 // root
365 // container
366 // child0 <-- inserted
367 // grandchild0 <--
368 // child1
369 // grandchild1
370 // child2
371 // grandchild2
372 // <-- child3 (and grandchild3) deleted
373
374 AccessibilityNodeData tree2_grandchild0;
375 tree2_grandchild0.id = 9;
376 tree2_grandchild0.SetName("GrandChild0");
377 tree2_grandchild0.role = blink::WebAXRoleButton;
378 tree2_grandchild0.state = 0;
379
380 AccessibilityNodeData tree2_child0;
381 tree2_child0.id = 10;
382 tree2_child0.SetName("Child0");
383 tree2_child0.role = blink::WebAXRoleButton;
384 tree2_child0.state = 0;
385 tree2_child0.child_ids.push_back(9);
386
387 AccessibilityNodeData tree2_container;
388 tree2_container.id = 2;
389 tree2_container.SetName("Container");
390 tree2_container.role = blink::WebAXRoleGroup;
391 tree2_container.state = 0;
392 tree2_container.child_ids.push_back(10);
393 tree2_container.child_ids.push_back(3);
394 tree2_container.child_ids.push_back(5);
395
396 // Construct a BrowserAccessibilityManager with tree1.
397 CountedBrowserAccessibility::global_obj_count_ = 0;
398 BrowserAccessibilityManager* manager =
399 BrowserAccessibilityManager::Create(
400 tree1_root,
401 NULL,
402 new CountedBrowserAccessibilityFactory());
403 manager->UpdateNodesForTesting(tree1_container,
404 tree1_child1, tree1_grandchild1,
405 tree1_child2, tree1_grandchild2,
406 tree1_child3, tree1_grandchild3);
407 ASSERT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
408
409 // Save references to some objects.
410 CountedBrowserAccessibility* root_accessible =
411 static_cast<CountedBrowserAccessibility*>(manager->GetRoot());
412 root_accessible->NativeAddReference();
413 CountedBrowserAccessibility* container_accessible =
414 static_cast<CountedBrowserAccessibility*>(
415 root_accessible->PlatformGetChild(0));
416 container_accessible->NativeAddReference();
417 CountedBrowserAccessibility* child2_accessible =
418 static_cast<CountedBrowserAccessibility*>(
419 container_accessible->PlatformGetChild(1));
420 child2_accessible->NativeAddReference();
421 CountedBrowserAccessibility* child3_accessible =
422 static_cast<CountedBrowserAccessibility*>(
423 container_accessible->PlatformGetChild(2));
424 child3_accessible->NativeAddReference();
425
426 // Check the index in parent.
427 EXPECT_EQ(1, child2_accessible->index_in_parent());
428 EXPECT_EQ(2, child3_accessible->index_in_parent());
429
430 // Process a notification containing the changed subtree rooted at
431 // the container.
432 std::vector<AccessibilityHostMsg_EventParams> params;
433 params.push_back(AccessibilityHostMsg_EventParams());
434 AccessibilityHostMsg_EventParams* msg = ¶ms[0];
435 msg->event_type = blink::WebAXEventChildrenChanged;
436 msg->nodes.push_back(tree2_container);
437 msg->nodes.push_back(tree2_child0);
438 msg->nodes.push_back(tree2_grandchild0);
439 msg->id = tree2_container.id;
440 manager->OnAccessibilityEvents(params);
441
442 // There should be 9 objects now: the 8 from the new tree, plus the
443 // reference to child3 we kept.
444 EXPECT_EQ(9, CountedBrowserAccessibility::global_obj_count_);
445
446 // Check that our references to the root and container and child2 are
447 // still valid, but that the reference to child3 is now invalid.
448 EXPECT_TRUE(root_accessible->instance_active());
449 EXPECT_TRUE(container_accessible->instance_active());
450 EXPECT_TRUE(child2_accessible->instance_active());
451 EXPECT_FALSE(child3_accessible->instance_active());
452
453 // Ensure that we retain the parent of the detached subtree.
454 EXPECT_EQ(root_accessible, container_accessible->parent());
455 EXPECT_EQ(0, container_accessible->index_in_parent());
456
457 // Check that the index in parent has been updated.
458 EXPECT_EQ(2, child2_accessible->index_in_parent());
459
460 // Release our references. The object count should only decrease by 1
461 // for child3.
462 root_accessible->NativeReleaseReference();
463 container_accessible->NativeReleaseReference();
464 child2_accessible->NativeReleaseReference();
465 child3_accessible->NativeReleaseReference();
466
467 EXPECT_EQ(8, CountedBrowserAccessibility::global_obj_count_);
468
469 // Delete the manager and make sure all memory is cleaned up.
470 delete manager;
471 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
472 }
473
TEST(BrowserAccessibilityManagerTest,TestMoveChildUp)474 TEST(BrowserAccessibilityManagerTest, TestMoveChildUp) {
475 // Tree 1:
476 //
477 // 1
478 // 2
479 // 3
480 // 4
481
482 AccessibilityNodeData tree1_4;
483 tree1_4.id = 4;
484 tree1_4.state = 0;
485
486 AccessibilityNodeData tree1_3;
487 tree1_3.id = 3;
488 tree1_3.state = 0;
489 tree1_3.child_ids.push_back(4);
490
491 AccessibilityNodeData tree1_2;
492 tree1_2.id = 2;
493 tree1_2.state = 0;
494
495 AccessibilityNodeData tree1_1;
496 tree1_1.id = 1;
497 tree1_1.role = blink::WebAXRoleRootWebArea;
498 tree1_1.state = 0;
499 tree1_1.child_ids.push_back(2);
500 tree1_1.child_ids.push_back(3);
501
502 // Tree 2:
503 //
504 // 1
505 // 4 <-- moves up a level and gains child
506 // 6 <-- new
507 // 5 <-- new
508
509 AccessibilityNodeData tree2_6;
510 tree2_6.id = 6;
511 tree2_6.state = 0;
512
513 AccessibilityNodeData tree2_5;
514 tree2_5.id = 5;
515 tree2_5.state = 0;
516
517 AccessibilityNodeData tree2_4;
518 tree2_4.id = 4;
519 tree2_4.state = 0;
520 tree2_4.child_ids.push_back(6);
521
522 AccessibilityNodeData tree2_1;
523 tree2_1.id = 1;
524 tree2_1.state = 0;
525 tree2_1.child_ids.push_back(4);
526 tree2_1.child_ids.push_back(5);
527
528 // Construct a BrowserAccessibilityManager with tree1.
529 CountedBrowserAccessibility::global_obj_count_ = 0;
530 BrowserAccessibilityManager* manager =
531 BrowserAccessibilityManager::Create(
532 tree1_1,
533 NULL,
534 new CountedBrowserAccessibilityFactory());
535 manager->UpdateNodesForTesting(tree1_2, tree1_3, tree1_4);
536 ASSERT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
537
538 // Process a notification containing the changed subtree.
539 std::vector<AccessibilityHostMsg_EventParams> params;
540 params.push_back(AccessibilityHostMsg_EventParams());
541 AccessibilityHostMsg_EventParams* msg = ¶ms[0];
542 msg->event_type = blink::WebAXEventChildrenChanged;
543 msg->nodes.push_back(tree2_1);
544 msg->nodes.push_back(tree2_4);
545 msg->nodes.push_back(tree2_5);
546 msg->nodes.push_back(tree2_6);
547 msg->id = tree2_1.id;
548 manager->OnAccessibilityEvents(params);
549
550 // There should be 4 objects now.
551 EXPECT_EQ(4, CountedBrowserAccessibility::global_obj_count_);
552
553 // Delete the manager and make sure all memory is cleaned up.
554 delete manager;
555 ASSERT_EQ(0, CountedBrowserAccessibility::global_obj_count_);
556 }
557
558 // Crashes on Windows. http://crbug.com/304130
559 #if defined(OS_WIN)
560 #define MAYBE_TestFatalError DISABLED_TestFatalError
561 #else
562 #define MAYBE_TestFatalError TestFatalError
563 #endif
TEST(BrowserAccessibilityManagerTest,TestFatalError)564 TEST(BrowserAccessibilityManagerTest, TestFatalError) {
565 // Test that BrowserAccessibilityManager raises a fatal error
566 // (which will crash the renderer) if the same id is used in
567 // two places in the tree.
568
569 AccessibilityNodeData root;
570 root.id = 1;
571 root.role = blink::WebAXRoleRootWebArea;
572 root.child_ids.push_back(2);
573 root.child_ids.push_back(2);
574
575 CountedBrowserAccessibilityFactory* factory =
576 new CountedBrowserAccessibilityFactory();
577 scoped_ptr<TestBrowserAccessibilityDelegate> delegate(
578 new TestBrowserAccessibilityDelegate());
579 scoped_ptr<BrowserAccessibilityManager> manager;
580 ASSERT_FALSE(delegate->got_fatal_error());
581 manager.reset(BrowserAccessibilityManager::Create(
582 root,
583 delegate.get(),
584 factory));
585 ASSERT_TRUE(delegate->got_fatal_error());
586
587 AccessibilityNodeData root2;
588 root2.id = 1;
589 root2.role = blink::WebAXRoleRootWebArea;
590 root2.child_ids.push_back(2);
591 root2.child_ids.push_back(3);
592
593 AccessibilityNodeData child1;
594 child1.id = 2;
595 child1.child_ids.push_back(4);
596 child1.child_ids.push_back(5);
597
598 AccessibilityNodeData child2;
599 child2.id = 3;
600 child2.child_ids.push_back(6);
601 child2.child_ids.push_back(5); // Duplicate
602
603 delegate->reset_got_fatal_error();
604 factory = new CountedBrowserAccessibilityFactory();
605 manager.reset(BrowserAccessibilityManager::Create(
606 root2,
607 delegate.get(),
608 factory));
609 ASSERT_FALSE(delegate->got_fatal_error());
610 manager->UpdateNodesForTesting(child1, child2);
611 ASSERT_TRUE(delegate->got_fatal_error());
612 }
613
TEST(BrowserAccessibilityManagerTest,BoundsForRange)614 TEST(BrowserAccessibilityManagerTest, BoundsForRange) {
615 AccessibilityNodeData root;
616 root.id = 1;
617 root.role = blink::WebAXRoleRootWebArea;
618
619 AccessibilityNodeData static_text;
620 static_text.id = 2;
621 static_text.SetValue("Hello, world.");
622 static_text.role = blink::WebAXRoleStaticText;
623 static_text.location = gfx::Rect(100, 100, 29, 18);
624 root.child_ids.push_back(2);
625
626 AccessibilityNodeData inline_text1;
627 inline_text1.id = 3;
628 inline_text1.SetValue("Hello, ");
629 inline_text1.role = blink::WebAXRoleInlineTextBox;
630 inline_text1.location = gfx::Rect(100, 100, 29, 9);
631 inline_text1.AddIntAttribute(AccessibilityNodeData::ATTR_TEXT_DIRECTION,
632 blink::WebAXTextDirectionLR);
633 std::vector<int32> character_offsets1;
634 character_offsets1.push_back(6); // 0
635 character_offsets1.push_back(11); // 1
636 character_offsets1.push_back(16); // 2
637 character_offsets1.push_back(21); // 3
638 character_offsets1.push_back(26); // 4
639 character_offsets1.push_back(29); // 5
640 character_offsets1.push_back(29); // 6 (note that the space has no width)
641 inline_text1.AddIntListAttribute(
642 AccessibilityNodeData::ATTR_CHARACTER_OFFSETS, character_offsets1);
643 static_text.child_ids.push_back(3);
644
645 AccessibilityNodeData inline_text2;
646 inline_text2.id = 4;
647 inline_text2.SetValue("world.");
648 inline_text2.role = blink::WebAXRoleInlineTextBox;
649 inline_text2.location = gfx::Rect(100, 109, 28, 9);
650 inline_text2.AddIntAttribute(AccessibilityNodeData::ATTR_TEXT_DIRECTION,
651 blink::WebAXTextDirectionLR);
652 std::vector<int32> character_offsets2;
653 character_offsets2.push_back(5);
654 character_offsets2.push_back(10);
655 character_offsets2.push_back(15);
656 character_offsets2.push_back(20);
657 character_offsets2.push_back(25);
658 character_offsets2.push_back(28);
659 inline_text2.AddIntListAttribute(
660 AccessibilityNodeData::ATTR_CHARACTER_OFFSETS, character_offsets2);
661 static_text.child_ids.push_back(4);
662
663 scoped_ptr<BrowserAccessibilityManager> manager(
664 BrowserAccessibilityManager::Create(
665 root,
666 NULL,
667 new CountedBrowserAccessibilityFactory()));
668 manager->UpdateNodesForTesting(static_text, inline_text1, inline_text2);
669
670 BrowserAccessibility* root_accessible = manager->GetRoot();
671 BrowserAccessibility* static_text_accessible =
672 root_accessible->PlatformGetChild(0);
673
674 EXPECT_EQ(gfx::Rect(100, 100, 6, 9).ToString(),
675 static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
676
677 EXPECT_EQ(gfx::Rect(100, 100, 26, 9).ToString(),
678 static_text_accessible->GetLocalBoundsForRange(0, 5).ToString());
679
680 EXPECT_EQ(gfx::Rect(100, 109, 5, 9).ToString(),
681 static_text_accessible->GetLocalBoundsForRange(7, 1).ToString());
682
683 EXPECT_EQ(gfx::Rect(100, 109, 25, 9).ToString(),
684 static_text_accessible->GetLocalBoundsForRange(7, 5).ToString());
685
686 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
687 static_text_accessible->GetLocalBoundsForRange(5, 3).ToString());
688
689 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
690 static_text_accessible->GetLocalBoundsForRange(0, 13).ToString());
691
692 // Test range that's beyond the text.
693 EXPECT_EQ(gfx::Rect(100, 100, 29, 18).ToString(),
694 static_text_accessible->GetLocalBoundsForRange(-1, 999).ToString());
695 }
696
TEST(BrowserAccessibilityManagerTest,BoundsForRangeBiDi)697 TEST(BrowserAccessibilityManagerTest, BoundsForRangeBiDi) {
698 // In this example, we assume that the string "123abc" is rendered with
699 // "123" going left-to-right and "abc" going right-to-left. In other
700 // words, on-screen it would look like "123cba". This is possible to
701 // acheive if the source string had unicode control characters
702 // to switch directions. This test doesn't worry about how, though - it just
703 // tests that if something like that were to occur, GetLocalBoundsForRange
704 // returns the correct bounds for different ranges.
705
706 AccessibilityNodeData root;
707 root.id = 1;
708 root.role = blink::WebAXRoleRootWebArea;
709
710 AccessibilityNodeData static_text;
711 static_text.id = 2;
712 static_text.SetValue("123abc");
713 static_text.role = blink::WebAXRoleStaticText;
714 static_text.location = gfx::Rect(100, 100, 60, 20);
715 root.child_ids.push_back(2);
716
717 AccessibilityNodeData inline_text1;
718 inline_text1.id = 3;
719 inline_text1.SetValue("123");
720 inline_text1.role = blink::WebAXRoleInlineTextBox;
721 inline_text1.location = gfx::Rect(100, 100, 30, 20);
722 inline_text1.AddIntAttribute(AccessibilityNodeData::ATTR_TEXT_DIRECTION,
723 blink::WebAXTextDirectionLR);
724 std::vector<int32> character_offsets1;
725 character_offsets1.push_back(10); // 0
726 character_offsets1.push_back(20); // 1
727 character_offsets1.push_back(30); // 2
728 inline_text1.AddIntListAttribute(
729 AccessibilityNodeData::ATTR_CHARACTER_OFFSETS, character_offsets1);
730 static_text.child_ids.push_back(3);
731
732 AccessibilityNodeData inline_text2;
733 inline_text2.id = 4;
734 inline_text2.SetValue("abc");
735 inline_text2.role = blink::WebAXRoleInlineTextBox;
736 inline_text2.location = gfx::Rect(130, 100, 30, 20);
737 inline_text2.AddIntAttribute(AccessibilityNodeData::ATTR_TEXT_DIRECTION,
738 blink::WebAXTextDirectionRL);
739 std::vector<int32> character_offsets2;
740 character_offsets2.push_back(10);
741 character_offsets2.push_back(20);
742 character_offsets2.push_back(30);
743 inline_text2.AddIntListAttribute(
744 AccessibilityNodeData::ATTR_CHARACTER_OFFSETS, character_offsets2);
745 static_text.child_ids.push_back(4);
746
747 scoped_ptr<BrowserAccessibilityManager> manager(
748 BrowserAccessibilityManager::Create(
749 root,
750 NULL,
751 new CountedBrowserAccessibilityFactory()));
752 manager->UpdateNodesForTesting(static_text, inline_text1, inline_text2);
753
754 BrowserAccessibility* root_accessible = manager->GetRoot();
755 BrowserAccessibility* static_text_accessible =
756 root_accessible->PlatformGetChild(0);
757
758 EXPECT_EQ(gfx::Rect(100, 100, 60, 20).ToString(),
759 static_text_accessible->GetLocalBoundsForRange(0, 6).ToString());
760
761 EXPECT_EQ(gfx::Rect(100, 100, 10, 20).ToString(),
762 static_text_accessible->GetLocalBoundsForRange(0, 1).ToString());
763
764 EXPECT_EQ(gfx::Rect(100, 100, 30, 20).ToString(),
765 static_text_accessible->GetLocalBoundsForRange(0, 3).ToString());
766
767 EXPECT_EQ(gfx::Rect(150, 100, 10, 20).ToString(),
768 static_text_accessible->GetLocalBoundsForRange(3, 1).ToString());
769
770 EXPECT_EQ(gfx::Rect(130, 100, 30, 20).ToString(),
771 static_text_accessible->GetLocalBoundsForRange(3, 3).ToString());
772
773 // This range is only two characters, but because of the direction switch
774 // the bounds are as wide as four characters.
775 EXPECT_EQ(gfx::Rect(120, 100, 40, 20).ToString(),
776 static_text_accessible->GetLocalBoundsForRange(2, 2).ToString());
777 }
778
779 } // namespace content
780