Commit 600f7962 authored by erikchen's avatar erikchen Committed by Commit bot

Reland 1: "mac: Allow Chrome to hand off its active URL to other devices."

The original CL used instance variables in a class extension and automatic
generation of ivars for synthesized properties, features only available on
64-bit builds. The Mac Memory bots are still compiling Chromium in 32-bits.
This reland removes the usage of those features.

> This CL adds the class HandoffManager, which is responsible for interfacing
> with Apple's Handoff APIs. It takes a GURL, and exposes that GURL to Handoff.
>
> This CL adds the class ActiveWebContentsObserver, which is responsible for
> listening to changes to the active browser, the active tab, and the visible
> URL. It notifies its delegate when any of this state might have changed.
>
> AppControllerMac is the delegate of ActiveWebContentsObserver, as well as the
> owner of the HandoffManager. When it receives a delegate callback, it passes an
> updated GURL to the HandoffManager. There is some minimal logic in
> AppControllerMac that prevents URLs from incognito windows from being passed to
> the HandoffManager.
>
> BUG=431051, 438823
> Committed: https://crrev.com/708abc5b0abb5e0916d779bf6d1342fd472a2aa1
> Cr-Commit-Position: refs/heads/master@{#307846}

BUG=431051, 438823
TBR=sky, erikwright, mmenke, avi

Review URL: https://codereview.chromium.org/794853004

Cr-Commit-Position: refs/heads/master@{#308005}
parent d01af9d9
......@@ -334,6 +334,10 @@ BASE_EXPORT extern "C" NSString* const kCWSSIDDidChangeNotification;
@property (copy) NSDictionary* userInfo;
@property (copy) NSURL* webpageURL;
- (instancetype)initWithActivityType:(NSString*)activityType;
- (void)becomeCurrent;
- (void)invalidate;
@end
BASE_EXPORT extern "C" NSString* const NSUserActivityTypeBrowsingWeb;
......
......@@ -22,6 +22,8 @@ class AppControllerProfileObserver;
class BookmarkMenuBridge;
class CommandUpdater;
class GURL;
class HandoffActiveURLObserverBridge;
@class HandoffManager;
class HistoryMenuBridge;
class Profile;
@class ProfileMenuController;
......@@ -97,6 +99,13 @@ class WorkAreaWatcherObserver;
// Displays a notification when quitting while apps are running.
scoped_refptr<QuitWithAppsController> quitWithAppsController_;
// Responsible for maintaining all state related to the Handoff feature.
base::scoped_nsobject<HandoffManager> handoffManager_;
// Observes changes to the active URL.
scoped_ptr<HandoffActiveURLObserverBridge>
handoff_active_url_observer_bridge_;
}
@property(readonly, nonatomic) BOOL startupComplete;
......
......@@ -63,6 +63,7 @@
#import "chrome/browser/ui/cocoa/confirm_quit.h"
#import "chrome/browser/ui/cocoa/confirm_quit_panel_controller.h"
#import "chrome/browser/ui/cocoa/encoding_menu_controller_delegate_mac.h"
#include "chrome/browser/ui/cocoa/handoff_active_url_observer_bridge.h"
#import "chrome/browser/ui/cocoa/history_menu_bridge.h"
#include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h"
#import "chrome/browser/ui/cocoa/profiles/profile_menu_controller.h"
......@@ -83,6 +84,7 @@
#include "chrome/common/url_constants.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/handoff/handoff_manager.h"
#include "components/handoff/handoff_utility.h"
#include "components/signin/core/browser/signin_manager.h"
#include "components/signin/core/common/profile_management_switches.h"
......@@ -209,9 +211,10 @@ bool IsProfileSignedOut(Profile* profile) {
return cache.ProfileIsSigninRequiredAtIndex(profile_index);
}
} // anonymous namespace
} // namespace
@interface AppController () <HandoffActiveURLObserverBridgeDelegate>
@interface AppController (Private)
- (void)initMenuState;
- (void)initProfileMenu;
- (void)updateConfirmToQuitPrefMenuItem:(NSMenuItem*)item;
......@@ -240,6 +243,25 @@ bool IsProfileSignedOut(Profile* profile) {
// this method is called, and that tab is the NTP, then this method closes the
// NTP after all the |urls| have been opened.
- (void)openUrlsReplacingNTP:(const std::vector<GURL>&)urls;
// Whether instances of this class should use the Handoff feature.
- (BOOL)shouldUseHandoff;
// This method passes |handoffURL| to |handoffManager_|.
- (void)passURLToHandoffManager:(const GURL&)handoffURL;
// Lazily creates the Handoff Manager. Updates the state of the Handoff
// Manager. This method is idempotent. This should be called:
// - During initialization.
// - When the current tab navigates to a new URL.
// - When the active browser changes.
// - When the active browser's active tab switches.
// |webContents| should be the new, active WebContents.
- (void)updateHandoffManager:(content::WebContents*)webContents;
// Given |webContents|, extracts a GURL to be used for Handoff. This may return
// the empty GURL.
- (GURL)handoffURLFromWebContents:(content::WebContents*)webContents;
@end
class AppControllerProfileObserver : public ProfileInfoCacheObserver {
......@@ -776,6 +798,12 @@ class AppControllerProfileObserver : public ProfileInfoCacheObserver {
startupComplete_ = YES;
Browser* browser =
FindLastActiveWithHostDesktopType(chrome::HOST_DESKTOP_TYPE_NATIVE);
content::WebContents* activeWebContents = nullptr;
if (browser)
activeWebContents = browser->tab_strip_model()->GetActiveWebContents();
[self updateHandoffManager:activeWebContents];
[self openStartupUrls];
PrefService* localState = g_browser_process->local_state();
......@@ -786,6 +814,9 @@ class AppControllerProfileObserver : public ProfileInfoCacheObserver {
base::Bind(&chrome::BrowserCommandController::UpdateOpenFileState,
menuState_.get()));
}
handoff_active_url_observer_bridge_.reset(
new HandoffActiveURLObserverBridge(self));
}
// This is called after profiles have been loaded and preferences registered.
......@@ -1638,6 +1669,53 @@ class AppControllerProfileObserver : public ProfileInfoCacheObserver {
error:(NSError*)error {
}
#pragma mark - Handoff Manager
- (BOOL)shouldUseHandoff {
return base::mac::IsOSYosemiteOrLater();
}
- (void)passURLToHandoffManager:(const GURL&)handoffURL {
[handoffManager_ updateActiveURL:handoffURL];
}
- (void)updateHandoffManager:(content::WebContents*)webContents {
if (![self shouldUseHandoff])
return;
if (!handoffManager_)
handoffManager_.reset([[HandoffManager alloc] init]);
GURL handoffURL = [self handoffURLFromWebContents:webContents];
[self passURLToHandoffManager:handoffURL];
}
- (GURL)handoffURLFromWebContents:(content::WebContents*)webContents {
if (!webContents)
return GURL();
Profile* profile =
Profile::FromBrowserContext(webContents->GetBrowserContext());
if (!profile)
return GURL();
// Handoff is not allowed from an incognito profile. To err on the safe side,
// also disallow Handoff from a guest profile.
if (profile->GetProfileType() != Profile::REGULAR_PROFILE)
return GURL();
if (!webContents)
return GURL();
return webContents->GetVisibleURL();
}
#pragma mark - HandoffActiveURLObserverBridgeDelegate
- (void)handoffActiveURLChanged:(content::WebContents*)webContents {
[self updateHandoffManager:webContents];
}
@end // @implementation AppController
//---------------------------------------------------------------------------
......
......@@ -35,9 +35,11 @@
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/bookmarks/test/bookmark_test_helpers.h"
#include "components/signin/core/common/profile_management_switches.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "extensions/browser/app_window/app_window_registry.h"
#include "extensions/common/extension.h"
......@@ -481,3 +483,144 @@ IN_PROC_BROWSER_TEST_F(AppControllerMainMenuBrowserTest,
}
} // namespace
//--------------------------AppControllerHandoffBrowserTest---------------------
static GURL g_handoff_url;
@interface AppController (BrowserTest)
- (BOOL)new_shouldUseHandoff;
- (void)new_passURLToHandoffManager:(const GURL&)handoffURL;
@end
@implementation AppController (BrowserTest)
- (BOOL)new_shouldUseHandoff {
return YES;
}
- (void)new_passURLToHandoffManager:(const GURL&)handoffURL {
g_handoff_url = handoffURL;
}
@end
namespace {
class AppControllerHandoffBrowserTest : public InProcessBrowserTest {
protected:
AppControllerHandoffBrowserTest() {}
// Exchanges the implementations of the two selectors on the class
// AppController.
void ExchangeSelectors(SEL originalMethod, SEL newMethod) {
Class appControllerClass = NSClassFromString(@"AppController");
ASSERT_TRUE(appControllerClass != nil);
Method original =
class_getInstanceMethod(appControllerClass, originalMethod);
Method destination = class_getInstanceMethod(appControllerClass, newMethod);
ASSERT_TRUE(original != NULL);
ASSERT_TRUE(destination != NULL);
method_exchangeImplementations(original, destination);
}
// Swizzle Handoff related implementations.
void SetUpInProcessBrowserTestFixture() override {
// Handoff is only available on OSX 10.10+. This swizzle makes the logic
// run on all OSX versions.
SEL originalMethod = @selector(shouldUseHandoff);
SEL newMethod = @selector(new_shouldUseHandoff);
ExchangeSelectors(originalMethod, newMethod);
// This swizzle intercepts the URL that would be sent to the Handoff
// Manager, and instead puts it into a variable accessible to this test.
originalMethod = @selector(passURLToHandoffManager:);
newMethod = @selector(new_passURLToHandoffManager:);
ExchangeSelectors(originalMethod, newMethod);
}
// Closes the tab, and waits for the close to finish.
void CloseTab(Browser* browser, int index) {
content::WebContentsDestroyedWatcher destroyed_watcher(
browser->tab_strip_model()->GetWebContentsAt(index));
browser->tab_strip_model()->CloseWebContentsAt(
index, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
destroyed_watcher.Wait();
}
};
// Tests that as a user switches between tabs, navigates within a tab, and
// switches between browser windows, the correct URL is being passed to the
// Handoff.
IN_PROC_BROWSER_TEST_F(AppControllerHandoffBrowserTest, TestHandoffURLs) {
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
EXPECT_EQ(g_handoff_url, GURL(url::kAboutBlankURL));
// Test that navigating to a URL updates the handoff URL.
GURL test_url1 = embedded_test_server()->GetURL("/title1.html");
ui_test_utils::NavigateToURL(browser(), test_url1);
EXPECT_EQ(g_handoff_url, test_url1);
// Test that opening a new tab updates the handoff URL.
GURL test_url2 = embedded_test_server()->GetURL("/title2.html");
chrome::NavigateParams params(browser(), test_url2, ui::PAGE_TRANSITION_LINK);
params.disposition = NEW_FOREGROUND_TAB;
ui_test_utils::NavigateToURL(&params);
EXPECT_EQ(g_handoff_url, test_url2);
// Test that switching tabs updates the handoff URL.
browser()->tab_strip_model()->ActivateTabAt(0, true);
EXPECT_EQ(g_handoff_url, test_url1);
// Test that closing the current tab updates the handoff URL.
CloseTab(browser(), 0);
EXPECT_EQ(g_handoff_url, test_url2);
// Test that opening a new browser window updates the handoff URL.
GURL test_url3 = embedded_test_server()->GetURL("/title3.html");
ui_test_utils::NavigateToURLWithDisposition(
browser(), GURL(test_url3), NEW_WINDOW,
ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
EXPECT_EQ(g_handoff_url, test_url3);
// Check that there are exactly 2 browsers.
BrowserList* active_browser_list =
BrowserList::GetInstance(chrome::GetActiveDesktop());
EXPECT_EQ(2u, active_browser_list->size());
// Close the one and only tab for the second browser window.
Browser* browser2 = active_browser_list->get(1);
CloseTab(browser2, 0);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(g_handoff_url, test_url2);
// The URLs of incognito windows should not be passed to Handoff.
GURL test_url4 = embedded_test_server()->GetURL("/simple.html");
ui_test_utils::NavigateToURLWithDisposition(
browser(), GURL(test_url4), OFF_THE_RECORD,
ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
EXPECT_EQ(g_handoff_url, GURL());
// Open a new tab in the incognito window.
EXPECT_EQ(2u, active_browser_list->size());
Browser* browser3 = active_browser_list->get(1);
ui_test_utils::NavigateToURLWithDisposition(
browser3, test_url4, NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
EXPECT_EQ(g_handoff_url, GURL());
// Navigate the current tab in the incognito window.
ui_test_utils::NavigateToURLWithDisposition(
browser3, test_url1, CURRENT_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
EXPECT_EQ(g_handoff_url, GURL());
// Activate the original browser window.
Browser* browser1 = active_browser_list->get(0);
browser1->window()->Show();
EXPECT_EQ(g_handoff_url, test_url2);
}
} // namespace
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/cocoa/handoff_active_url_observer.h"
#include "base/logging.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/cocoa/handoff_active_url_observer_delegate.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "content/public/browser/web_contents.h"
HandoffActiveURLObserver::HandoffActiveURLObserver(
HandoffActiveURLObserverDelegate* delegate)
: delegate_(delegate),
active_tab_strip_model_(nullptr),
active_browser_(nullptr) {
DCHECK(delegate_);
active_browser_ = chrome::FindLastActiveWithHostDesktopType(
chrome::HOST_DESKTOP_TYPE_NATIVE);
BrowserList::AddObserver(this);
UpdateObservations();
}
HandoffActiveURLObserver::~HandoffActiveURLObserver() {
BrowserList::RemoveObserver(this);
StopObservingTabStripModel();
StopObservingWebContents();
}
void HandoffActiveURLObserver::OnBrowserSetLastActive(Browser* browser) {
active_browser_ = browser;
UpdateObservations();
delegate_->HandoffActiveURLChanged(GetActiveWebContents());
}
void HandoffActiveURLObserver::OnBrowserRemoved(Browser* removed_browser) {
if (active_browser_ != removed_browser)
return;
active_browser_ = chrome::FindLastActiveWithHostDesktopType(
chrome::HOST_DESKTOP_TYPE_NATIVE);
UpdateObservations();
delegate_->HandoffActiveURLChanged(GetActiveWebContents());
}
void HandoffActiveURLObserver::ActiveTabChanged(
content::WebContents* old_contents,
content::WebContents* new_contents,
int index,
int reason) {
StartObservingWebContents(new_contents);
delegate_->HandoffActiveURLChanged(new_contents);
}
void HandoffActiveURLObserver::TabStripModelDeleted() {
StopObservingTabStripModel();
StopObservingWebContents();
}
void HandoffActiveURLObserver::DidNavigateMainFrame(
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) {
delegate_->HandoffActiveURLChanged(web_contents());
}
void HandoffActiveURLObserver::WebContentsDestroyed() {
StopObservingWebContents();
}
void HandoffActiveURLObserver::UpdateObservations() {
if (!active_browser_) {
StopObservingTabStripModel();
StopObservingWebContents();
return;
}
TabStripModel* model = active_browser_->tab_strip_model();
StartObservingTabStripModel(model);
content::WebContents* web_contents = model->GetActiveWebContents();
if (web_contents)
StartObservingWebContents(web_contents);
else
StopObservingWebContents();
}
void HandoffActiveURLObserver::StartObservingTabStripModel(
TabStripModel* tab_strip_model) {
DCHECK(tab_strip_model);
if (active_tab_strip_model_ == tab_strip_model)
return;
StopObservingTabStripModel();
tab_strip_model->AddObserver(this);
active_tab_strip_model_ = tab_strip_model;
}
void HandoffActiveURLObserver::StopObservingTabStripModel() {
if (active_tab_strip_model_) {
active_tab_strip_model_->RemoveObserver(this);
active_tab_strip_model_ = nullptr;
}
}
void HandoffActiveURLObserver::StartObservingWebContents(
content::WebContents* web_contents) {
DCHECK(web_contents);
Observe(web_contents);
}
void HandoffActiveURLObserver::StopObservingWebContents() {
Observe(nullptr);
}
content::WebContents* HandoffActiveURLObserver::GetActiveWebContents() {
if (!active_browser_)
return nullptr;
return active_browser_->tab_strip_model()->GetActiveWebContents();
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_COCOA_HANDOFF_ACTIVE_URL_OBSERVER_H_
#define CHROME_BROWSER_UI_COCOA_HANDOFF_ACTIVE_URL_OBSERVER_H_
#include "chrome/browser/ui/browser_list_observer.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "content/public/browser/web_contents_observer.h"
namespace content {
class WebContents;
}
class Browser;
class HandoffActiveURLObserverDelegate;
class TabStripModel;
// This class observes changes to the "active URL". This is defined as the
// visible URL of the WebContents of the selected tab of the most recently
// focused browser window.
class HandoffActiveURLObserver : public chrome::BrowserListObserver,
public TabStripModelObserver,
public content::WebContentsObserver {
public:
explicit HandoffActiveURLObserver(HandoffActiveURLObserverDelegate* delegate);
~HandoffActiveURLObserver() override;
private:
// chrome::BrowserListObserver
void OnBrowserSetLastActive(Browser* browser) override;
void OnBrowserRemoved(Browser* browser) override;
// TabStripModelObserver
void ActiveTabChanged(content::WebContents* old_contents,
content::WebContents* new_contents,
int index,
int reason) override;
void TabStripModelDeleted() override;
// content::WebContentsObserver
void DidNavigateMainFrame(
const content::LoadCommittedDetails& details,
const content::FrameNavigateParams& params) override;
void WebContentsDestroyed() override;
// This method ensures that the instance is registered as an observer of the
// correct TabStripModel and WebContents for |active_browser_|.
void UpdateObservations();
// Makes this object start observing the TabStripModel, if it is not already
// doing so. This method is idempotent.
void StartObservingTabStripModel(TabStripModel* tab_strip_model);
// Makes this object stop observing the TabStripModel.
void StopObservingTabStripModel();
// Makes this object start observing the WebContents, if it is not already
// doing so. This method is idempotent.
void StartObservingWebContents(content::WebContents* web_contents);
// Makes this object stop observing the WebContents.
void StopObservingWebContents();
// Returns the active WebContents. May return nullptr.
content::WebContents* GetActiveWebContents();
// Instances of this class should be owned by their |delegate_|.
HandoffActiveURLObserverDelegate* delegate_;
// When this pointer is not nullptr, this object is registered as an observer
// of the TabStripModel.
TabStripModel* active_tab_strip_model_;
// This pointer is always up to date, and points to the most recently
// activated browser, or nullptr if no browsers exist.
Browser* active_browser_;
DISALLOW_COPY_AND_ASSIGN(HandoffActiveURLObserver);
};
#endif // CHROME_BROWSER_UI_COCOA_HANDOFF_ACTIVE_URL_OBSERVER_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_COCOA_HANDOFF_ACTIVE_URL_OBSERVER_BRIDGE_H_
#define CHROME_BROWSER_UI_COCOA_HANDOFF_ACTIVE_URL_OBSERVER_BRIDGE_H_
#import <Cocoa/Cocoa.h>
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/ui/cocoa/handoff_active_url_observer_delegate.h"
namespace content {
class WebContents;
}
class HandoffActiveURLObserver;
// A protocol that allows ObjC objects to receive delegate callbacks from
// HandoffActiveURLObserver.
@protocol HandoffActiveURLObserverBridgeDelegate
- (void)handoffActiveURLChanged:(content::WebContents*)webContents;
@end
// This class allows an ObjC object to receive the delegate callbacks from an
// HandoffActiveURLObserver.
class HandoffActiveURLObserverBridge : public HandoffActiveURLObserverDelegate {
public:
explicit HandoffActiveURLObserverBridge(
NSObject<HandoffActiveURLObserverBridgeDelegate>* delegate);
~HandoffActiveURLObserverBridge() override;
private:
void HandoffActiveURLChanged(content::WebContents* web_contents) override;
// Instances of this class should be owned by their |delegate_|.
NSObject<HandoffActiveURLObserverBridgeDelegate>* delegate_;
// The C++ object that this class acts as a bridge for.
scoped_ptr<HandoffActiveURLObserver> observer_;
DISALLOW_COPY_AND_ASSIGN(HandoffActiveURLObserverBridge);
};
#endif // CHROME_BROWSER_UI_COCOA_HANDOFF_ACTIVE_URL_OBSERVER_BRIDGE_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/cocoa/handoff_active_url_observer_bridge.h"
#include "chrome/browser/ui/cocoa/handoff_active_url_observer.h"
HandoffActiveURLObserverBridge::HandoffActiveURLObserverBridge(
NSObject<HandoffActiveURLObserverBridgeDelegate>* delegate)
: delegate_(delegate) {
DCHECK(delegate_);
observer_.reset(new HandoffActiveURLObserver(this));
}
HandoffActiveURLObserverBridge::~HandoffActiveURLObserverBridge(){};
void HandoffActiveURLObserverBridge::HandoffActiveURLChanged(
content::WebContents* web_contents) {
[delegate_ handoffActiveURLChanged:web_contents];
}
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_COCOA_HANDOFF_ACTIVE_URL_OBSERVER_DELEGATE_H_
#define CHROME_BROWSER_UI_COCOA_HANDOFF_ACTIVE_URL_OBSERVER_DELEGATE_H_
namespace content {
class WebContents;
}
// The delegate for a HandoffActiveURLObserver.
class HandoffActiveURLObserverDelegate {
public:
// Called when:
// 1. The most recently focused browser changes.
// 2. The active tab of the browser changes.
// 3. After a navigation of the web contents of the active tab.
// |web_contents| is the WebContents whose VisibleURL is considered the
// "Active URL" of Chrome.
virtual void HandoffActiveURLChanged(content::WebContents* web_contents) = 0;
protected:
virtual ~HandoffActiveURLObserverDelegate(){};
};
#endif // CHROME_BROWSER_UI_COCOA_HANDOFF_ACTIVE_URL_OBSERVER_DELEGATE_H_
......@@ -465,6 +465,11 @@
'browser/ui/cocoa/global_error_bubble_controller.mm',
'browser/ui/cocoa/gradient_button_cell.h',
'browser/ui/cocoa/gradient_button_cell.mm',
'browser/ui/cocoa/handoff_active_url_observer.cc',
'browser/ui/cocoa/handoff_active_url_observer.h',
'browser/ui/cocoa/handoff_active_url_observer_bridge.h',
'browser/ui/cocoa/handoff_active_url_observer_bridge.mm',
'browser/ui/cocoa/handoff_active_url_observer_delegate.h',
'browser/ui/cocoa/history_menu_bridge.h',
'browser/ui/cocoa/history_menu_bridge.mm',
'browser/ui/cocoa/history_menu_cocoa_controller.h',
......
......@@ -82,6 +82,8 @@ per-file gcm*=zea@chromium.org
per-file google*=isherman@chromium.org
per-file google*=pkasting@chromium.org
per-file handoff.gypi=erikchen@chromium.org
per-file history.gypi=sky@chromium.org
# Temporary for the duration of the history componentization.
per-file history.gypi=sdefresne@chromium.org
......
......@@ -18,6 +18,15 @@
'handoff/handoff_utility.mm',
],
}],
['OS=="mac"', {
'sources': [
'handoff/handoff_manager.h',
'handoff/handoff_manager.mm',
],
'dependencies': [
'../base/base.gyp:base',
],
}],
],
},
],
......
include_rules = [
"+net",
]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_HANDOFF_HANDOFF_MANAGER_H_
#define COMPONENTS_HANDOFF_HANDOFF_MANAGER_H_
#include <Foundation/Foundation.h>
#include "base/mac/objc_property_releaser.h"
#include "url/gurl.h"
@class NSUserActivity;
// Maintains all of the state relevant to the Handoff feature. Allows Chrome to
// hand off the current active URL to other devices.
@interface HandoffManager : NSObject {
@private
base::mac::ObjCPropertyReleaser _propertyReleaser_HandoffManager;
GURL _activeURL;
NSUserActivity* _userActivity;
}
// The active URL is defined as the URL of the most recently accessed tab. This
// method should be called any time the active URL might have changed. This
// method is idempotent.
- (void)updateActiveURL:(const GURL&)url;
@end
#endif // COMPONENTS_HANDOFF_HANDOFF_MANAGER_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/handoff/handoff_manager.h"
#include "base/logging.h"
#include "base/mac/scoped_nsobject.h"
#include "components/handoff/handoff_utility.h"
#include "net/base/mac/url_conversions.h"
#if defined(OS_IOS)
#include "base/ios/ios_util.h"
#endif
#if defined(OS_MACOSX) && !defined(OS_IOS)
#include "base/mac/mac_util.h"
#include "base/mac/sdk_forward_declarations.h"
#endif
@interface HandoffManager ()
// The active user activity.
@property(nonatomic, retain) NSUserActivity* userActivity;
// Whether the URL of the current tab should be exposed for Handoff.
- (BOOL)shouldUseActiveURL;
// Updates the active NSUserActivity.
- (void)updateUserActivity;
@end
@implementation HandoffManager
@synthesize userActivity = _userActivity;
- (instancetype)init {
self = [super init];
if (self) {
_propertyReleaser_HandoffManager.Init(self, [HandoffManager class]);
}
return self;
}
- (void)updateActiveURL:(const GURL&)url {
#if defined(OS_IOS)
// Handoff is only available on iOS 8+.
DCHECK(base::ios::IsRunningOnIOS8OrLater());
#endif
#if defined(OS_MACOSX) && !defined(OS_IOS)
// Handoff is only available on OSX 10.10+.
DCHECK(base::mac::IsOSYosemiteOrLater());
#endif
_activeURL = url;
[self updateUserActivity];
}
- (BOOL)shouldUseActiveURL {
return _activeURL.SchemeIsHTTPOrHTTPS();
}
- (void)updateUserActivity {
// Clear the user activity.
if (![self shouldUseActiveURL]) {
[self.userActivity invalidate];
self.userActivity = nil;
return;
}
// No change to the user activity.
const GURL userActivityURL(net::GURLWithNSURL(self.userActivity.webpageURL));
if (userActivityURL == _activeURL)
return;
// Invalidate the old user activity and make a new one.
[self.userActivity invalidate];
Class aClass = NSClassFromString(@"NSUserActivity");
NSUserActivity* userActivity = [[aClass performSelector:@selector(alloc)]
performSelector:@selector(initWithActivityType:)
withObject:handoff::kChromeHandoffActivityType];
self.userActivity = base::scoped_nsobject<NSUserActivity>(userActivity);
self.userActivity.webpageURL = net::NSURLWithGURL(_activeURL);
self.userActivity.userInfo = @{ handoff::kOriginKey : handoff::kOriginiOS };
[self.userActivity becomeCurrent];
}
@end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment