Roll src/third_party/WebKit 8b42d1d:744641d (svn 186770:186771)
[chromium-blink-merge.git] / chrome / browser / app_controller_mac_browsertest.mm
bloba136a1267f4744dd7b2011a483903a20b1e2d52f
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 <Carbon/Carbon.h>
6 #import <Cocoa/Cocoa.h>
7 #import <Foundation/Foundation.h>
8 #import <Foundation/NSAppleEventDescriptor.h>
9 #import <objc/message.h>
10 #import <objc/runtime.h>
12 #include "base/command_line.h"
13 #include "base/mac/foundation_util.h"
14 #include "base/mac/scoped_nsobject.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/run_loop.h"
17 #include "base/strings/sys_string_conversions.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "chrome/app/chrome_command_ids.h"
20 #include "components/bookmarks/browser/bookmark_model.h"
21 #import "chrome/browser/app_controller_mac.h"
22 #include "chrome/browser/apps/app_browsertest_util.h"
23 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
24 #include "chrome/browser/browser_process.h"
25 #include "chrome/browser/profiles/profile_manager.h"
26 #include "chrome/browser/ui/browser.h"
27 #include "chrome/browser/ui/browser_list.h"
28 #include "chrome/browser/ui/browser_window.h"
29 #include "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h"
30 #include "chrome/browser/ui/host_desktop.h"
31 #include "chrome/browser/ui/tabs/tab_strip_model.h"
32 #include "chrome/browser/ui/user_manager.h"
33 #include "chrome/common/chrome_constants.h"
34 #include "chrome/common/chrome_switches.h"
35 #include "chrome/common/pref_names.h"
36 #include "chrome/common/url_constants.h"
37 #include "chrome/test/base/in_process_browser_test.h"
38 #include "components/bookmarks/test/bookmark_test_helpers.h"
39 #include "components/signin/core/common/profile_management_switches.h"
40 #include "content/public/browser/web_contents.h"
41 #include "content/public/test/test_navigation_observer.h"
42 #include "extensions/browser/app_window/app_window_registry.h"
43 #include "extensions/common/extension.h"
44 #include "extensions/test/extension_test_message_listener.h"
45 #include "net/test/embedded_test_server/embedded_test_server.h"
47 namespace {
49 GURL g_open_shortcut_url = GURL::EmptyGURL();
51 // Returns an Apple Event that instructs the application to open |url|.
52 NSAppleEventDescriptor* AppleEventToOpenUrl(const GURL& url) {
53   NSAppleEventDescriptor* shortcut_event = [[[NSAppleEventDescriptor alloc]
54       initWithEventClass:kASAppleScriptSuite
55                  eventID:kASSubroutineEvent
56         targetDescriptor:nil
57                 returnID:kAutoGenerateReturnID
58            transactionID:kAnyTransactionID] autorelease];
59   NSString* url_string = [NSString stringWithUTF8String:url.spec().c_str()];
60   [shortcut_event setParamDescriptor:[NSAppleEventDescriptor
61                                          descriptorWithString:url_string]
62                           forKeyword:keyDirectObject];
63   return shortcut_event;
66 // Instructs the NSApp's delegate to open |url|.
67 void SendAppleEventToOpenUrlToAppController(const GURL& url) {
68   AppController* controller =
69       base::mac::ObjCCast<AppController>([NSApp delegate]);
70   Method get_url =
71       class_getInstanceMethod([controller class], @selector(getUrl:withReply:));
73   ASSERT_TRUE(get_url);
75   NSAppleEventDescriptor* shortcut_event = AppleEventToOpenUrl(url);
77   method_invoke(controller, get_url, shortcut_event, NULL);
80 }  // namespace
82 @interface TestOpenShortcutOnStartup : NSObject
83 - (void)applicationWillFinishLaunching:(NSNotification*)notification;
84 @end
86 @implementation TestOpenShortcutOnStartup
88 - (void)applicationWillFinishLaunching:(NSNotification*)notification {
89   if (!g_open_shortcut_url.is_valid())
90     return;
92   SendAppleEventToOpenUrlToAppController(g_open_shortcut_url);
95 @end
97 namespace {
99 class AppControllerPlatformAppBrowserTest
100     : public extensions::PlatformAppBrowserTest {
101  protected:
102   AppControllerPlatformAppBrowserTest()
103       : active_browser_list_(BrowserList::GetInstance(
104                                 chrome::GetActiveDesktop())) {
105   }
107   void SetUpCommandLine(CommandLine* command_line) override {
108     PlatformAppBrowserTest::SetUpCommandLine(command_line);
109     command_line->AppendSwitchASCII(switches::kAppId,
110                                     "1234");
111   }
113   const BrowserList* active_browser_list_;
116 // Test that if only a platform app window is open and no browser windows are
117 // open then a reopen event does nothing.
118 IN_PROC_BROWSER_TEST_F(AppControllerPlatformAppBrowserTest,
119                        PlatformAppReopenWithWindows) {
120   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
121   NSUInteger old_window_count = [[NSApp windows] count];
122   EXPECT_EQ(1u, active_browser_list_->size());
123   [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:YES];
124   // We do not EXPECT_TRUE the result here because the method
125   // deminiaturizes windows manually rather than return YES and have
126   // AppKit do it.
128   EXPECT_EQ(old_window_count, [[NSApp windows] count]);
129   EXPECT_EQ(1u, active_browser_list_->size());
132 IN_PROC_BROWSER_TEST_F(AppControllerPlatformAppBrowserTest,
133                        ActivationFocusesBrowserWindow) {
134   base::scoped_nsobject<AppController> app_controller(
135       [[AppController alloc] init]);
137   ExtensionTestMessageListener listener("Launched", false);
138   const extensions::Extension* app =
139       InstallAndLaunchPlatformApp("minimal");
140   ASSERT_TRUE(listener.WaitUntilSatisfied());
142   NSWindow* app_window = extensions::AppWindowRegistry::Get(profile())
143                              ->GetAppWindowsForApp(app->id())
144                              .front()
145                              ->GetNativeWindow();
146   NSWindow* browser_window = browser()->window()->GetNativeWindow();
148   EXPECT_LE([[NSApp orderedWindows] indexOfObject:app_window],
149             [[NSApp orderedWindows] indexOfObject:browser_window]);
150   [app_controller applicationShouldHandleReopen:NSApp
151                               hasVisibleWindows:YES];
152   EXPECT_LE([[NSApp orderedWindows] indexOfObject:browser_window],
153             [[NSApp orderedWindows] indexOfObject:app_window]);
156 class AppControllerWebAppBrowserTest : public InProcessBrowserTest {
157  protected:
158   AppControllerWebAppBrowserTest()
159       : active_browser_list_(BrowserList::GetInstance(
160                                 chrome::GetActiveDesktop())) {
161   }
163   void SetUpCommandLine(CommandLine* command_line) override {
164     command_line->AppendSwitchASCII(switches::kApp, GetAppURL());
165   }
167   std::string GetAppURL() const {
168     return "http://example.com/";
169   }
171   const BrowserList* active_browser_list_;
174 // Test that in web app mode a reopen event opens the app URL.
175 IN_PROC_BROWSER_TEST_F(AppControllerWebAppBrowserTest,
176                        WebAppReopenWithNoWindows) {
177   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
178   EXPECT_EQ(1u, active_browser_list_->size());
179   BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
181   EXPECT_FALSE(result);
182   EXPECT_EQ(2u, active_browser_list_->size());
184   Browser* browser = active_browser_list_->get(0);
185   GURL current_url =
186       browser->tab_strip_model()->GetActiveWebContents()->GetURL();
187   EXPECT_EQ(GetAppURL(), current_url.spec());
190 // Called when the ProfileManager has created a profile.
191 void CreateProfileCallback(const base::Closure& quit_closure,
192                            Profile* profile,
193                            Profile::CreateStatus status) {
194   EXPECT_TRUE(profile);
195   EXPECT_NE(Profile::CREATE_STATUS_LOCAL_FAIL, status);
196   EXPECT_NE(Profile::CREATE_STATUS_REMOTE_FAIL, status);
197   // This will be called multiple times. Wait until the profile is initialized
198   // fully to quit the loop.
199   if (status == Profile::CREATE_STATUS_INITIALIZED)
200     quit_closure.Run();
203 void CreateAndWaitForGuestProfile() {
204   ProfileManager::CreateCallback create_callback =
205       base::Bind(&CreateProfileCallback,
206                  base::MessageLoop::current()->QuitClosure());
207   g_browser_process->profile_manager()->CreateProfileAsync(
208       ProfileManager::GetGuestProfilePath(),
209       create_callback,
210       base::string16(),
211       base::string16(),
212       std::string());
213   base::RunLoop().Run();
216 class AppControllerNewProfileManagementBrowserTest
217     : public InProcessBrowserTest {
218  protected:
219   AppControllerNewProfileManagementBrowserTest()
220       : active_browser_list_(BrowserList::GetInstance(
221                                 chrome::GetActiveDesktop())) {
222   }
224   void SetUpCommandLine(CommandLine* command_line) override {
225     switches::EnableNewProfileManagementForTesting(command_line);
226   }
228   const BrowserList* active_browser_list_;
231 // Test that for a regular last profile, a reopen event opens a browser.
232 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
233                        RegularProfileReopenWithNoWindows) {
234   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
235   EXPECT_EQ(1u, active_browser_list_->size());
236   BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
238   EXPECT_FALSE(result);
239   EXPECT_EQ(2u, active_browser_list_->size());
240   EXPECT_FALSE(UserManager::IsShowing());
243 // Test that for a locked last profile, a reopen event opens the User Manager.
244 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
245                        LockedProfileReopenWithNoWindows) {
246   // The User Manager uses the guest profile as its underlying profile. To
247   // minimize flakiness due to the scheduling/descheduling of tasks on the
248   // different threads, pre-initialize the guest profile before it is needed.
249   CreateAndWaitForGuestProfile();
250   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
252   // Lock the active profile.
253   Profile* profile = [ac lastProfile];
254   ProfileInfoCache& cache =
255       g_browser_process->profile_manager()->GetProfileInfoCache();
256   size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath());
257   cache.SetProfileSigninRequiredAtIndex(profile_index, true);
258   EXPECT_TRUE(cache.ProfileIsSigninRequiredAtIndex(profile_index));
260   EXPECT_EQ(1u, active_browser_list_->size());
261   BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
262   EXPECT_FALSE(result);
264   base::RunLoop().RunUntilIdle();
265   EXPECT_EQ(1u, active_browser_list_->size());
266   EXPECT_TRUE(UserManager::IsShowing());
267   UserManager::Hide();
270 // Test that for a guest last profile, a reopen event opens the User Manager.
271 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
272                        GuestProfileReopenWithNoWindows) {
273   // Create the guest profile, and set it as the last used profile so the
274   // app controller can use it on init.
275   CreateAndWaitForGuestProfile();
276   PrefService* local_state = g_browser_process->local_state();
277   local_state->SetString(prefs::kProfileLastUsed, chrome::kGuestProfileDir);
279   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
281   Profile* profile = [ac lastProfile];
282   EXPECT_EQ(ProfileManager::GetGuestProfilePath(), profile->GetPath());
283   EXPECT_TRUE(profile->IsGuestSession());
285   EXPECT_EQ(1u, active_browser_list_->size());
286   BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
287   EXPECT_FALSE(result);
289   base::RunLoop().RunUntilIdle();
291   EXPECT_EQ(1u, active_browser_list_->size());
292   EXPECT_TRUE(UserManager::IsShowing());
293   UserManager::Hide();
296 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
297                        AboutChromeForcesUserManager) {
298   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
300   // Create the guest profile, and set it as the last used profile so the
301   // app controller can use it on init.
302   CreateAndWaitForGuestProfile();
303   PrefService* local_state = g_browser_process->local_state();
304   local_state->SetString(prefs::kProfileLastUsed, chrome::kGuestProfileDir);
306   // Prohibiting guest mode forces the user manager flow for About Chrome.
307   local_state->SetBoolean(prefs::kBrowserGuestModeEnabled, false);
309   Profile* guest_profile = [ac lastProfile];
310   EXPECT_EQ(ProfileManager::GetGuestProfilePath(), guest_profile->GetPath());
311   EXPECT_TRUE(guest_profile->IsGuestSession());
313   // Tell the browser to open About Chrome.
314   EXPECT_EQ(1u, active_browser_list_->size());
315   [ac orderFrontStandardAboutPanel:NSApp];
317   base::RunLoop().RunUntilIdle();
319   // No new browser is opened; the User Manager opens instead.
320   EXPECT_EQ(1u, active_browser_list_->size());
321   EXPECT_TRUE(UserManager::IsShowing());
323   UserManager::Hide();
326 class AppControllerOpenShortcutBrowserTest : public InProcessBrowserTest {
327  protected:
328   AppControllerOpenShortcutBrowserTest() {
329   }
331   void SetUpInProcessBrowserTestFixture() override {
332     // In order to mimic opening shortcut during browser startup, we need to
333     // send the event before -applicationDidFinishLaunching is called, but
334     // after AppController is loaded.
335     //
336     // Since -applicationWillFinishLaunching does nothing now, we swizzle it to
337     // our function to send the event. We need to do this early before running
338     // the main message loop.
339     //
340     // NSApp does not exist yet. We need to get the AppController using
341     // reflection.
342     Class appControllerClass = NSClassFromString(@"AppController");
343     Class openShortcutClass = NSClassFromString(@"TestOpenShortcutOnStartup");
345     ASSERT_TRUE(appControllerClass != nil);
346     ASSERT_TRUE(openShortcutClass != nil);
348     SEL targetMethod = @selector(applicationWillFinishLaunching:);
349     Method original = class_getInstanceMethod(appControllerClass,
350         targetMethod);
351     Method destination = class_getInstanceMethod(openShortcutClass,
352         targetMethod);
354     ASSERT_TRUE(original != NULL);
355     ASSERT_TRUE(destination != NULL);
357     method_exchangeImplementations(original, destination);
359     ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
360     g_open_shortcut_url = embedded_test_server()->GetURL("/simple.html");
361   }
363   void SetUpCommandLine(CommandLine* command_line) override {
364     // If the arg is empty, PrepareTestCommandLine() after this function will
365     // append about:blank as default url.
366     command_line->AppendArg(chrome::kChromeUINewTabURL);
367   }
370 IN_PROC_BROWSER_TEST_F(AppControllerOpenShortcutBrowserTest,
371                        OpenShortcutOnStartup) {
372   EXPECT_EQ(1, browser()->tab_strip_model()->count());
373   EXPECT_EQ(g_open_shortcut_url,
374       browser()->tab_strip_model()->GetActiveWebContents()
375           ->GetLastCommittedURL());
378 class AppControllerReplaceNTPBrowserTest : public InProcessBrowserTest {
379  protected:
380   AppControllerReplaceNTPBrowserTest() {}
382   void SetUpInProcessBrowserTestFixture() override {
383     ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
384   }
386   void SetUpCommandLine(CommandLine* command_line) override {
387     // If the arg is empty, PrepareTestCommandLine() after this function will
388     // append about:blank as default url.
389     command_line->AppendArg(chrome::kChromeUINewTabURL);
390   }
393 // Tests that when a GURL is opened after startup, it replaces the NTP.
394 IN_PROC_BROWSER_TEST_F(AppControllerReplaceNTPBrowserTest,
395                        ReplaceNTPAfterStartup) {
396   // Ensure that there is exactly 1 tab showing, and the tab is the NTP.
397   GURL ntp(chrome::kChromeUINewTabURL);
398   EXPECT_EQ(1, browser()->tab_strip_model()->count());
399   EXPECT_EQ(ntp,
400             browser()
401                 ->tab_strip_model()
402                 ->GetActiveWebContents()
403                 ->GetLastCommittedURL());
405   GURL simple(embedded_test_server()->GetURL("/simple.html"));
406   SendAppleEventToOpenUrlToAppController(simple);
408   // Wait for one navigation on the active web contents.
409   EXPECT_EQ(1, browser()->tab_strip_model()->count());
410   content::TestNavigationObserver obs(
411       browser()->tab_strip_model()->GetActiveWebContents(), 1);
412   obs.Wait();
414   EXPECT_EQ(simple,
415             browser()
416                 ->tab_strip_model()
417                 ->GetActiveWebContents()
418                 ->GetLastCommittedURL());
421 class AppControllerMainMenuBrowserTest : public InProcessBrowserTest {
422  protected:
423   AppControllerMainMenuBrowserTest() {
424   }
427 IN_PROC_BROWSER_TEST_F(AppControllerMainMenuBrowserTest,
428     BookmarksMenuIsRestoredAfterProfileSwitch) {
429   ProfileManager* profile_manager = g_browser_process->profile_manager();
430   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
431   [ac awakeFromNib];
433   // Constants for bookmarks that we will create later.
434   const base::string16 title1(base::ASCIIToUTF16("Dinosaur Comics"));
435   const GURL url1("http://qwantz.com//");
437   const base::string16 title2(base::ASCIIToUTF16("XKCD"));
438   const GURL url2("https://www.xkcd.com/");
440   // Use the existing profile as profile 1.
441   Profile* profile1 = browser()->profile();
442   bookmarks::test::WaitForBookmarkModelToLoad(
443       BookmarkModelFactory::GetForProfile(profile1));
445   // Create profile 2.
446   base::FilePath path2 = profile_manager->GenerateNextProfileDirectoryPath();
447   Profile* profile2 =
448       Profile::CreateProfile(path2, NULL, Profile::CREATE_MODE_SYNCHRONOUS);
449   profile_manager->RegisterTestingProfile(profile2, false, true);
450   bookmarks::test::WaitForBookmarkModelToLoad(
451       BookmarkModelFactory::GetForProfile(profile2));
453   // Switch to profile 1, create bookmark 1 and force the menu to build.
454   [ac windowChangedToProfile:profile1];
455   [ac bookmarkMenuBridge]->GetBookmarkModel()->AddURL(
456       [ac bookmarkMenuBridge]->GetBookmarkModel()->bookmark_bar_node(),
457       0, title1, url1);
458   [ac bookmarkMenuBridge]->BuildMenu();
460   // Switch to profile 2, create bookmark 2 and force the menu to build.
461   [ac windowChangedToProfile:profile2];
462   [ac bookmarkMenuBridge]->GetBookmarkModel()->AddURL(
463       [ac bookmarkMenuBridge]->GetBookmarkModel()->bookmark_bar_node(),
464       0, title2, url2);
465   [ac bookmarkMenuBridge]->BuildMenu();
467   // Test that only bookmark 2 is shown.
468   EXPECT_FALSE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
469       SysUTF16ToNSString(title1)]);
470   EXPECT_TRUE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
471       SysUTF16ToNSString(title2)]);
473   // Switch *back* to profile 1 and *don't* force the menu to build.
474   [ac windowChangedToProfile:profile1];
476   // Test that only bookmark 1 is shown in the restored menu.
477   EXPECT_TRUE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
478       SysUTF16ToNSString(title1)]);
479   EXPECT_FALSE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
480       SysUTF16ToNSString(title2)]);
483 }  // namespace