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 #import <Cocoa/Cocoa.h> 6 7 #include "base/memory/scoped_ptr.h" 8 #include "base/strings/string_util.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "content/browser/accessibility/browser_accessibility_cocoa.h" 11 #include "content/browser/accessibility/browser_accessibility_manager.h" 12 #include "content/browser/accessibility/browser_accessibility_manager_mac.h" 13 #include "testing/gtest/include/gtest/gtest.h" 14 #import "testing/gtest_mac.h" 15 #import "ui/base/test/ui_cocoa_test_helper.h" 16 17 @interface MockAccessibilityDelegate : 18 NSView<BrowserAccessibilityDelegateCocoa> 19 20 - (NSPoint)accessibilityPointInScreen:(NSPoint)origin 21 size:(NSSize)size; 22 - (void)doDefaultAction:(int32)accessibilityObjectId; 23 - (void)accessibilitySetTextSelection:(int32)accId 24 startOffset:(int32)startOffset 25 endOffset:(int32)endOffset; 26 - (void)performShowMenuAction:(BrowserAccessibilityCocoa*)accessibility; 27 - (void)setAccessibilityFocus:(BOOL)focus 28 accessibilityId:(int32)accessibilityObjectId; 29 - (NSWindow*)window; 30 31 @end 32 33 @implementation MockAccessibilityDelegate 34 35 - (NSPoint)accessibilityPointInScreen:(NSPoint)origin 36 size:(NSSize)size { 37 return NSZeroPoint; 38 } 39 - (void)doDefaultAction:(int32)accessibilityObjectId { 40 } 41 - (void)accessibilitySetTextSelection:(int32)accId 42 startOffset:(int32)startOffset 43 endOffset:(int32)endOffset { 44 } 45 - (void)performShowMenuAction:(BrowserAccessibilityCocoa*)accessibility { 46 } 47 - (void)setAccessibilityFocus:(BOOL)focus 48 accessibilityId:(int32)accessibilityObjectId { 49 } 50 - (NSWindow*)window { 51 return nil; 52 } 53 54 @end 55 56 namespace content { 57 58 class BrowserAccessibilityTest : public ui::CocoaTest { 59 public: 60 virtual void SetUp() { 61 CocoaTest::SetUp(); 62 RebuildAccessibilityTree(); 63 } 64 65 protected: 66 void RebuildAccessibilityTree() { 67 AccessibilityNodeData root; 68 root.id = 1000; 69 root.location.set_width(500); 70 root.location.set_height(100); 71 root.role = blink::WebAXRoleRootWebArea; 72 root.AddStringAttribute(AccessibilityNodeData::ATTR_HELP, "HelpText"); 73 root.child_ids.push_back(1001); 74 root.child_ids.push_back(1002); 75 76 AccessibilityNodeData child1; 77 child1.id = 1001; 78 child1.SetName("Child1"); 79 child1.location.set_width(250); 80 child1.location.set_height(100); 81 child1.role = blink::WebAXRoleButton; 82 83 AccessibilityNodeData child2; 84 child2.id = 1002; 85 child2.location.set_x(250); 86 child2.location.set_width(250); 87 child2.location.set_height(100); 88 child2.role = blink::WebAXRoleHeading; 89 90 delegate_.reset([[MockAccessibilityDelegate alloc] init]); 91 manager_.reset( 92 new BrowserAccessibilityManagerMac(delegate_, root, NULL)); 93 manager_->UpdateNodesForTesting(child1, child2); 94 accessibility_.reset([manager_->GetRoot()->ToBrowserAccessibilityCocoa() 95 retain]); 96 } 97 98 base::scoped_nsobject<MockAccessibilityDelegate> delegate_; 99 base::scoped_nsobject<BrowserAccessibilityCocoa> accessibility_; 100 scoped_ptr<BrowserAccessibilityManager> manager_; 101 }; 102 103 // Standard hit test. 104 TEST_F(BrowserAccessibilityTest, HitTestTest) { 105 BrowserAccessibilityCocoa* firstChild = 106 [accessibility_ accessibilityHitTest:NSMakePoint(50, 50)]; 107 EXPECT_NSEQ(@"Child1", 108 [firstChild accessibilityAttributeValue:NSAccessibilityTitleAttribute]); 109 } 110 111 // Test doing a hit test on the edge of a child. 112 TEST_F(BrowserAccessibilityTest, EdgeHitTest) { 113 BrowserAccessibilityCocoa* firstChild = 114 [accessibility_ accessibilityHitTest:NSZeroPoint]; 115 EXPECT_NSEQ(@"Child1", 116 [firstChild accessibilityAttributeValue:NSAccessibilityTitleAttribute]); 117 } 118 119 // This will test a hit test with invalid coordinates. It is assumed that 120 // the hit test has been narrowed down to this object or one of its children 121 // so it should return itself since it has no better hit result. 122 TEST_F(BrowserAccessibilityTest, InvalidHitTestCoordsTest) { 123 BrowserAccessibilityCocoa* hitTestResult = 124 [accessibility_ accessibilityHitTest:NSMakePoint(-50, 50)]; 125 EXPECT_NSEQ(accessibility_, hitTestResult); 126 } 127 128 // Test to ensure querying standard attributes works. 129 TEST_F(BrowserAccessibilityTest, BasicAttributeTest) { 130 NSString* helpText = [accessibility_ 131 accessibilityAttributeValue:NSAccessibilityHelpAttribute]; 132 EXPECT_NSEQ(@"HelpText", helpText); 133 } 134 135 // Test querying for an invalid attribute to ensure it doesn't crash. 136 TEST_F(BrowserAccessibilityTest, InvalidAttributeTest) { 137 NSString* shouldBeNil = [accessibility_ 138 accessibilityAttributeValue:@"NSAnInvalidAttribute"]; 139 EXPECT_TRUE(shouldBeNil == nil); 140 } 141 142 TEST_F(BrowserAccessibilityTest, RetainedDetachedObjectsReturnNil) { 143 // Get the first child. 144 BrowserAccessibilityCocoa* retainedFirstChild = 145 [accessibility_ accessibilityHitTest:NSMakePoint(50, 50)]; 146 EXPECT_NSEQ(@"Child1", [retainedFirstChild 147 accessibilityAttributeValue:NSAccessibilityTitleAttribute]); 148 149 // Retain it. This simulates what the system might do with an 150 // accessibility object. 151 [retainedFirstChild retain]; 152 153 // Rebuild the accessibility tree, which should detach |retainedFirstChild|. 154 RebuildAccessibilityTree(); 155 156 // Now any attributes we query should return nil. 157 EXPECT_EQ(nil, [retainedFirstChild 158 accessibilityAttributeValue:NSAccessibilityTitleAttribute]); 159 160 // Don't leak memory in the test. 161 [retainedFirstChild release]; 162 } 163 164 } // namespace content 165