Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / app_controller_mac_browsertest.mm
blobf23b840ddbe0dd986bc05176aa0306b15ce79e0e
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 #import "chrome/browser/app_controller_mac.h"
21 #include "chrome/browser/apps/app_browsertest_util.h"
22 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
23 #include "chrome/browser/browser_process.h"
24 #include "chrome/browser/history/history_service_factory.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/cocoa/history_menu_bridge.h"
31 #include "chrome/browser/ui/host_desktop.h"
32 #include "chrome/browser/ui/tabs/tab_strip_model.h"
33 #include "chrome/browser/ui/user_manager.h"
34 #include "chrome/common/chrome_constants.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/common/pref_names.h"
37 #include "chrome/common/url_constants.h"
38 #include "chrome/test/base/in_process_browser_test.h"
39 #include "chrome/test/base/ui_test_utils.h"
40 #include "components/bookmarks/browser/bookmark_model.h"
41 #include "components/bookmarks/test/bookmark_test_helpers.h"
42 #include "components/signin/core/common/profile_management_switches.h"
43 #include "content/public/browser/web_contents.h"
44 #include "content/public/test/browser_test_utils.h"
45 #include "content/public/test/test_navigation_observer.h"
46 #include "extensions/browser/app_window/app_window_registry.h"
47 #include "extensions/common/extension.h"
48 #include "extensions/test/extension_test_message_listener.h"
49 #include "net/test/embedded_test_server/embedded_test_server.h"
51 namespace {
53 GURL g_open_shortcut_url = GURL::EmptyGURL();
55 // Returns an Apple Event that instructs the application to open |url|.
56 NSAppleEventDescriptor* AppleEventToOpenUrl(const GURL& url) {
57   NSAppleEventDescriptor* shortcut_event = [[[NSAppleEventDescriptor alloc]
58       initWithEventClass:kASAppleScriptSuite
59                  eventID:kASSubroutineEvent
60         targetDescriptor:nil
61                 returnID:kAutoGenerateReturnID
62            transactionID:kAnyTransactionID] autorelease];
63   NSString* url_string = [NSString stringWithUTF8String:url.spec().c_str()];
64   [shortcut_event setParamDescriptor:[NSAppleEventDescriptor
65                                          descriptorWithString:url_string]
66                           forKeyword:keyDirectObject];
67   return shortcut_event;
70 // Instructs the NSApp's delegate to open |url|.
71 void SendAppleEventToOpenUrlToAppController(const GURL& url) {
72   AppController* controller =
73       base::mac::ObjCCast<AppController>([NSApp delegate]);
74   Method get_url =
75       class_getInstanceMethod([controller class], @selector(getUrl:withReply:));
77   ASSERT_TRUE(get_url);
79   NSAppleEventDescriptor* shortcut_event = AppleEventToOpenUrl(url);
81   method_invoke(controller, get_url, shortcut_event, NULL);
84 void RunClosureWhenProfileInitialized(const base::Closure& closure,
85                                       Profile* profile,
86                                       Profile::CreateStatus status) {
87   if (status == Profile::CREATE_STATUS_INITIALIZED)
88     closure.Run();
91 }  // namespace
93 @interface TestOpenShortcutOnStartup : NSObject
94 - (void)applicationWillFinishLaunching:(NSNotification*)notification;
95 @end
97 @implementation TestOpenShortcutOnStartup
99 - (void)applicationWillFinishLaunching:(NSNotification*)notification {
100   if (!g_open_shortcut_url.is_valid())
101     return;
103   SendAppleEventToOpenUrlToAppController(g_open_shortcut_url);
106 @end
108 namespace {
110 class AppControllerPlatformAppBrowserTest
111     : public extensions::PlatformAppBrowserTest {
112  protected:
113   AppControllerPlatformAppBrowserTest()
114       : active_browser_list_(BrowserList::GetInstance(
115                                 chrome::GetActiveDesktop())) {
116   }
118   void SetUpCommandLine(base::CommandLine* command_line) override {
119     PlatformAppBrowserTest::SetUpCommandLine(command_line);
120     command_line->AppendSwitchASCII(switches::kAppId,
121                                     "1234");
122   }
124   const BrowserList* active_browser_list_;
127 // Test that if only a platform app window is open and no browser windows are
128 // open then a reopen event does nothing.
129 IN_PROC_BROWSER_TEST_F(AppControllerPlatformAppBrowserTest,
130                        PlatformAppReopenWithWindows) {
131   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
132   NSUInteger old_window_count = [[NSApp windows] count];
133   EXPECT_EQ(1u, active_browser_list_->size());
134   [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:YES];
135   // We do not EXPECT_TRUE the result here because the method
136   // deminiaturizes windows manually rather than return YES and have
137   // AppKit do it.
139   EXPECT_EQ(old_window_count, [[NSApp windows] count]);
140   EXPECT_EQ(1u, active_browser_list_->size());
143 IN_PROC_BROWSER_TEST_F(AppControllerPlatformAppBrowserTest,
144                        ActivationFocusesBrowserWindow) {
145   base::scoped_nsobject<AppController> app_controller(
146       [[AppController alloc] init]);
148   ExtensionTestMessageListener listener("Launched", false);
149   const extensions::Extension* app =
150       InstallAndLaunchPlatformApp("minimal");
151   ASSERT_TRUE(listener.WaitUntilSatisfied());
153   NSWindow* app_window = extensions::AppWindowRegistry::Get(profile())
154                              ->GetAppWindowsForApp(app->id())
155                              .front()
156                              ->GetNativeWindow();
157   NSWindow* browser_window = browser()->window()->GetNativeWindow();
159   EXPECT_LE([[NSApp orderedWindows] indexOfObject:app_window],
160             [[NSApp orderedWindows] indexOfObject:browser_window]);
161   [app_controller applicationShouldHandleReopen:NSApp
162                               hasVisibleWindows:YES];
163   EXPECT_LE([[NSApp orderedWindows] indexOfObject:browser_window],
164             [[NSApp orderedWindows] indexOfObject:app_window]);
167 class AppControllerWebAppBrowserTest : public InProcessBrowserTest {
168  protected:
169   AppControllerWebAppBrowserTest()
170       : active_browser_list_(BrowserList::GetInstance(
171                                 chrome::GetActiveDesktop())) {
172   }
174   void SetUpCommandLine(base::CommandLine* command_line) override {
175     command_line->AppendSwitchASCII(switches::kApp, GetAppURL());
176   }
178   std::string GetAppURL() const {
179     return "http://example.com/";
180   }
182   const BrowserList* active_browser_list_;
185 // Test that in web app mode a reopen event opens the app URL.
186 IN_PROC_BROWSER_TEST_F(AppControllerWebAppBrowserTest,
187                        WebAppReopenWithNoWindows) {
188   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
189   EXPECT_EQ(1u, active_browser_list_->size());
190   BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
192   EXPECT_FALSE(result);
193   EXPECT_EQ(2u, active_browser_list_->size());
195   Browser* browser = active_browser_list_->get(0);
196   GURL current_url =
197       browser->tab_strip_model()->GetActiveWebContents()->GetURL();
198   EXPECT_EQ(GetAppURL(), current_url.spec());
201 // Called when the ProfileManager has created a profile.
202 void CreateProfileCallback(const base::Closure& quit_closure,
203                            Profile* profile,
204                            Profile::CreateStatus status) {
205   EXPECT_TRUE(profile);
206   EXPECT_NE(Profile::CREATE_STATUS_LOCAL_FAIL, status);
207   EXPECT_NE(Profile::CREATE_STATUS_REMOTE_FAIL, status);
208   // This will be called multiple times. Wait until the profile is initialized
209   // fully to quit the loop.
210   if (status == Profile::CREATE_STATUS_INITIALIZED)
211     quit_closure.Run();
214 void CreateAndWaitForSystemProfile() {
215   ProfileManager::CreateCallback create_callback =
216       base::Bind(&CreateProfileCallback,
217                  base::MessageLoop::current()->QuitClosure());
218   g_browser_process->profile_manager()->CreateProfileAsync(
219       ProfileManager::GetSystemProfilePath(),
220       create_callback,
221       base::string16(),
222       base::string16(),
223       std::string());
224   base::RunLoop().Run();
227 class AppControllerNewProfileManagementBrowserTest
228     : public InProcessBrowserTest {
229  protected:
230   AppControllerNewProfileManagementBrowserTest()
231       : active_browser_list_(BrowserList::GetInstance(
232                                 chrome::GetActiveDesktop())) {
233   }
235   void SetUpCommandLine(base::CommandLine* command_line) override {
236     switches::EnableNewProfileManagementForTesting(command_line);
237   }
239   const BrowserList* active_browser_list_;
242 // Test that for a regular last profile, a reopen event opens a browser.
243 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
244                        RegularProfileReopenWithNoWindows) {
245   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
246   EXPECT_EQ(1u, active_browser_list_->size());
247   BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
249   EXPECT_FALSE(result);
250   EXPECT_EQ(2u, active_browser_list_->size());
251   EXPECT_FALSE(UserManager::IsShowing());
254 // Test that for a locked last profile, a reopen event opens the User Manager.
255 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
256                        LockedProfileReopenWithNoWindows) {
257   // The User Manager uses the system profile as its underlying profile. To
258   // minimize flakiness due to the scheduling/descheduling of tasks on the
259   // different threads, pre-initialize the guest profile before it is needed.
260   CreateAndWaitForSystemProfile();
261   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
263   // Lock the active profile.
264   Profile* profile = [ac lastProfile];
265   ProfileInfoCache& cache =
266       g_browser_process->profile_manager()->GetProfileInfoCache();
267   size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath());
268   cache.SetProfileSigninRequiredAtIndex(profile_index, true);
269   EXPECT_TRUE(cache.ProfileIsSigninRequiredAtIndex(profile_index));
271   EXPECT_EQ(1u, active_browser_list_->size());
272   BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
273   EXPECT_FALSE(result);
275   base::RunLoop().RunUntilIdle();
276   EXPECT_EQ(1u, active_browser_list_->size());
277   EXPECT_TRUE(UserManager::IsShowing());
278   UserManager::Hide();
281 // Test that for a guest last profile, a reopen event opens the User Manager.
282 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
283                        GuestProfileReopenWithNoWindows) {
284   // Create the system profile. Set the guest as the last used profile so the
285   // app controller can use it on init.
286   CreateAndWaitForSystemProfile();
287   PrefService* local_state = g_browser_process->local_state();
288   local_state->SetString(prefs::kProfileLastUsed, chrome::kGuestProfileDir);
290   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
292   Profile* profile = [ac lastProfile];
293   EXPECT_EQ(ProfileManager::GetGuestProfilePath(), profile->GetPath());
294   EXPECT_TRUE(profile->IsGuestSession());
296   EXPECT_EQ(1u, active_browser_list_->size());
297   BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
298   EXPECT_FALSE(result);
300   base::RunLoop().RunUntilIdle();
302   EXPECT_EQ(1u, active_browser_list_->size());
303   EXPECT_TRUE(UserManager::IsShowing());
304   UserManager::Hide();
307 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
308                        AboutChromeForcesUserManager) {
309   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
311   // Create the guest profile, and set it as the last used profile so the
312   // app controller can use it on init.
313   CreateAndWaitForSystemProfile();
314   PrefService* local_state = g_browser_process->local_state();
315   local_state->SetString(prefs::kProfileLastUsed, chrome::kGuestProfileDir);
317   // Prohibiting guest mode forces the user manager flow for About Chrome.
318   local_state->SetBoolean(prefs::kBrowserGuestModeEnabled, false);
320   Profile* guest_profile = [ac lastProfile];
321   EXPECT_EQ(ProfileManager::GetGuestProfilePath(), guest_profile->GetPath());
322   EXPECT_TRUE(guest_profile->IsGuestSession());
324   // Tell the browser to open About Chrome.
325   EXPECT_EQ(1u, active_browser_list_->size());
326   [ac orderFrontStandardAboutPanel:NSApp];
328   base::RunLoop().RunUntilIdle();
330   // No new browser is opened; the User Manager opens instead.
331   EXPECT_EQ(1u, active_browser_list_->size());
332   EXPECT_TRUE(UserManager::IsShowing());
334   UserManager::Hide();
337 class AppControllerOpenShortcutBrowserTest : public InProcessBrowserTest {
338  protected:
339   AppControllerOpenShortcutBrowserTest() {
340   }
342   void SetUpInProcessBrowserTestFixture() override {
343     // In order to mimic opening shortcut during browser startup, we need to
344     // send the event before -applicationDidFinishLaunching is called, but
345     // after AppController is loaded.
346     //
347     // Since -applicationWillFinishLaunching does nothing now, we swizzle it to
348     // our function to send the event. We need to do this early before running
349     // the main message loop.
350     //
351     // NSApp does not exist yet. We need to get the AppController using
352     // reflection.
353     Class appControllerClass = NSClassFromString(@"AppController");
354     Class openShortcutClass = NSClassFromString(@"TestOpenShortcutOnStartup");
356     ASSERT_TRUE(appControllerClass != nil);
357     ASSERT_TRUE(openShortcutClass != nil);
359     SEL targetMethod = @selector(applicationWillFinishLaunching:);
360     Method original = class_getInstanceMethod(appControllerClass,
361         targetMethod);
362     Method destination = class_getInstanceMethod(openShortcutClass,
363         targetMethod);
365     ASSERT_TRUE(original != NULL);
366     ASSERT_TRUE(destination != NULL);
368     method_exchangeImplementations(original, destination);
370     ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
371     g_open_shortcut_url = embedded_test_server()->GetURL("/simple.html");
372   }
374   void SetUpCommandLine(base::CommandLine* command_line) override {
375     // If the arg is empty, PrepareTestCommandLine() after this function will
376     // append about:blank as default url.
377     command_line->AppendArg(chrome::kChromeUINewTabURL);
378   }
381 IN_PROC_BROWSER_TEST_F(AppControllerOpenShortcutBrowserTest,
382                        OpenShortcutOnStartup) {
383   EXPECT_EQ(1, browser()->tab_strip_model()->count());
384   EXPECT_EQ(g_open_shortcut_url,
385       browser()->tab_strip_model()->GetActiveWebContents()
386           ->GetLastCommittedURL());
389 class AppControllerReplaceNTPBrowserTest : public InProcessBrowserTest {
390  protected:
391   AppControllerReplaceNTPBrowserTest() {}
393   void SetUpInProcessBrowserTestFixture() override {
394     ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
395   }
397   void SetUpCommandLine(base::CommandLine* command_line) override {
398     // If the arg is empty, PrepareTestCommandLine() after this function will
399     // append about:blank as default url.
400     command_line->AppendArg(chrome::kChromeUINewTabURL);
401   }
404 // Tests that when a GURL is opened after startup, it replaces the NTP.
405 IN_PROC_BROWSER_TEST_F(AppControllerReplaceNTPBrowserTest,
406                        ReplaceNTPAfterStartup) {
407   // Ensure that there is exactly 1 tab showing, and the tab is the NTP.
408   GURL ntp(chrome::kChromeUINewTabURL);
409   EXPECT_EQ(1, browser()->tab_strip_model()->count());
410   EXPECT_EQ(ntp,
411             browser()
412                 ->tab_strip_model()
413                 ->GetActiveWebContents()
414                 ->GetLastCommittedURL());
416   GURL simple(embedded_test_server()->GetURL("/simple.html"));
417   SendAppleEventToOpenUrlToAppController(simple);
419   // Wait for one navigation on the active web contents.
420   EXPECT_EQ(1, browser()->tab_strip_model()->count());
421   content::TestNavigationObserver obs(
422       browser()->tab_strip_model()->GetActiveWebContents(), 1);
423   obs.Wait();
425   EXPECT_EQ(simple,
426             browser()
427                 ->tab_strip_model()
428                 ->GetActiveWebContents()
429                 ->GetLastCommittedURL());
432 class AppControllerMainMenuBrowserTest : public InProcessBrowserTest {
433  protected:
434   AppControllerMainMenuBrowserTest() {
435   }
438 IN_PROC_BROWSER_TEST_F(AppControllerMainMenuBrowserTest,
439     HistoryMenuResetAfterProfileDeletion) {
440   ProfileManager* profile_manager = g_browser_process->profile_manager();
441   AppController* ac = [NSApp delegate];
443   // Use the existing profile as profile 1.
444   Profile* profile1 = browser()->profile();
446   // Create profile 2.
447   base::FilePath profile2_path =
448       profile_manager->GenerateNextProfileDirectoryPath();
449   base::RunLoop run_loop;
450   profile_manager->CreateProfileAsync(
451       profile2_path,
452       base::Bind(&RunClosureWhenProfileInitialized,
453                  run_loop.QuitClosure()),
454                  base::string16(),
455                  base::string16(),
456                  std::string());
457   run_loop.Run();
458   Profile* profile2 = profile_manager->GetProfileByPath(profile2_path);
459   ASSERT_TRUE(profile2);
461   // Switch the controller to profile1.
462   [ac windowChangedToProfile:profile1];
463   base::RunLoop().RunUntilIdle();
465   // Verify the controller's History Menu corresponds to profile1.
466   EXPECT_TRUE([ac historyMenuBridge]->service());
467   EXPECT_EQ([ac historyMenuBridge]->service(),
468       HistoryServiceFactory::GetForProfile(profile1,
469                                            ServiceAccessType::EXPLICIT_ACCESS));
471   // Load profile2's History Service backend so it will be assigned to the
472   // HistoryMenuBridge when windowChangedToProfile is called, or else this test
473   // will fail flaky.
474   ui_test_utils::WaitForHistoryToLoad(
475       HistoryServiceFactory::GetForProfile(profile2,
476                                            ServiceAccessType::EXPLICIT_ACCESS));
477   // Switch the controller to profile2.
478   [ac windowChangedToProfile:profile2];
479   base::RunLoop().RunUntilIdle();
481   // Verify the controller's History Menu has changed.
482   EXPECT_TRUE([ac historyMenuBridge]->service());
483   EXPECT_EQ([ac historyMenuBridge]->service(),
484       HistoryServiceFactory::GetForProfile(profile2,
485                                            ServiceAccessType::EXPLICIT_ACCESS));
486   EXPECT_NE(
487       HistoryServiceFactory::GetForProfile(profile1,
488                                            ServiceAccessType::EXPLICIT_ACCESS),
489       HistoryServiceFactory::GetForProfile(profile2,
490                                            ServiceAccessType::EXPLICIT_ACCESS));
492   // Delete profile2.
493   profile_manager->ScheduleProfileForDeletion(
494       profile2->GetPath(), ProfileManager::CreateCallback());
495   base::RunLoop().RunUntilIdle();
497   // Verify the controller's history is back to profile1.
498   EXPECT_EQ([ac historyMenuBridge]->service(),
499       HistoryServiceFactory::GetForProfile(profile1,
500                                            ServiceAccessType::EXPLICIT_ACCESS));
503 IN_PROC_BROWSER_TEST_F(AppControllerMainMenuBrowserTest,
504     BookmarksMenuIsRestoredAfterProfileSwitch) {
505   ProfileManager* profile_manager = g_browser_process->profile_manager();
506   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
507   [ac awakeFromNib];
509   // Constants for bookmarks that we will create later.
510   const base::string16 title1(base::ASCIIToUTF16("Dinosaur Comics"));
511   const GURL url1("http://qwantz.com//");
513   const base::string16 title2(base::ASCIIToUTF16("XKCD"));
514   const GURL url2("https://www.xkcd.com/");
516   // Use the existing profile as profile 1.
517   Profile* profile1 = browser()->profile();
518   bookmarks::test::WaitForBookmarkModelToLoad(
519       BookmarkModelFactory::GetForProfile(profile1));
521   // Create profile 2.
522   base::FilePath path2 = profile_manager->GenerateNextProfileDirectoryPath();
523   Profile* profile2 =
524       Profile::CreateProfile(path2, NULL, Profile::CREATE_MODE_SYNCHRONOUS);
525   profile_manager->RegisterTestingProfile(profile2, false, true);
526   bookmarks::test::WaitForBookmarkModelToLoad(
527       BookmarkModelFactory::GetForProfile(profile2));
529   // Switch to profile 1, create bookmark 1 and force the menu to build.
530   [ac windowChangedToProfile:profile1];
531   [ac bookmarkMenuBridge]->GetBookmarkModel()->AddURL(
532       [ac bookmarkMenuBridge]->GetBookmarkModel()->bookmark_bar_node(),
533       0, title1, url1);
534   [ac bookmarkMenuBridge]->BuildMenu();
536   // Switch to profile 2, create bookmark 2 and force the menu to build.
537   [ac windowChangedToProfile:profile2];
538   [ac bookmarkMenuBridge]->GetBookmarkModel()->AddURL(
539       [ac bookmarkMenuBridge]->GetBookmarkModel()->bookmark_bar_node(),
540       0, title2, url2);
541   [ac bookmarkMenuBridge]->BuildMenu();
543   // Test that only bookmark 2 is shown.
544   EXPECT_FALSE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
545       SysUTF16ToNSString(title1)]);
546   EXPECT_TRUE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
547       SysUTF16ToNSString(title2)]);
549   // Switch *back* to profile 1 and *don't* force the menu to build.
550   [ac windowChangedToProfile:profile1];
552   // Test that only bookmark 1 is shown in the restored menu.
553   EXPECT_TRUE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
554       SysUTF16ToNSString(title1)]);
555   EXPECT_FALSE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
556       SysUTF16ToNSString(title2)]);
559 }  // namespace
561 //--------------------------AppControllerHandoffBrowserTest---------------------
563 static GURL g_handoff_url;
565 @interface AppController (BrowserTest)
566 - (BOOL)new_shouldUseHandoff;
567 - (void)new_passURLToHandoffManager:(const GURL&)handoffURL;
568 @end
570 @implementation AppController (BrowserTest)
571 - (BOOL)new_shouldUseHandoff {
572   return YES;
575 - (void)new_passURLToHandoffManager:(const GURL&)handoffURL {
576   g_handoff_url = handoffURL;
578 @end
580 namespace {
582 class AppControllerHandoffBrowserTest : public InProcessBrowserTest {
583  protected:
584   AppControllerHandoffBrowserTest() {}
586   // Exchanges the implementations of the two selectors on the class
587   // AppController.
588   void ExchangeSelectors(SEL originalMethod, SEL newMethod) {
589     Class appControllerClass = NSClassFromString(@"AppController");
591     ASSERT_TRUE(appControllerClass != nil);
593     Method original =
594         class_getInstanceMethod(appControllerClass, originalMethod);
595     Method destination = class_getInstanceMethod(appControllerClass, newMethod);
597     ASSERT_TRUE(original != NULL);
598     ASSERT_TRUE(destination != NULL);
600     method_exchangeImplementations(original, destination);
601   }
603   // Swizzle Handoff related implementations.
604   void SetUpInProcessBrowserTestFixture() override {
605     // Handoff is only available on OSX 10.10+. This swizzle makes the logic
606     // run on all OSX versions.
607     SEL originalMethod = @selector(shouldUseHandoff);
608     SEL newMethod = @selector(new_shouldUseHandoff);
609     ExchangeSelectors(originalMethod, newMethod);
611     // This swizzle intercepts the URL that would be sent to the Handoff
612     // Manager, and instead puts it into a variable accessible to this test.
613     originalMethod = @selector(passURLToHandoffManager:);
614     newMethod = @selector(new_passURLToHandoffManager:);
615     ExchangeSelectors(originalMethod, newMethod);
616   }
618   // Closes the tab, and waits for the close to finish.
619   void CloseTab(Browser* browser, int index) {
620     content::WebContentsDestroyedWatcher destroyed_watcher(
621         browser->tab_strip_model()->GetWebContentsAt(index));
622     browser->tab_strip_model()->CloseWebContentsAt(
623         index, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
624     destroyed_watcher.Wait();
625   }
628 // Tests that as a user switches between tabs, navigates within a tab, and
629 // switches between browser windows, the correct URL is being passed to the
630 // Handoff.
631 IN_PROC_BROWSER_TEST_F(AppControllerHandoffBrowserTest, TestHandoffURLs) {
632   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
633   EXPECT_EQ(g_handoff_url, GURL(url::kAboutBlankURL));
635   // Test that navigating to a URL updates the handoff URL.
636   GURL test_url1 = embedded_test_server()->GetURL("/title1.html");
637   ui_test_utils::NavigateToURL(browser(), test_url1);
638   EXPECT_EQ(g_handoff_url, test_url1);
640   // Test that opening a new tab updates the handoff URL.
641   GURL test_url2 = embedded_test_server()->GetURL("/title2.html");
642   chrome::NavigateParams params(browser(), test_url2, ui::PAGE_TRANSITION_LINK);
643   params.disposition = NEW_FOREGROUND_TAB;
644   ui_test_utils::NavigateToURL(&params);
645   EXPECT_EQ(g_handoff_url, test_url2);
647   // Test that switching tabs updates the handoff URL.
648   browser()->tab_strip_model()->ActivateTabAt(0, true);
649   EXPECT_EQ(g_handoff_url, test_url1);
651   // Test that closing the current tab updates the handoff URL.
652   CloseTab(browser(), 0);
653   EXPECT_EQ(g_handoff_url, test_url2);
655   // Test that opening a new browser window updates the handoff URL.
656   GURL test_url3 = embedded_test_server()->GetURL("/title3.html");
657   ui_test_utils::NavigateToURLWithDisposition(
658       browser(), GURL(test_url3), NEW_WINDOW,
659       ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
660   EXPECT_EQ(g_handoff_url, test_url3);
662   // Check that there are exactly 2 browsers.
663   BrowserList* active_browser_list =
664       BrowserList::GetInstance(chrome::GetActiveDesktop());
665   EXPECT_EQ(2u, active_browser_list->size());
667   // Close the second browser window (which only has 1 tab left).
668   Browser* browser2 = active_browser_list->get(1);
669   CloseBrowserSynchronously(browser2);
670   EXPECT_EQ(g_handoff_url, test_url2);
672   // The URLs of incognito windows should not be passed to Handoff.
673   GURL test_url4 = embedded_test_server()->GetURL("/simple.html");
674   ui_test_utils::NavigateToURLWithDisposition(
675       browser(), GURL(test_url4), OFF_THE_RECORD,
676       ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
677   EXPECT_EQ(g_handoff_url, GURL());
679   // Open a new tab in the incognito window.
680   EXPECT_EQ(2u, active_browser_list->size());
681   Browser* browser3 = active_browser_list->get(1);
682   ui_test_utils::NavigateToURLWithDisposition(
683       browser3, test_url4, NEW_FOREGROUND_TAB,
684       ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
685   EXPECT_EQ(g_handoff_url, GURL());
687   // Navigate the current tab in the incognito window.
688   ui_test_utils::NavigateToURL(browser3, test_url1);
689   EXPECT_EQ(g_handoff_url, GURL());
691   // Activate the original browser window.
692   Browser* browser1 = active_browser_list->get(0);
693   browser1->window()->Show();
694   EXPECT_EQ(g_handoff_url, test_url2);
697 }  // namespace