OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "chrome/browser/ui/cocoa/base_bubble_controller.h" | 5 #import "chrome/browser/ui/cocoa/base_bubble_controller.h" |
6 | 6 |
7 #include "base/mac/mac_util.h" | 7 #include "base/mac/mac_util.h" |
8 #import "base/mac/scoped_nsobject.h" | 8 #import "base/mac/scoped_nsobject.h" |
9 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" | 9 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" |
10 #import "chrome/browser/ui/cocoa/info_bubble_view.h" | 10 #import "chrome/browser/ui/cocoa/info_bubble_view.h" |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
79 | 79 |
80 - (void)menuDidClose:(NSMenu*)menu { | 80 - (void)menuDidClose:(NSMenu*)menu { |
81 isMenuOpen_ = NO; | 81 isMenuOpen_ = NO; |
82 didOpen_ = YES; | 82 didOpen_ = YES; |
83 } | 83 } |
84 | 84 |
85 @end | 85 @end |
86 | 86 |
87 class BaseBubbleControllerTest : public CocoaTest { | 87 class BaseBubbleControllerTest : public CocoaTest { |
88 public: | 88 public: |
| 89 BaseBubbleControllerTest() : controller_(nil) {} |
| 90 |
89 virtual void SetUp() OVERRIDE { | 91 virtual void SetUp() OVERRIDE { |
90 bubbleWindow_.reset([[InfoBubbleWindow alloc] | 92 bubble_window_.reset([[InfoBubbleWindow alloc] |
91 initWithContentRect:NSMakeRect(0, 0, kBubbleWindowWidth, | 93 initWithContentRect:NSMakeRect(0, 0, kBubbleWindowWidth, |
92 kBubbleWindowHeight) | 94 kBubbleWindowHeight) |
93 styleMask:NSBorderlessWindowMask | 95 styleMask:NSBorderlessWindowMask |
94 backing:NSBackingStoreBuffered | 96 backing:NSBackingStoreBuffered |
95 defer:YES]); | 97 defer:YES]); |
96 [bubbleWindow_ setAllowedAnimations:0]; | 98 [bubble_window_ setAllowedAnimations:0]; |
97 | 99 |
98 // The bubble controller will release itself when the window closes. | 100 // The bubble controller will release itself when the window closes. |
99 controller_ = [[BaseBubbleController alloc] | 101 controller_ = [[BaseBubbleController alloc] |
100 initWithWindow:bubbleWindow_.get() | 102 initWithWindow:bubble_window_ |
101 parentWindow:test_window() | 103 parentWindow:test_window() |
102 anchoredAt:NSMakePoint(kAnchorPointX, kAnchorPointY)]; | 104 anchoredAt:NSMakePoint(kAnchorPointX, kAnchorPointY)]; |
103 EXPECT_TRUE([controller_ bubble]); | 105 EXPECT_TRUE([controller_ bubble]); |
| 106 EXPECT_EQ(bubble_window_.get(), [controller_ window]); |
104 } | 107 } |
105 | 108 |
106 virtual void TearDown() OVERRIDE { | 109 virtual void TearDown() OVERRIDE { |
107 // Close our windows. | 110 // Close our windows. |
108 [controller_ close]; | 111 [controller_ close]; |
109 bubbleWindow_.reset(NULL); | 112 bubble_window_.reset(); |
110 CocoaTest::TearDown(); | 113 CocoaTest::TearDown(); |
111 } | 114 } |
112 | 115 |
113 public: | 116 // Closing the bubble will autorelease the controller. Give callers a keep- |
114 base::scoped_nsobject<InfoBubbleWindow> bubbleWindow_; | 117 // alive to run checks after closing. |
| 118 base::scoped_nsobject<BaseBubbleController> ShowBubble() WARN_UNUSED_RESULT { |
| 119 base::scoped_nsobject<BaseBubbleController> keep_alive( |
| 120 [controller_ retain]); |
| 121 EXPECT_FALSE([bubble_window_ isVisible]); |
| 122 [controller_ showWindow:nil]; |
| 123 EXPECT_TRUE([bubble_window_ isVisible]); |
| 124 return keep_alive; |
| 125 } |
| 126 |
| 127 // Fake the key state notification. Because unit_tests is a "daemon" process |
| 128 // type, its windows can never become key (nor can the app become active). |
| 129 // Instead of the hacks below, one could make a browser_test or transform the |
| 130 // process type, but this seems easiest and is best suited to a unit test. |
| 131 // |
| 132 // On Lion and above, which have the event taps, simply post a notification |
| 133 // that will cause the controller to call |-windowDidResignKey:|. Earlier |
| 134 // OSes can call through directly. |
| 135 void SimulateKeyStatusChange() { |
| 136 NSNotification* notif = |
| 137 [NSNotification notificationWithName:NSWindowDidResignKeyNotification |
| 138 object:[controller_ window]]; |
| 139 if (base::mac::IsOSLionOrLater()) |
| 140 [[NSNotificationCenter defaultCenter] postNotification:notif]; |
| 141 else |
| 142 [controller_ windowDidResignKey:notif]; |
| 143 } |
| 144 |
| 145 protected: |
| 146 base::scoped_nsobject<InfoBubbleWindow> bubble_window_; |
115 BaseBubbleController* controller_; | 147 BaseBubbleController* controller_; |
| 148 |
| 149 private: |
| 150 DISALLOW_COPY_AND_ASSIGN(BaseBubbleControllerTest); |
116 }; | 151 }; |
117 | 152 |
118 // Test that kAlignEdgeToAnchorEdge and a left bubble arrow correctly aligns the | 153 // Test that kAlignEdgeToAnchorEdge and a left bubble arrow correctly aligns the |
119 // left edge of the buble to the anchor point. | 154 // left edge of the buble to the anchor point. |
120 TEST_F(BaseBubbleControllerTest, LeftAlign) { | 155 TEST_F(BaseBubbleControllerTest, LeftAlign) { |
121 [[controller_ bubble] setArrowLocation:info_bubble::kTopLeft]; | 156 [[controller_ bubble] setArrowLocation:info_bubble::kTopLeft]; |
122 [[controller_ bubble] setAlignment:info_bubble::kAlignEdgeToAnchorEdge]; | 157 [[controller_ bubble] setAlignment:info_bubble::kAlignEdgeToAnchorEdge]; |
123 [controller_ showWindow:nil]; | 158 [controller_ showWindow:nil]; |
124 | 159 |
125 NSRect frame = [[controller_ window] frame]; | 160 NSRect frame = [[controller_ window] frame]; |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
193 EXPECT_EQ(frame.size.width, kBubbleWindowWidth); | 228 EXPECT_EQ(frame.size.width, kBubbleWindowWidth); |
194 EXPECT_EQ(frame.size.height, kBubbleWindowHeight); | 229 EXPECT_EQ(frame.size.height, kBubbleWindowHeight); |
195 // Make sure the bubble arrow points to the anchor. | 230 // Make sure the bubble arrow points to the anchor. |
196 EXPECT_EQ(NSMidX(frame), kAnchorPointX); | 231 EXPECT_EQ(NSMidX(frame), kAnchorPointX); |
197 EXPECT_GE(NSMaxY(frame), kAnchorPointY); | 232 EXPECT_GE(NSMaxY(frame), kAnchorPointY); |
198 } | 233 } |
199 | 234 |
200 // Tests that when a new window gets key state (and the bubble resigns) that | 235 // Tests that when a new window gets key state (and the bubble resigns) that |
201 // the key window changes. | 236 // the key window changes. |
202 TEST_F(BaseBubbleControllerTest, ResignKeyCloses) { | 237 TEST_F(BaseBubbleControllerTest, ResignKeyCloses) { |
203 // Closing the bubble will autorelease the controller. | |
204 base::scoped_nsobject<BaseBubbleController> keep_alive([controller_ retain]); | |
205 | |
206 NSWindow* bubble_window = [controller_ window]; | |
207 EXPECT_FALSE([bubble_window isVisible]); | |
208 | |
209 base::scoped_nsobject<NSWindow> other_window( | 238 base::scoped_nsobject<NSWindow> other_window( |
210 [[NSWindow alloc] initWithContentRect:NSMakeRect(500, 500, 500, 500) | 239 [[NSWindow alloc] initWithContentRect:NSMakeRect(500, 500, 500, 500) |
211 styleMask:NSTitledWindowMask | 240 styleMask:NSTitledWindowMask |
212 backing:NSBackingStoreBuffered | 241 backing:NSBackingStoreBuffered |
213 defer:YES]); | 242 defer:YES]); |
214 EXPECT_FALSE([other_window isVisible]); | |
215 | 243 |
216 [controller_ showWindow:nil]; | 244 base::scoped_nsobject<BaseBubbleController> keep_alive = ShowBubble(); |
217 EXPECT_TRUE([bubble_window isVisible]); | |
218 EXPECT_FALSE([other_window isVisible]); | 245 EXPECT_FALSE([other_window isVisible]); |
219 | 246 |
220 [other_window makeKeyAndOrderFront:nil]; | 247 [other_window makeKeyAndOrderFront:nil]; |
221 // Fake the key state notification. Because unit_tests is a "daemon" process | 248 SimulateKeyStatusChange(); |
222 // type, its windows can never become key (nor can the app become active). | |
223 // Instead of the hacks below, one could make a browser_test or transform the | |
224 // process type, but this seems easiest and is best suited to a unit test. | |
225 // | |
226 // On Lion and above, which have the event taps, simply post a notification | |
227 // that will cause the controller to call |-windowDidResignKey:|. Earlier | |
228 // OSes can call through directly. | |
229 NSNotification* notif = | |
230 [NSNotification notificationWithName:NSWindowDidResignKeyNotification | |
231 object:bubble_window]; | |
232 if (base::mac::IsOSLionOrLater()) | |
233 [[NSNotificationCenter defaultCenter] postNotification:notif]; | |
234 else | |
235 [controller_ windowDidResignKey:notif]; | |
236 | 249 |
237 | 250 EXPECT_FALSE([bubble_window_ isVisible]); |
238 EXPECT_FALSE([bubble_window isVisible]); | |
239 EXPECT_TRUE([other_window isVisible]); | 251 EXPECT_TRUE([other_window isVisible]); |
240 } | 252 } |
241 | 253 |
242 // Test that clicking outside the window causes the bubble to close if | 254 // Test that clicking outside the window causes the bubble to close if |
243 // shouldCloseOnResignKey is YES. | 255 // shouldCloseOnResignKey is YES. |
244 TEST_F(BaseBubbleControllerTest, LionClickOutsideClosesWithoutContextMenu) { | 256 TEST_F(BaseBubbleControllerTest, LionClickOutsideClosesWithoutContextMenu) { |
245 // The event tap is only installed on 10.7+. | 257 // The event tap is only installed on 10.7+. |
246 if (!base::mac::IsOSLionOrLater()) | 258 if (!base::mac::IsOSLionOrLater()) |
247 return; | 259 return; |
248 | 260 |
249 // Closing the bubble will autorelease the controller. | 261 base::scoped_nsobject<BaseBubbleController> keep_alive = ShowBubble(); |
250 base::scoped_nsobject<BaseBubbleController> keep_alive([controller_ retain]); | |
251 NSWindow* window = [controller_ window]; | 262 NSWindow* window = [controller_ window]; |
252 | 263 |
253 EXPECT_TRUE([controller_ shouldCloseOnResignKey]); // Verify default value. | 264 EXPECT_TRUE([controller_ shouldCloseOnResignKey]); // Verify default value. |
254 EXPECT_FALSE([window isVisible]); | |
255 | |
256 [controller_ showWindow:nil]; | |
257 | |
258 EXPECT_TRUE([window isVisible]); | |
259 | |
260 [controller_ setShouldCloseOnResignKey:NO]; | 265 [controller_ setShouldCloseOnResignKey:NO]; |
261 NSEvent* event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow( | 266 NSEvent* event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow( |
262 NSMakePoint(10, 10), test_window()); | 267 NSMakePoint(10, 10), test_window()); |
263 [NSApp sendEvent:event]; | 268 [NSApp sendEvent:event]; |
264 | 269 |
265 EXPECT_TRUE([window isVisible]); | 270 EXPECT_TRUE([window isVisible]); |
266 | 271 |
267 event = cocoa_test_event_utils::RightMouseDownAtPointInWindow( | 272 event = cocoa_test_event_utils::RightMouseDownAtPointInWindow( |
268 NSMakePoint(10, 10), test_window()); | 273 NSMakePoint(10, 10), test_window()); |
269 [NSApp sendEvent:event]; | 274 [NSApp sendEvent:event]; |
(...skipping 18 matching lines...) Expand all Loading... |
288 EXPECT_FALSE([window isVisible]); | 293 EXPECT_FALSE([window isVisible]); |
289 } | 294 } |
290 | 295 |
291 // Test that right-clicking the window with displaying a context menu causes | 296 // Test that right-clicking the window with displaying a context menu causes |
292 // the bubble to close. | 297 // the bubble to close. |
293 TEST_F(BaseBubbleControllerTest, LionRightClickOutsideClosesWithContextMenu) { | 298 TEST_F(BaseBubbleControllerTest, LionRightClickOutsideClosesWithContextMenu) { |
294 // The event tap is only installed on 10.7+. | 299 // The event tap is only installed on 10.7+. |
295 if (!base::mac::IsOSLionOrLater()) | 300 if (!base::mac::IsOSLionOrLater()) |
296 return; | 301 return; |
297 | 302 |
298 // Closing the bubble will autorelease the controller. | 303 base::scoped_nsobject<BaseBubbleController> keep_alive = ShowBubble(); |
299 base::scoped_nsobject<BaseBubbleController> keep_alive([controller_ retain]); | |
300 NSWindow* window = [controller_ window]; | 304 NSWindow* window = [controller_ window]; |
301 | 305 |
302 EXPECT_TRUE([controller_ shouldCloseOnResignKey]); // Verify default value. | |
303 EXPECT_FALSE([window isVisible]); | |
304 | |
305 [controller_ showWindow:nil]; | |
306 | |
307 EXPECT_TRUE([window isVisible]); | |
308 | |
309 base::scoped_nsobject<NSMenu> context_menu( | 306 base::scoped_nsobject<NSMenu> context_menu( |
310 [[NSMenu alloc] initWithTitle:@""]); | 307 [[NSMenu alloc] initWithTitle:@""]); |
311 [context_menu addItemWithTitle:@"ContextMenuTest" | 308 [context_menu addItemWithTitle:@"ContextMenuTest" |
312 action:nil | 309 action:nil |
313 keyEquivalent:@""]; | 310 keyEquivalent:@""]; |
314 base::scoped_nsobject<ContextMenuController> menu_controller( | 311 base::scoped_nsobject<ContextMenuController> menu_controller( |
315 [[ContextMenuController alloc] initWithMenu:context_menu | 312 [[ContextMenuController alloc] initWithMenu:context_menu |
316 andWindow:window]); | 313 andWindow:window]); |
317 | 314 |
318 // Set the menu as the contextual menu of contentView of test_window(). | 315 // Set the menu as the contextual menu of contentView of test_window(). |
(...skipping 15 matching lines...) Expand all Loading... |
334 [NSApp sendEvent:event]; | 331 [NSApp sendEvent:event]; |
335 | 332 |
336 // When we got here, menu has already run its RunLoop. | 333 // When we got here, menu has already run its RunLoop. |
337 // See -[ContextualMenuController menuWillOpen:]. | 334 // See -[ContextualMenuController menuWillOpen:]. |
338 EXPECT_FALSE([window isVisible]); | 335 EXPECT_FALSE([window isVisible]); |
339 | 336 |
340 EXPECT_FALSE([menu_controller isMenuOpen]); | 337 EXPECT_FALSE([menu_controller isMenuOpen]); |
341 EXPECT_TRUE([menu_controller didOpen]); | 338 EXPECT_TRUE([menu_controller didOpen]); |
342 } | 339 } |
343 | 340 |
| 341 // Test that the bubble is not dismissed when it has an attached sheet, or when |
| 342 // a sheet loses key status (since the sheet is not attached when that happens). |
| 343 TEST_F(BaseBubbleControllerTest, BubbleStaysOpenWithSheet) { |
| 344 base::scoped_nsobject<BaseBubbleController> keep_alive = ShowBubble(); |
| 345 |
| 346 // Make a dummy NSPanel for the sheet. Don't use [NSOpenPanel openPanel], |
| 347 // otherwise a stray FI_TFloatingInputWindow is created which the unit test |
| 348 // harness doesn't like. |
| 349 base::scoped_nsobject<NSPanel> panel( |
| 350 [[NSPanel alloc] initWithContentRect:NSMakeRect(0, 0, 100, 50) |
| 351 styleMask:NSTitledWindowMask |
| 352 backing:NSBackingStoreBuffered |
| 353 defer:YES]); |
| 354 EXPECT_FALSE([panel isReleasedWhenClosed]); // scoped_nsobject releases it. |
| 355 |
| 356 // With a NSOpenPanel, we would call -[NSSavePanel beginSheetModalForWindow] |
| 357 // here. In 10.9, we would call [NSWindow beginSheet:]. For 10.6, this: |
| 358 [[NSApplication sharedApplication] beginSheet:panel |
| 359 modalForWindow:bubble_window_ |
| 360 modalDelegate:nil |
| 361 didEndSelector:NULL |
| 362 contextInfo:NULL]; |
| 363 |
| 364 EXPECT_TRUE([bubble_window_ isVisible]); |
| 365 EXPECT_TRUE([panel isVisible]); |
| 366 // Losing key status while there is an attached window should not close the |
| 367 // bubble. |
| 368 SimulateKeyStatusChange(); |
| 369 EXPECT_TRUE([bubble_window_ isVisible]); |
| 370 EXPECT_TRUE([panel isVisible]); |
| 371 |
| 372 // Closing the attached sheet should not close the bubble. |
| 373 [[NSApplication sharedApplication] endSheet:panel]; |
| 374 [panel close]; |
| 375 |
| 376 EXPECT_FALSE([bubble_window_ attachedSheet]); |
| 377 EXPECT_TRUE([bubble_window_ isVisible]); |
| 378 EXPECT_FALSE([panel isVisible]); |
| 379 |
| 380 // Now that the sheet is gone, a key status change should close the bubble. |
| 381 SimulateKeyStatusChange(); |
| 382 EXPECT_FALSE([bubble_window_ isVisible]); |
| 383 } |
OLD | NEW |