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 #import <Cocoa/Cocoa.h>
7 #import "base/mac/scoped_nsobject.h"
8 #include "base/run_loop.h"
9 #include "base/test/test_timeouts.h"
10 #include "chrome/app/chrome_command_ids.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/ui/browser_commands.h"
13 #include "chrome/browser/ui/browser_finder.h"
14 #include "chrome/browser/ui/browser_window.h"
15 #include "chrome/browser/ui/panels/base_panel_browser_test.h"
16 #include "chrome/browser/ui/panels/panel.h"
17 #include "content/public/test/test_utils.h"
19 // Class that spins a run loop until an NSWindow gains key status.
20 @interface CocoaActivationWaiter : NSObject {
22 base::RunLoop* runLoop_;
26 - (id)initWithWindow:(NSWindow*)window;
27 - (void)windowDidBecomeKey:(NSNotification*)notification;
28 - (BOOL)waitUntilActive;
32 @implementation CocoaActivationWaiter
34 - (id)initWithWindow:(NSWindow*)window {
35 EXPECT_FALSE([window isKeyWindow]);
36 if ((self = [super init])) {
37 [[NSNotificationCenter defaultCenter]
39 selector:@selector(windowDidBecomeKey:)
40 name:NSWindowDidBecomeKeyNotification
47 [[NSNotificationCenter defaultCenter] removeObserver:self];
51 - (void)windowDidBecomeKey:(NSNotification*)notification {
57 - (BOOL)waitUntilActive {
61 base::RunLoop runLoop;
62 base::MessageLoop::current()->task_runner()->PostDelayedTask(
63 FROM_HERE, runLoop.QuitClosure(), TestTimeouts::action_timeout());
72 typedef BasePanelBrowserTest PanelCocoaBrowserTest;
74 IN_PROC_BROWSER_TEST_F(PanelCocoaBrowserTest, MenuItems) {
75 Panel* panel = CreatePanel("Panel");
77 // Close main tabbed window.
78 content::WindowedNotificationObserver signal(
79 chrome::NOTIFICATION_BROWSER_CLOSED,
80 content::Source<Browser>(browser()));
81 chrome::CloseWindow(browser());
84 // There should be no browser windows.
85 EXPECT_EQ(0u, chrome::GetTotalBrowserCount());
87 // There should be one panel.
88 EXPECT_EQ(1, PanelManager::GetInstance()->num_panels());
90 NSMenu* mainMenu = [NSApp mainMenu];
91 EXPECT_TRUE(mainMenu);
93 // Chrome(0) File(1) ....
94 // Get File submenu. It doesn't have a command id, fetch it by index.
95 NSMenu* fileSubmenu = [[[mainMenu itemArray] objectAtIndex:1] submenu];
96 EXPECT_TRUE(fileSubmenu);
99 // Verify the items normally enabled for "all windows closed" case are
100 // also enabled when there is a panel but no browser windows on the screen.
101 NSMenuItem* item = [fileSubmenu itemWithTag:IDC_NEW_TAB];
103 EXPECT_TRUE([item isEnabled]);
105 item = [fileSubmenu itemWithTag:IDC_NEW_WINDOW];
107 EXPECT_TRUE([item isEnabled]);
109 item = [fileSubmenu itemWithTag:IDC_NEW_INCOGNITO_WINDOW];
111 EXPECT_TRUE([item isEnabled]);
113 NSMenu* historySubmenu = [[mainMenu itemWithTag:IDC_HISTORY_MENU] submenu];
114 EXPECT_TRUE(historySubmenu);
115 [historySubmenu update];
117 // These should be disabled.
118 item = [historySubmenu itemWithTag:IDC_HOME];
120 EXPECT_FALSE([item isEnabled]);
122 item = [historySubmenu itemWithTag:IDC_BACK];
124 EXPECT_FALSE([item isEnabled]);
126 item = [historySubmenu itemWithTag:IDC_FORWARD];
128 EXPECT_FALSE([item isEnabled]);
130 // 'Close Window' should be enabled because the remaining Panel is a Responder
131 // which implements performClose:, the 'action' of 'Close Window'.
132 for (NSMenuItem *i in [fileSubmenu itemArray]) {
133 if ([i action] == @selector(performClose:)) {
139 EXPECT_TRUE([item isEnabled]);
144 // Test that panels do not become active when closing a window, even when a
145 // panel is otherwise the topmost window.
146 IN_PROC_BROWSER_TEST_F(PanelCocoaBrowserTest, InactivePanelsNotActivated) {
147 // Note CreateDockedPanel() sets wait_for_fully_created and SHOW_AS_ACTIVE,
148 // so the following spins a run loop until the respective panel is activated.
149 Panel* docked_panel_1 = CreateDockedPanel("Panel1", gfx::Rect());
150 EXPECT_TRUE([docked_panel_1->GetNativeWindow() isKeyWindow]);
152 // Activate the browser. Otherwise closing the second panel will correctly
153 // raise the first panel since panels are allowed to become key if they were
154 // actually the most recently focused window (as opposed to being merely the
155 // topmost window on the z-order stack).
156 NSWindow* browser_window = browser()->window()->GetNativeWindow();
157 base::scoped_nsobject<CocoaActivationWaiter> waiter(
158 [[CocoaActivationWaiter alloc] initWithWindow:browser_window]);
159 browser()->window()->Activate();
160 EXPECT_TRUE([waiter waitUntilActive]);
162 // Creating a second panel will activate it (and make it topmost).
163 Panel* docked_panel_2 = CreateDockedPanel("Panel2", gfx::Rect());
164 EXPECT_TRUE([docked_panel_2->GetNativeWindow() isKeyWindow]);
166 // Verify the assumptions that the panels are actually topmost.
167 EXPECT_EQ(docked_panel_2->GetNativeWindow(),
168 [[NSApp orderedWindows] objectAtIndex:0]);
169 EXPECT_EQ(docked_panel_1->GetNativeWindow(),
170 [[NSApp orderedWindows] objectAtIndex:1]);
172 // Close the second panel and wait for the browser to become active.
173 waiter.reset([[CocoaActivationWaiter alloc] initWithWindow:browser_window]);
174 docked_panel_2->Close();
176 EXPECT_TRUE([waiter waitUntilActive]);
177 EXPECT_TRUE([browser_window isKeyWindow]);