Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / chrome / browser / app_controller_mac_browsertest.mm
blobb93aea0750a4d809db2eef0385e8eacf2e88a846
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/cocoa/run_loop_testing.h"
32 #include "chrome/browser/ui/host_desktop.h"
33 #include "chrome/browser/ui/tabs/tab_strip_model.h"
34 #include "chrome/browser/ui/user_manager.h"
35 #include "chrome/common/chrome_constants.h"
36 #include "chrome/common/chrome_switches.h"
37 #include "chrome/common/pref_names.h"
38 #include "chrome/common/url_constants.h"
39 #include "chrome/test/base/in_process_browser_test.h"
40 #include "chrome/test/base/ui_test_utils.h"
41 #include "components/bookmarks/browser/bookmark_model.h"
42 #include "components/bookmarks/test/bookmark_test_helpers.h"
43 #include "components/signin/core/common/profile_management_switches.h"
44 #include "content/public/browser/web_contents.h"
45 #include "content/public/test/browser_test_utils.h"
46 #include "content/public/test/test_navigation_observer.h"
47 #include "extensions/browser/app_window/app_window_registry.h"
48 #include "extensions/common/extension.h"
49 #include "extensions/test/extension_test_message_listener.h"
50 #include "net/test/embedded_test_server/embedded_test_server.h"
52 namespace {
54 GURL g_open_shortcut_url = GURL::EmptyGURL();
56 // Returns an Apple Event that instructs the application to open |url|.
57 NSAppleEventDescriptor* AppleEventToOpenUrl(const GURL& url) {
58   NSAppleEventDescriptor* shortcut_event = [[[NSAppleEventDescriptor alloc]
59       initWithEventClass:kASAppleScriptSuite
60                  eventID:kASSubroutineEvent
61         targetDescriptor:nil
62                 returnID:kAutoGenerateReturnID
63            transactionID:kAnyTransactionID] autorelease];
64   NSString* url_string = [NSString stringWithUTF8String:url.spec().c_str()];
65   [shortcut_event setParamDescriptor:[NSAppleEventDescriptor
66                                          descriptorWithString:url_string]
67                           forKeyword:keyDirectObject];
68   return shortcut_event;
71 // Instructs the NSApp's delegate to open |url|.
72 void SendAppleEventToOpenUrlToAppController(const GURL& url) {
73   AppController* controller =
74       base::mac::ObjCCast<AppController>([NSApp delegate]);
75   Method get_url =
76       class_getInstanceMethod([controller class], @selector(getUrl:withReply:));
78   ASSERT_TRUE(get_url);
80   NSAppleEventDescriptor* shortcut_event = AppleEventToOpenUrl(url);
82   method_invoke(controller, get_url, shortcut_event, NULL);
85 void RunClosureWhenProfileInitialized(const base::Closure& closure,
86                                       Profile* profile,
87                                       Profile::CreateStatus status) {
88   if (status == Profile::CREATE_STATUS_INITIALIZED)
89     closure.Run();
92 }  // namespace
94 @interface TestOpenShortcutOnStartup : NSObject
95 - (void)applicationWillFinishLaunching:(NSNotification*)notification;
96 @end
98 @implementation TestOpenShortcutOnStartup
100 - (void)applicationWillFinishLaunching:(NSNotification*)notification {
101   if (!g_open_shortcut_url.is_valid())
102     return;
104   SendAppleEventToOpenUrlToAppController(g_open_shortcut_url);
107 @end
109 namespace {
111 class AppControllerPlatformAppBrowserTest
112     : public extensions::PlatformAppBrowserTest {
113  protected:
114   AppControllerPlatformAppBrowserTest()
115       : active_browser_list_(BrowserList::GetInstance(
116                                 chrome::GetActiveDesktop())) {
117   }
119   void SetUpCommandLine(base::CommandLine* command_line) override {
120     PlatformAppBrowserTest::SetUpCommandLine(command_line);
121     command_line->AppendSwitchASCII(switches::kAppId,
122                                     "1234");
123   }
125   const BrowserList* active_browser_list_;
128 // Test that if only a platform app window is open and no browser windows are
129 // open then a reopen event does nothing.
130 IN_PROC_BROWSER_TEST_F(AppControllerPlatformAppBrowserTest,
131                        PlatformAppReopenWithWindows) {
132   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
133   NSUInteger old_window_count = [[NSApp windows] count];
134   EXPECT_EQ(1u, active_browser_list_->size());
135   [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:YES];
136   // We do not EXPECT_TRUE the result here because the method
137   // deminiaturizes windows manually rather than return YES and have
138   // AppKit do it.
140   EXPECT_EQ(old_window_count, [[NSApp windows] count]);
141   EXPECT_EQ(1u, active_browser_list_->size());
144 IN_PROC_BROWSER_TEST_F(AppControllerPlatformAppBrowserTest,
145                        ActivationFocusesBrowserWindow) {
146   base::scoped_nsobject<AppController> app_controller(
147       [[AppController alloc] init]);
149   ExtensionTestMessageListener listener("Launched", false);
150   const extensions::Extension* app =
151       InstallAndLaunchPlatformApp("minimal");
152   ASSERT_TRUE(listener.WaitUntilSatisfied());
154   NSWindow* app_window = extensions::AppWindowRegistry::Get(profile())
155                              ->GetAppWindowsForApp(app->id())
156                              .front()
157                              ->GetNativeWindow();
158   NSWindow* browser_window = browser()->window()->GetNativeWindow();
160   chrome::testing::NSRunLoopRunAllPending();
161   EXPECT_LE([[NSApp orderedWindows] indexOfObject:app_window],
162             [[NSApp orderedWindows] indexOfObject:browser_window]);
163   [app_controller applicationShouldHandleReopen:NSApp
164                               hasVisibleWindows:YES];
165   chrome::testing::NSRunLoopRunAllPending();
166   EXPECT_LE([[NSApp orderedWindows] indexOfObject:browser_window],
167             [[NSApp orderedWindows] indexOfObject:app_window]);
170 class AppControllerWebAppBrowserTest : public InProcessBrowserTest {
171  protected:
172   AppControllerWebAppBrowserTest()
173       : active_browser_list_(BrowserList::GetInstance(
174                                 chrome::GetActiveDesktop())) {
175   }
177   void SetUpCommandLine(base::CommandLine* command_line) override {
178     command_line->AppendSwitchASCII(switches::kApp, GetAppURL());
179   }
181   std::string GetAppURL() const {
182     return "http://example.com/";
183   }
185   const BrowserList* active_browser_list_;
188 // Test that in web app mode a reopen event opens the app URL.
189 IN_PROC_BROWSER_TEST_F(AppControllerWebAppBrowserTest,
190                        WebAppReopenWithNoWindows) {
191   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
192   EXPECT_EQ(1u, active_browser_list_->size());
193   BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
195   EXPECT_FALSE(result);
196   EXPECT_EQ(2u, active_browser_list_->size());
198   Browser* browser = active_browser_list_->get(0);
199   GURL current_url =
200       browser->tab_strip_model()->GetActiveWebContents()->GetURL();
201   EXPECT_EQ(GetAppURL(), current_url.spec());
204 // Called when the ProfileManager has created a profile.
205 void CreateProfileCallback(const base::Closure& quit_closure,
206                            Profile* profile,
207                            Profile::CreateStatus status) {
208   EXPECT_TRUE(profile);
209   EXPECT_NE(Profile::CREATE_STATUS_LOCAL_FAIL, status);
210   EXPECT_NE(Profile::CREATE_STATUS_REMOTE_FAIL, status);
211   // This will be called multiple times. Wait until the profile is initialized
212   // fully to quit the loop.
213   if (status == Profile::CREATE_STATUS_INITIALIZED)
214     quit_closure.Run();
217 void CreateAndWaitForSystemProfile() {
218   ProfileManager::CreateCallback create_callback =
219       base::Bind(&CreateProfileCallback,
220                  base::MessageLoop::current()->QuitClosure());
221   g_browser_process->profile_manager()->CreateProfileAsync(
222       ProfileManager::GetSystemProfilePath(),
223       create_callback,
224       base::string16(),
225       base::string16(),
226       std::string());
227   base::RunLoop().Run();
230 class AppControllerNewProfileManagementBrowserTest
231     : public InProcessBrowserTest {
232  protected:
233   AppControllerNewProfileManagementBrowserTest()
234       : active_browser_list_(BrowserList::GetInstance(
235                                 chrome::GetActiveDesktop())) {
236   }
238   void SetUpCommandLine(base::CommandLine* command_line) override {
239     switches::EnableNewProfileManagementForTesting(command_line);
240   }
242   const BrowserList* active_browser_list_;
245 // Test that for a regular last profile, a reopen event opens a browser.
246 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
247                        RegularProfileReopenWithNoWindows) {
248   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
249   EXPECT_EQ(1u, active_browser_list_->size());
250   BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
252   EXPECT_FALSE(result);
253   EXPECT_EQ(2u, active_browser_list_->size());
254   EXPECT_FALSE(UserManager::IsShowing());
257 // Test that for a locked last profile, a reopen event opens the User Manager.
258 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
259                        LockedProfileReopenWithNoWindows) {
260   // The User Manager uses the system profile as its underlying profile. To
261   // minimize flakiness due to the scheduling/descheduling of tasks on the
262   // different threads, pre-initialize the guest profile before it is needed.
263   CreateAndWaitForSystemProfile();
264   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
266   // Lock the active profile.
267   Profile* profile = [ac lastProfile];
268   ProfileInfoCache& cache =
269       g_browser_process->profile_manager()->GetProfileInfoCache();
270   size_t profile_index = cache.GetIndexOfProfileWithPath(profile->GetPath());
271   cache.SetProfileSigninRequiredAtIndex(profile_index, true);
272   EXPECT_TRUE(cache.ProfileIsSigninRequiredAtIndex(profile_index));
274   EXPECT_EQ(1u, active_browser_list_->size());
275   BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
276   EXPECT_FALSE(result);
278   base::RunLoop().RunUntilIdle();
279   EXPECT_EQ(1u, active_browser_list_->size());
280   EXPECT_TRUE(UserManager::IsShowing());
281   UserManager::Hide();
284 // Test that for a guest last profile, a reopen event opens the User Manager.
285 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
286                        GuestProfileReopenWithNoWindows) {
287   // Create the system profile. Set the guest as the last used profile so the
288   // app controller can use it on init.
289   CreateAndWaitForSystemProfile();
290   PrefService* local_state = g_browser_process->local_state();
291   local_state->SetString(prefs::kProfileLastUsed, chrome::kGuestProfileDir);
293   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
295   Profile* profile = [ac lastProfile];
296   EXPECT_EQ(ProfileManager::GetGuestProfilePath(), profile->GetPath());
297   EXPECT_TRUE(profile->IsGuestSession());
299   EXPECT_EQ(1u, active_browser_list_->size());
300   BOOL result = [ac applicationShouldHandleReopen:NSApp hasVisibleWindows:NO];
301   EXPECT_FALSE(result);
303   base::RunLoop().RunUntilIdle();
305   EXPECT_EQ(1u, active_browser_list_->size());
306   EXPECT_TRUE(UserManager::IsShowing());
307   UserManager::Hide();
310 IN_PROC_BROWSER_TEST_F(AppControllerNewProfileManagementBrowserTest,
311                        AboutChromeForcesUserManager) {
312   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
314   // Create the guest profile, and set it as the last used profile so the
315   // app controller can use it on init.
316   CreateAndWaitForSystemProfile();
317   PrefService* local_state = g_browser_process->local_state();
318   local_state->SetString(prefs::kProfileLastUsed, chrome::kGuestProfileDir);
320   // Prohibiting guest mode forces the user manager flow for About Chrome.
321   local_state->SetBoolean(prefs::kBrowserGuestModeEnabled, false);
323   Profile* guest_profile = [ac lastProfile];
324   EXPECT_EQ(ProfileManager::GetGuestProfilePath(), guest_profile->GetPath());
325   EXPECT_TRUE(guest_profile->IsGuestSession());
327   // Tell the browser to open About Chrome.
328   EXPECT_EQ(1u, active_browser_list_->size());
329   [ac orderFrontStandardAboutPanel:NSApp];
331   base::RunLoop().RunUntilIdle();
333   // No new browser is opened; the User Manager opens instead.
334   EXPECT_EQ(1u, active_browser_list_->size());
335   EXPECT_TRUE(UserManager::IsShowing());
337   UserManager::Hide();
340 class AppControllerOpenShortcutBrowserTest : public InProcessBrowserTest {
341  protected:
342   AppControllerOpenShortcutBrowserTest() {
343   }
345   void SetUpInProcessBrowserTestFixture() override {
346     // In order to mimic opening shortcut during browser startup, we need to
347     // send the event before -applicationDidFinishLaunching is called, but
348     // after AppController is loaded.
349     //
350     // Since -applicationWillFinishLaunching does nothing now, we swizzle it to
351     // our function to send the event. We need to do this early before running
352     // the main message loop.
353     //
354     // NSApp does not exist yet. We need to get the AppController using
355     // reflection.
356     Class appControllerClass = NSClassFromString(@"AppController");
357     Class openShortcutClass = NSClassFromString(@"TestOpenShortcutOnStartup");
359     ASSERT_TRUE(appControllerClass != nil);
360     ASSERT_TRUE(openShortcutClass != nil);
362     SEL targetMethod = @selector(applicationWillFinishLaunching:);
363     Method original = class_getInstanceMethod(appControllerClass,
364         targetMethod);
365     Method destination = class_getInstanceMethod(openShortcutClass,
366         targetMethod);
368     ASSERT_TRUE(original != NULL);
369     ASSERT_TRUE(destination != NULL);
371     method_exchangeImplementations(original, destination);
373     ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
374     g_open_shortcut_url = embedded_test_server()->GetURL("/simple.html");
375   }
377   void SetUpCommandLine(base::CommandLine* command_line) override {
378     // If the arg is empty, PrepareTestCommandLine() after this function will
379     // append about:blank as default url.
380     command_line->AppendArg(chrome::kChromeUINewTabURL);
381   }
384 IN_PROC_BROWSER_TEST_F(AppControllerOpenShortcutBrowserTest,
385                        OpenShortcutOnStartup) {
386   EXPECT_EQ(1, browser()->tab_strip_model()->count());
387   EXPECT_EQ(g_open_shortcut_url,
388       browser()->tab_strip_model()->GetActiveWebContents()
389           ->GetLastCommittedURL());
392 class AppControllerReplaceNTPBrowserTest : public InProcessBrowserTest {
393  protected:
394   AppControllerReplaceNTPBrowserTest() {}
396   void SetUpInProcessBrowserTestFixture() override {
397     ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
398   }
400   void SetUpCommandLine(base::CommandLine* command_line) override {
401     // If the arg is empty, PrepareTestCommandLine() after this function will
402     // append about:blank as default url.
403     command_line->AppendArg(chrome::kChromeUINewTabURL);
404   }
407 // Tests that when a GURL is opened after startup, it replaces the NTP.
408 IN_PROC_BROWSER_TEST_F(AppControllerReplaceNTPBrowserTest,
409                        ReplaceNTPAfterStartup) {
410   // Ensure that there is exactly 1 tab showing, and the tab is the NTP.
411   GURL ntp(chrome::kChromeUINewTabURL);
412   EXPECT_EQ(1, browser()->tab_strip_model()->count());
413   EXPECT_EQ(ntp,
414             browser()
415                 ->tab_strip_model()
416                 ->GetActiveWebContents()
417                 ->GetLastCommittedURL());
419   GURL simple(embedded_test_server()->GetURL("/simple.html"));
420   SendAppleEventToOpenUrlToAppController(simple);
422   // Wait for one navigation on the active web contents.
423   EXPECT_EQ(1, browser()->tab_strip_model()->count());
424   content::TestNavigationObserver obs(
425       browser()->tab_strip_model()->GetActiveWebContents(), 1);
426   obs.Wait();
428   EXPECT_EQ(simple,
429             browser()
430                 ->tab_strip_model()
431                 ->GetActiveWebContents()
432                 ->GetLastCommittedURL());
435 class AppControllerMainMenuBrowserTest : public InProcessBrowserTest {
436  protected:
437   AppControllerMainMenuBrowserTest() {
438   }
441 IN_PROC_BROWSER_TEST_F(AppControllerMainMenuBrowserTest,
442     HistoryMenuResetAfterProfileDeletion) {
443   ProfileManager* profile_manager = g_browser_process->profile_manager();
444   AppController* ac = [NSApp delegate];
446   // Use the existing profile as profile 1.
447   Profile* profile1 = browser()->profile();
449   // Create profile 2.
450   base::FilePath profile2_path =
451       profile_manager->GenerateNextProfileDirectoryPath();
452   base::RunLoop run_loop;
453   profile_manager->CreateProfileAsync(
454       profile2_path,
455       base::Bind(&RunClosureWhenProfileInitialized,
456                  run_loop.QuitClosure()),
457                  base::string16(),
458                  base::string16(),
459                  std::string());
460   run_loop.Run();
461   Profile* profile2 = profile_manager->GetProfileByPath(profile2_path);
462   ASSERT_TRUE(profile2);
464   // Switch the controller to profile1.
465   [ac windowChangedToProfile:profile1];
466   base::RunLoop().RunUntilIdle();
468   // Verify the controller's History Menu corresponds to profile1.
469   EXPECT_TRUE([ac historyMenuBridge]->service());
470   EXPECT_EQ([ac historyMenuBridge]->service(),
471       HistoryServiceFactory::GetForProfile(profile1,
472                                            ServiceAccessType::EXPLICIT_ACCESS));
474   // Load profile2's History Service backend so it will be assigned to the
475   // HistoryMenuBridge when windowChangedToProfile is called, or else this test
476   // will fail flaky.
477   ui_test_utils::WaitForHistoryToLoad(
478       HistoryServiceFactory::GetForProfile(profile2,
479                                            ServiceAccessType::EXPLICIT_ACCESS));
480   // Switch the controller to profile2.
481   [ac windowChangedToProfile:profile2];
482   base::RunLoop().RunUntilIdle();
484   // Verify the controller's History Menu has changed.
485   EXPECT_TRUE([ac historyMenuBridge]->service());
486   EXPECT_EQ([ac historyMenuBridge]->service(),
487       HistoryServiceFactory::GetForProfile(profile2,
488                                            ServiceAccessType::EXPLICIT_ACCESS));
489   EXPECT_NE(
490       HistoryServiceFactory::GetForProfile(profile1,
491                                            ServiceAccessType::EXPLICIT_ACCESS),
492       HistoryServiceFactory::GetForProfile(profile2,
493                                            ServiceAccessType::EXPLICIT_ACCESS));
495   // Delete profile2.
496   profile_manager->ScheduleProfileForDeletion(
497       profile2->GetPath(), ProfileManager::CreateCallback());
498   base::RunLoop().RunUntilIdle();
500   // Verify the controller's history is back to profile1.
501   EXPECT_EQ([ac historyMenuBridge]->service(),
502       HistoryServiceFactory::GetForProfile(profile1,
503                                            ServiceAccessType::EXPLICIT_ACCESS));
506 IN_PROC_BROWSER_TEST_F(AppControllerMainMenuBrowserTest,
507     BookmarksMenuIsRestoredAfterProfileSwitch) {
508   ProfileManager* profile_manager = g_browser_process->profile_manager();
509   base::scoped_nsobject<AppController> ac([[AppController alloc] init]);
510   [ac awakeFromNib];
512   // Constants for bookmarks that we will create later.
513   const base::string16 title1(base::ASCIIToUTF16("Dinosaur Comics"));
514   const GURL url1("http://qwantz.com//");
516   const base::string16 title2(base::ASCIIToUTF16("XKCD"));
517   const GURL url2("https://www.xkcd.com/");
519   // Use the existing profile as profile 1.
520   Profile* profile1 = browser()->profile();
521   bookmarks::test::WaitForBookmarkModelToLoad(
522       BookmarkModelFactory::GetForProfile(profile1));
524   // Create profile 2.
525   base::FilePath path2 = profile_manager->GenerateNextProfileDirectoryPath();
526   Profile* profile2 =
527       Profile::CreateProfile(path2, NULL, Profile::CREATE_MODE_SYNCHRONOUS);
528   profile_manager->RegisterTestingProfile(profile2, false, true);
529   bookmarks::test::WaitForBookmarkModelToLoad(
530       BookmarkModelFactory::GetForProfile(profile2));
532   // Switch to profile 1, create bookmark 1 and force the menu to build.
533   [ac windowChangedToProfile:profile1];
534   [ac bookmarkMenuBridge]->GetBookmarkModel()->AddURL(
535       [ac bookmarkMenuBridge]->GetBookmarkModel()->bookmark_bar_node(),
536       0, title1, url1);
537   [ac bookmarkMenuBridge]->BuildMenu();
539   // Switch to profile 2, create bookmark 2 and force the menu to build.
540   [ac windowChangedToProfile:profile2];
541   [ac bookmarkMenuBridge]->GetBookmarkModel()->AddURL(
542       [ac bookmarkMenuBridge]->GetBookmarkModel()->bookmark_bar_node(),
543       0, title2, url2);
544   [ac bookmarkMenuBridge]->BuildMenu();
546   // Test that only bookmark 2 is shown.
547   EXPECT_FALSE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
548       SysUTF16ToNSString(title1)]);
549   EXPECT_TRUE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
550       SysUTF16ToNSString(title2)]);
552   // Switch *back* to profile 1 and *don't* force the menu to build.
553   [ac windowChangedToProfile:profile1];
555   // Test that only bookmark 1 is shown in the restored menu.
556   EXPECT_TRUE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
557       SysUTF16ToNSString(title1)]);
558   EXPECT_FALSE([[ac bookmarkMenuBridge]->BookmarkMenu() itemWithTitle:
559       SysUTF16ToNSString(title2)]);
562 }  // namespace
564 //--------------------------AppControllerHandoffBrowserTest---------------------
566 static GURL g_handoff_url;
568 @interface AppController (BrowserTest)
569 - (BOOL)new_shouldUseHandoff;
570 - (void)new_passURLToHandoffManager:(const GURL&)handoffURL;
571 @end
573 @implementation AppController (BrowserTest)
574 - (BOOL)new_shouldUseHandoff {
575   return YES;
578 - (void)new_passURLToHandoffManager:(const GURL&)handoffURL {
579   g_handoff_url = handoffURL;
581 @end
583 namespace {
585 class AppControllerHandoffBrowserTest : public InProcessBrowserTest {
586  protected:
587   AppControllerHandoffBrowserTest() {}
589   // Exchanges the implementations of the two selectors on the class
590   // AppController.
591   void ExchangeSelectors(SEL originalMethod, SEL newMethod) {
592     Class appControllerClass = NSClassFromString(@"AppController");
594     ASSERT_TRUE(appControllerClass != nil);
596     Method original =
597         class_getInstanceMethod(appControllerClass, originalMethod);
598     Method destination = class_getInstanceMethod(appControllerClass, newMethod);
600     ASSERT_TRUE(original != NULL);
601     ASSERT_TRUE(destination != NULL);
603     method_exchangeImplementations(original, destination);
604   }
606   // Swizzle Handoff related implementations.
607   void SetUpInProcessBrowserTestFixture() override {
608     // Handoff is only available on OSX 10.10+. This swizzle makes the logic
609     // run on all OSX versions.
610     SEL originalMethod = @selector(shouldUseHandoff);
611     SEL newMethod = @selector(new_shouldUseHandoff);
612     ExchangeSelectors(originalMethod, newMethod);
614     // This swizzle intercepts the URL that would be sent to the Handoff
615     // Manager, and instead puts it into a variable accessible to this test.
616     originalMethod = @selector(passURLToHandoffManager:);
617     newMethod = @selector(new_passURLToHandoffManager:);
618     ExchangeSelectors(originalMethod, newMethod);
619   }
621   // Closes the tab, and waits for the close to finish.
622   void CloseTab(Browser* browser, int index) {
623     content::WebContentsDestroyedWatcher destroyed_watcher(
624         browser->tab_strip_model()->GetWebContentsAt(index));
625     browser->tab_strip_model()->CloseWebContentsAt(
626         index, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
627     destroyed_watcher.Wait();
628   }
631 // Tests that as a user switches between tabs, navigates within a tab, and
632 // switches between browser windows, the correct URL is being passed to the
633 // Handoff.
634 IN_PROC_BROWSER_TEST_F(AppControllerHandoffBrowserTest, TestHandoffURLs) {
635   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
636   EXPECT_EQ(g_handoff_url, GURL(url::kAboutBlankURL));
638   // Test that navigating to a URL updates the handoff URL.
639   GURL test_url1 = embedded_test_server()->GetURL("/title1.html");
640   ui_test_utils::NavigateToURL(browser(), test_url1);
641   EXPECT_EQ(g_handoff_url, test_url1);
643   // Test that opening a new tab updates the handoff URL.
644   GURL test_url2 = embedded_test_server()->GetURL("/title2.html");
645   chrome::NavigateParams params(browser(), test_url2, ui::PAGE_TRANSITION_LINK);
646   params.disposition = NEW_FOREGROUND_TAB;
647   ui_test_utils::NavigateToURL(&params);
648   EXPECT_EQ(g_handoff_url, test_url2);
650   // Test that switching tabs updates the handoff URL.
651   browser()->tab_strip_model()->ActivateTabAt(0, true);
652   EXPECT_EQ(g_handoff_url, test_url1);
654   // Test that closing the current tab updates the handoff URL.
655   CloseTab(browser(), 0);
656   EXPECT_EQ(g_handoff_url, test_url2);
658   // Test that opening a new browser window updates the handoff URL.
659   GURL test_url3 = embedded_test_server()->GetURL("/title3.html");
660   ui_test_utils::NavigateToURLWithDisposition(
661       browser(), GURL(test_url3), NEW_WINDOW,
662       ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
663   EXPECT_EQ(g_handoff_url, test_url3);
665   // Check that there are exactly 2 browsers.
666   BrowserList* active_browser_list =
667       BrowserList::GetInstance(chrome::GetActiveDesktop());
668   EXPECT_EQ(2u, active_browser_list->size());
670   // Close the second browser window (which only has 1 tab left).
671   Browser* browser2 = active_browser_list->get(1);
672   CloseBrowserSynchronously(browser2);
673   EXPECT_EQ(g_handoff_url, test_url2);
675   // The URLs of incognito windows should not be passed to Handoff.
676   GURL test_url4 = embedded_test_server()->GetURL("/simple.html");
677   ui_test_utils::NavigateToURLWithDisposition(
678       browser(), GURL(test_url4), OFF_THE_RECORD,
679       ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
680   EXPECT_EQ(g_handoff_url, GURL());
682   // Open a new tab in the incognito window.
683   EXPECT_EQ(2u, active_browser_list->size());
684   Browser* browser3 = active_browser_list->get(1);
685   ui_test_utils::NavigateToURLWithDisposition(
686       browser3, test_url4, NEW_FOREGROUND_TAB,
687       ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
688   EXPECT_EQ(g_handoff_url, GURL());
690   // Navigate the current tab in the incognito window.
691   ui_test_utils::NavigateToURL(browser3, test_url1);
692   EXPECT_EQ(g_handoff_url, GURL());
694   // Activate the original browser window.
695   Browser* browser1 = active_browser_list->get(0);
696   browser1->window()->Show();
697   EXPECT_EQ(g_handoff_url, test_url2);
700 }  // namespace