Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / base_bubble_controller_unittest.mm
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 "chrome/browser/ui/cocoa/base_bubble_controller.h"
6
7 #include "base/mac/mac_util.h"
8 #import "base/mac/scoped_nsobject.h"
9 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
10 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
11 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
12 #import "ui/events/test/cocoa_test_event_utils.h"
13
14 namespace {
15 const CGFloat kBubbleWindowWidth = 100;
16 const CGFloat kBubbleWindowHeight = 50;
17 const CGFloat kAnchorPointX = 400;
18 const CGFloat kAnchorPointY = 300;
19 }  // namespace
20
21 @interface ContextMenuController : NSObject<NSMenuDelegate> {
22  @private
23   NSMenu* menu_;
24   NSWindow* window_;
25   BOOL isMenuOpen_;
26   BOOL didOpen_;
27 }
28
29 - (id)initWithMenu:(NSMenu*)menu andWindow:(NSWindow*)window;
30
31 - (BOOL)isMenuOpen;
32 - (BOOL)didOpen;
33 - (BOOL)isWindowVisible;
34
35 // NSMenuDelegate methods
36 - (void)menuWillOpen:(NSMenu*)menu;
37 - (void)menuDidClose:(NSMenu*)menu;
38
39 @end
40
41 @implementation ContextMenuController
42
43 - (id)initWithMenu:(NSMenu*)menu andWindow:(NSWindow*)window {
44   if (self = [super init]) {
45     menu_ = menu;
46     window_ = window;
47     isMenuOpen_ = NO;
48     didOpen_ = NO;
49     [menu_ setDelegate:self];
50   }
51   return self;
52 }
53
54 - (BOOL)isMenuOpen {
55   return isMenuOpen_;
56 }
57
58 - (BOOL)didOpen {
59   return didOpen_;
60 }
61
62 - (BOOL)isWindowVisible {
63   if (window_) {
64     return [window_ isVisible];
65   }
66   return NO;
67 }
68
69 - (void)menuWillOpen:(NSMenu*)menu {
70   isMenuOpen_ = YES;
71   didOpen_ = NO;
72
73   NSArray* modes = @[NSEventTrackingRunLoopMode, NSDefaultRunLoopMode];
74   [menu_ performSelector:@selector(cancelTracking)
75               withObject:nil
76               afterDelay:0.1
77                  inModes:modes];
78 }
79
80 - (void)menuDidClose:(NSMenu*)menu {
81   isMenuOpen_ = NO;
82   didOpen_ = YES;
83 }
84
85 @end
86
87 class BaseBubbleControllerTest : public CocoaTest {
88  public:
89   virtual void SetUp() OVERRIDE {
90     bubbleWindow_.reset([[InfoBubbleWindow alloc]
91         initWithContentRect:NSMakeRect(0, 0, kBubbleWindowWidth,
92                                        kBubbleWindowHeight)
93                   styleMask:NSBorderlessWindowMask
94                     backing:NSBackingStoreBuffered
95                       defer:YES]);
96     [bubbleWindow_ setAllowedAnimations:0];
97
98     // The bubble controller will release itself when the window closes.
99     controller_ = [[BaseBubbleController alloc]
100         initWithWindow:bubbleWindow_.get()
101           parentWindow:test_window()
102             anchoredAt:NSMakePoint(kAnchorPointX, kAnchorPointY)];
103     EXPECT_TRUE([controller_ bubble]);
104   }
105
106   virtual void TearDown() OVERRIDE {
107     // Close our windows.
108     [controller_ close];
109     bubbleWindow_.reset(NULL);
110     CocoaTest::TearDown();
111   }
112
113  public:
114   base::scoped_nsobject<InfoBubbleWindow> bubbleWindow_;
115   BaseBubbleController* controller_;
116 };
117
118 // Test that kAlignEdgeToAnchorEdge and a left bubble arrow correctly aligns the
119 // left edge of the buble to the anchor point.
120 TEST_F(BaseBubbleControllerTest, LeftAlign) {
121   [[controller_ bubble] setArrowLocation:info_bubble::kTopLeft];
122   [[controller_ bubble] setAlignment:info_bubble::kAlignEdgeToAnchorEdge];
123   [controller_ showWindow:nil];
124
125   NSRect frame = [[controller_ window] frame];
126   // Make sure the bubble size hasn't changed.
127   EXPECT_EQ(frame.size.width, kBubbleWindowWidth);
128   EXPECT_EQ(frame.size.height, kBubbleWindowHeight);
129   // Make sure the bubble is left aligned.
130   EXPECT_EQ(NSMinX(frame), kAnchorPointX);
131   EXPECT_GE(NSMaxY(frame), kAnchorPointY);
132 }
133
134 // Test that kAlignEdgeToAnchorEdge and a right bubble arrow correctly aligns
135 // the right edge of the buble to the anchor point.
136 TEST_F(BaseBubbleControllerTest, RightAlign) {
137   [[controller_ bubble] setArrowLocation:info_bubble::kTopRight];
138   [[controller_ bubble] setAlignment:info_bubble::kAlignEdgeToAnchorEdge];
139   [controller_ showWindow:nil];
140
141   NSRect frame = [[controller_ window] frame];
142   // Make sure the bubble size hasn't changed.
143   EXPECT_EQ(frame.size.width, kBubbleWindowWidth);
144   EXPECT_EQ(frame.size.height, kBubbleWindowHeight);
145   // Make sure the bubble is left aligned.
146   EXPECT_EQ(NSMaxX(frame), kAnchorPointX);
147   EXPECT_GE(NSMaxY(frame), kAnchorPointY);
148 }
149
150 // Test that kAlignArrowToAnchor and a left bubble arrow correctly aligns
151 // the bubble arrow to the anchor point.
152 TEST_F(BaseBubbleControllerTest, AnchorAlignLeftArrow) {
153   [[controller_ bubble] setArrowLocation:info_bubble::kTopLeft];
154   [[controller_ bubble] setAlignment:info_bubble::kAlignArrowToAnchor];
155   [controller_ showWindow:nil];
156
157   NSRect frame = [[controller_ window] frame];
158   // Make sure the bubble size hasn't changed.
159   EXPECT_EQ(frame.size.width, kBubbleWindowWidth);
160   EXPECT_EQ(frame.size.height, kBubbleWindowHeight);
161   // Make sure the bubble arrow points to the anchor.
162   EXPECT_EQ(NSMinX(frame) + info_bubble::kBubbleArrowXOffset +
163       roundf(info_bubble::kBubbleArrowWidth / 2.0), kAnchorPointX);
164   EXPECT_GE(NSMaxY(frame), kAnchorPointY);
165 }
166
167 // Test that kAlignArrowToAnchor and a right bubble arrow correctly aligns
168 // the bubble arrow to the anchor point.
169 TEST_F(BaseBubbleControllerTest, AnchorAlignRightArrow) {
170   [[controller_ bubble] setArrowLocation:info_bubble::kTopRight];
171   [[controller_ bubble] setAlignment:info_bubble::kAlignArrowToAnchor];
172   [controller_ showWindow:nil];
173
174   NSRect frame = [[controller_ window] frame];
175   // Make sure the bubble size hasn't changed.
176   EXPECT_EQ(frame.size.width, kBubbleWindowWidth);
177   EXPECT_EQ(frame.size.height, kBubbleWindowHeight);
178   // Make sure the bubble arrow points to the anchor.
179   EXPECT_EQ(NSMaxX(frame) - info_bubble::kBubbleArrowXOffset -
180       floorf(info_bubble::kBubbleArrowWidth / 2.0), kAnchorPointX);
181   EXPECT_GE(NSMaxY(frame), kAnchorPointY);
182 }
183
184 // Test that kAlignArrowToAnchor and a center bubble arrow correctly align
185 // the bubble towards the anchor point.
186 TEST_F(BaseBubbleControllerTest, AnchorAlignCenterArrow) {
187   [[controller_ bubble] setArrowLocation:info_bubble::kTopCenter];
188   [[controller_ bubble] setAlignment:info_bubble::kAlignArrowToAnchor];
189   [controller_ showWindow:nil];
190
191   NSRect frame = [[controller_ window] frame];
192   // Make sure the bubble size hasn't changed.
193   EXPECT_EQ(frame.size.width, kBubbleWindowWidth);
194   EXPECT_EQ(frame.size.height, kBubbleWindowHeight);
195   // Make sure the bubble arrow points to the anchor.
196   EXPECT_EQ(NSMidX(frame), kAnchorPointX);
197   EXPECT_GE(NSMaxY(frame), kAnchorPointY);
198 }
199
200 // Tests that when a new window gets key state (and the bubble resigns) that
201 // the key window changes.
202 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(
210       [[NSWindow alloc] initWithContentRect:NSMakeRect(500, 500, 500, 500)
211                                   styleMask:NSTitledWindowMask
212                                     backing:NSBackingStoreBuffered
213                                       defer:YES]);
214   EXPECT_FALSE([other_window isVisible]);
215
216   [controller_ showWindow:nil];
217   EXPECT_TRUE([bubble_window isVisible]);
218   EXPECT_FALSE([other_window isVisible]);
219
220   [other_window makeKeyAndOrderFront:nil];
221   // Fake the key state notification. Because unit_tests is a "daemon" process
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
237
238   EXPECT_FALSE([bubble_window isVisible]);
239   EXPECT_TRUE([other_window isVisible]);
240 }
241
242 // Test that clicking outside the window causes the bubble to close if
243 // shouldCloseOnResignKey is YES.
244 TEST_F(BaseBubbleControllerTest, LionClickOutsideClosesWithoutContextMenu) {
245   // The event tap is only installed on 10.7+.
246   if (!base::mac::IsOSLionOrLater())
247     return;
248
249   // Closing the bubble will autorelease the controller.
250   base::scoped_nsobject<BaseBubbleController> keep_alive([controller_ retain]);
251   NSWindow* window = [controller_ window];
252
253   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];
261   NSEvent* event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(
262       NSMakePoint(10, 10), test_window());
263   [NSApp sendEvent:event];
264
265   EXPECT_TRUE([window isVisible]);
266
267   event = cocoa_test_event_utils::RightMouseDownAtPointInWindow(
268       NSMakePoint(10, 10), test_window());
269   [NSApp sendEvent:event];
270
271   EXPECT_TRUE([window isVisible]);
272
273   [controller_ setShouldCloseOnResignKey:YES];
274   event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(
275       NSMakePoint(10, 10), test_window());
276   [NSApp sendEvent:event];
277
278   EXPECT_FALSE([window isVisible]);
279
280   [controller_ showWindow:nil]; // Show it again
281   EXPECT_TRUE([window isVisible]);
282   EXPECT_TRUE([controller_ shouldCloseOnResignKey]);  // Verify.
283
284   event = cocoa_test_event_utils::RightMouseDownAtPointInWindow(
285       NSMakePoint(10, 10), test_window());
286   [NSApp sendEvent:event];
287
288   EXPECT_FALSE([window isVisible]);
289 }
290
291 // Test that right-clicking the window with displaying a context menu causes
292 // the bubble  to close.
293 TEST_F(BaseBubbleControllerTest, LionRightClickOutsideClosesWithContextMenu) {
294   // The event tap is only installed on 10.7+.
295   if (!base::mac::IsOSLionOrLater())
296     return;
297
298   // Closing the bubble will autorelease the controller.
299   base::scoped_nsobject<BaseBubbleController> keep_alive([controller_ retain]);
300   NSWindow* window = [controller_ window];
301
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(
310       [[NSMenu alloc] initWithTitle:@""]);
311   [context_menu addItemWithTitle:@"ContextMenuTest"
312                           action:nil
313                    keyEquivalent:@""];
314   base::scoped_nsobject<ContextMenuController> menu_controller(
315       [[ContextMenuController alloc] initWithMenu:context_menu
316                                         andWindow:window]);
317
318   // Set the menu as the contextual menu of contentView of test_window().
319   [[test_window() contentView] setMenu:context_menu];
320
321   // RightMouseDown in test_window() would close the bubble window and then
322   // dispaly the contextual menu.
323   NSEvent* event = cocoa_test_event_utils::RightMouseDownAtPointInWindow(
324       NSMakePoint(10, 10), test_window());
325   // Verify bubble's window is closed when contextual menu is open.
326   CFRunLoopPerformBlock(CFRunLoopGetCurrent(), NSEventTrackingRunLoopMode, ^{
327       EXPECT_TRUE([menu_controller isMenuOpen]);
328       EXPECT_FALSE([menu_controller isWindowVisible]);
329   });
330
331   EXPECT_FALSE([menu_controller isMenuOpen]);
332   EXPECT_FALSE([menu_controller didOpen]);
333
334   [NSApp sendEvent:event];
335
336   // When we got here, menu has already run its RunLoop.
337   // See -[ContextualMenuController menuWillOpen:].
338   EXPECT_FALSE([window isVisible]);
339
340   EXPECT_FALSE([menu_controller isMenuOpen]);
341   EXPECT_TRUE([menu_controller didOpen]);
342 }
343