Index: chrome/browser/chrome_to_mobile_service.cc |
diff --git a/chrome/browser/chrome_to_mobile_service.cc b/chrome/browser/chrome_to_mobile_service.cc |
index 9d47a68d23b428c55a88d1f8ec925d8ebb7feb61..e311f2e4b6c188bc587949107e7119df889079b6 100644 |
--- a/chrome/browser/chrome_to_mobile_service.cc |
+++ b/chrome/browser/chrome_to_mobile_service.cc |
@@ -11,15 +11,15 @@ |
#include "base/json/json_reader.h" |
#include "base/json/json_writer.h" |
#include "base/metrics/histogram.h" |
-#include "base/stringprintf.h" |
#include "base/utf_string_conversions.h" |
#include "chrome/app/chrome_command_ids.h" |
-#include "chrome/browser/content_settings/cookie_settings.h" |
#include "chrome/browser/prefs/pref_service.h" |
#include "chrome/browser/printing/cloud_print/cloud_print_url.h" |
#include "chrome/browser/profiles/profile.h" |
#include "chrome/browser/signin/token_service.h" |
#include "chrome/browser/signin/token_service_factory.h" |
+#include "chrome/browser/sync/profile_sync_service.h" |
+#include "chrome/browser/sync/profile_sync_service_factory.h" |
#include "chrome/browser/ui/browser.h" |
#include "chrome/browser/ui/browser_command_controller.h" |
#include "chrome/browser/ui/browser_finder.h" |
@@ -38,6 +38,8 @@ |
#include "content/public/browser/notification_details.h" |
#include "content/public/browser/notification_source.h" |
#include "content/public/browser/web_contents.h" |
+#include "google/cacheinvalidation/include/types.h" |
+#include "google/cacheinvalidation/types.pb.h" |
#include "net/base/escape.h" |
#include "net/base/load_flags.h" |
#include "net/url_request/url_fetcher.h" |
@@ -59,19 +61,15 @@ const size_t kAuthRetryDelayHours = 6; |
// Note that this limitation does not hold across application restarts. |
const int kSearchRequestDelayHours = 24; |
+// The sync invalidation object ID for Chrome to Mobile's mobile device list. |
+// Meaning: "U" == "User", "CM" == "Chrome to Mobile", "MLST" == "Mobile LiST". |
+const char kSyncInvalidationObjectIdChromeToMobileDeviceList[] = "UCMMLST"; |
+ |
// The cloud print OAuth2 scope and 'printer' type of compatible mobile devices. |
const char kCloudPrintAuth[] = "https://www.googleapis.com/auth/cloudprint"; |
const char kTypeAndroid[] = "ANDROID_CHROME_SNAPSHOT"; |
const char kTypeIOS[] = "IOS_CHROME_SNAPSHOT"; |
-// The account info URL pattern and strings to check for cloud print access. |
-// The 'key=' query parameter is used for caching; supply a random number. |
-// The 'rv=2' query parameter requests a JSON response; use 'rv=1' for XML. |
-const char kAccountInfoURL[] = |
- "https://clients1.google.com/tbproxy/getaccountinfo?key=%s&rv=2&%s"; |
-const char kAccountServicesKey[] = "services"; |
-const char kCloudPrintSerivceValue[] = "cprt"; |
- |
// The Chrome To Mobile requestor type; used by services for filtering. |
const char kChromeToMobileRequestor[] = "requestor=chrome-to-mobile"; |
@@ -178,44 +176,59 @@ bool ChromeToMobileService::IsChromeToMobileEnabled() { |
void ChromeToMobileService::RegisterUserPrefs(PrefService* prefs) { |
prefs->RegisterListPref(prefs::kChromeToMobileDeviceList, |
PrefService::UNSYNCABLE_PREF); |
- prefs->RegisterInt64Pref(prefs::kChromeToMobileTimestamp, 0, |
- PrefService::UNSYNCABLE_PREF); |
} |
ChromeToMobileService::ChromeToMobileService(Profile* profile) |
: ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), |
profile_(profile), |
+ sync_invalidation_enabled_(false), |
cloud_print_url_(new CloudPrintURL(profile)), |
- cloud_print_accessible_(false) { |
- // Skip initialization if constructed without a profile. |
+ request_search_when_accessible_(false) { |
+ // TODO(msw): Fix GMock tests, which lack profiles (http://crbug.com/122183). |
if (profile_) { |
- // Get an access token as soon as the Gaia login refresh token is available. |
- TokenService* service = TokenServiceFactory::GetForProfile(profile_); |
- registrar_.Add(this, chrome::NOTIFICATION_TOKEN_AVAILABLE, |
- content::Source<TokenService>(service)); |
- if (service->HasOAuthLoginToken()) |
- RefreshAccessToken(); |
+ ProfileSyncService* profile_sync_service = |
+ ProfileSyncServiceFactory::GetForProfile(profile_); |
+ profile_sync_service->RegisterInvalidationHandler(this); |
+ syncer::ObjectIdSet ids; |
+ ids.insert(invalidation::ObjectId( |
+ ipc::invalidation::ObjectSource::CHROME_COMPONENTS, |
+ kSyncInvalidationObjectIdChromeToMobileDeviceList)); |
+ profile_sync_service->UpdateRegisteredInvalidationIds(this, ids); |
akalin
2012/08/13 22:23:58
what's the code path where chrome to phone is disa
msw
2012/08/16 02:41:51
It's tied to sync invalidation (presuming commandl
|
+ // TODO(msw): Use IsSyncEnabled? Is OnNotificationsEnabled called now? |
akalin
2012/08/13 22:23:58
don't use IsSyncEnabled. I plan to have OnNotific
msw
2012/08/16 02:41:51
Done. OnNotificationsEnabled seems to be called al
|
+ sync_invalidation_enabled_ = profile_sync_service->IsSyncEnabled(); |
} |
+ UpdateCommandState(); |
} |
ChromeToMobileService::~ChromeToMobileService() { |
while (!snapshots_.empty()) |
DeleteSnapshot(*snapshots_.begin()); |
+ if (profile_) |
akalin
2012/08/13 22:23:58
i think you can omit braces only if the 'then' sta
msw
2012/08/16 02:41:51
Done.
|
+ ProfileSyncServiceFactory::GetForProfile(profile_)-> |
+ UnregisterInvalidationHandler(this); |
} |
-bool ChromeToMobileService::HasMobiles() { |
- return !GetMobiles()->empty(); |
+bool ChromeToMobileService::HasMobiles() const { |
+ return sync_invalidation_enabled_ && !GetMobiles()->empty(); |
} |
const base::ListValue* ChromeToMobileService::GetMobiles() const { |
+ if (!sync_invalidation_enabled_) |
+ return NULL; |
+ |
return profile_->GetPrefs()->GetList(prefs::kChromeToMobileDeviceList); |
} |
void ChromeToMobileService::RequestMobileListUpdate() { |
- if (access_token_.empty()) |
+ if (!sync_invalidation_enabled_) |
+ return; |
+ |
+ if (access_token_.empty()) { |
+ request_search_when_accessible_ = true; |
RefreshAccessToken(); |
- else if (cloud_print_accessible_) |
+ } else { |
RequestSearch(); |
+ } |
} |
void ChromeToMobileService::GenerateSnapshot(Browser* browser, |
@@ -292,11 +305,8 @@ void ChromeToMobileService::LearnMore(Browser* browser) const { |
chrome::Navigate(¶ms); |
} |
-void ChromeToMobileService::OnURLFetchComplete( |
- const net::URLFetcher* source) { |
- if (source == account_info_request_.get()) |
- HandleAccountInfoResponse(); |
- else if (source == search_request_.get()) |
+void ChromeToMobileService::OnURLFetchComplete(const net::URLFetcher* source) { |
+ if (source == search_request_.get()) |
HandleSearchResponse(); |
else |
HandleSubmitResponse(source); |
@@ -309,6 +319,7 @@ void ChromeToMobileService::Observe( |
DCHECK_EQ(type, chrome::NOTIFICATION_TOKEN_AVAILABLE); |
TokenService::TokenAvailableDetails* token_details = |
content::Details<TokenService::TokenAvailableDetails>(details).ptr(); |
+ // Update the cloud print access token on Gaia login refresh token updates. |
if (token_details->service() == GaiaConstants::kGaiaOAuth2LoginRefreshToken) |
RefreshAccessToken(); |
} |
@@ -320,7 +331,11 @@ void ChromeToMobileService::OnGetTokenSuccess( |
access_token_fetcher_.reset(); |
auth_retry_timer_.Stop(); |
access_token_ = access_token; |
- RequestAccountInfo(); |
+ |
+ if (request_search_when_accessible_) { |
+ request_search_when_accessible_ = false; |
+ RequestSearch(); |
+ } |
} |
void ChromeToMobileService::OnGetTokenFailure( |
@@ -332,6 +347,45 @@ void ChromeToMobileService::OnGetTokenFailure( |
this, &ChromeToMobileService::RefreshAccessToken); |
} |
+void ChromeToMobileService::OnNotificationsEnabled() { |
+ // Only enable Chrome To Mobile if Sync Invalidation Notification are enabled. |
+ // Otherwise, the device list may be out of date and result in send failures. |
+ sync_invalidation_enabled_ = true; |
+ UpdateCommandState(); |
+} |
+ |
+void ChromeToMobileService::OnNotificationsDisabled( |
+ syncer::NotificationsDisabledReason reason) { |
+ // Only enable Chrome To Mobile if Sync Invalidation Notification are enabled. |
+ // Otherwise, the device list may be out of date and result in send failures. |
+ sync_invalidation_enabled_ = false; |
+ UpdateCommandState(); |
+} |
+ |
+void ChromeToMobileService::OnIncomingNotification( |
+ const syncer::ObjectIdPayloadMap& id_payloads, |
+ syncer::IncomingNotificationSource source) { |
+ DCHECK(sync_invalidation_enabled_); |
akalin
2012/08/13 22:23:58
i don't know if this should be a DCHECK. The API
msw
2012/08/16 02:41:51
Odd; then I'll just use it to gate the feature w/[
|
+ DCHECK_EQ(id_payloads.size(), 1U); |
+ DCHECK_EQ(id_payloads.count(invalidation::ObjectId( |
+ ipc::invalidation::ObjectSource::CHROME_COMPONENTS, |
+ kSyncInvalidationObjectIdChromeToMobileDeviceList)), 1U); |
+ RequestMobileListUpdate(); |
+} |
+ |
+// TODO(msw): Audit location bar code that checks enabled/disabled... |
+void ChromeToMobileService::UpdateCommandState() const { |
+ // Ensure the feature is not disabled by commandline options. |
+ DCHECK(IsChromeToMobileEnabled()); |
+ const bool has_mobiles = HasMobiles(); |
+ for (BrowserList::const_iterator i = BrowserList::begin(); |
+ i != BrowserList::end(); ++i) { |
+ Browser* browser = *i; |
+ if (browser->profile() == profile_) |
+ browser->command_controller()->SendToMobileStateChanged(has_mobiles); |
+ } |
+} |
+ |
void ChromeToMobileService::SnapshotFileCreated( |
base::WeakPtr<Observer> observer, |
SessionID::id_type browser_id, |
@@ -413,66 +467,32 @@ void ChromeToMobileService::SendRequest(net::URLFetcher* request, |
} |
void ChromeToMobileService::RefreshAccessToken() { |
- if (access_token_fetcher_.get()) |
- return; |
+ // Register to observe Gaia login refresh token updates. |
+ TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); |
+ if (registrar_.IsEmpty()) |
+ registrar_.Add(this, chrome::NOTIFICATION_TOKEN_AVAILABLE, |
+ content::Source<TokenService>(token_service)); |
- std::string token = TokenServiceFactory::GetForProfile(profile_)-> |
- GetOAuth2LoginRefreshToken(); |
- if (token.empty()) |
+ // Deny concurrent requests and bail without a valid Gaia login refresh token. |
+ if (access_token_fetcher_.get() || !token_service->HasOAuthLoginToken()) |
return; |
auth_retry_timer_.Stop(); |
access_token_fetcher_.reset( |
new OAuth2AccessTokenFetcher(this, profile_->GetRequestContext())); |
- std::vector<std::string> scopes(1, kCloudPrintAuth); |
GaiaUrls* gaia_urls = GaiaUrls::GetInstance(); |
access_token_fetcher_->Start(gaia_urls->oauth2_chrome_client_id(), |
- gaia_urls->oauth2_chrome_client_secret(), token, scopes); |
-} |
- |
-void ChromeToMobileService::RequestAccountInfo() { |
- // Deny concurrent requests. |
- if (account_info_request_.get()) |
- return; |
- |
- std::string url_string = StringPrintf(kAccountInfoURL, |
- base::GenerateGUID().c_str(), kChromeToMobileRequestor); |
- GURL url(url_string); |
- |
- // Account information is read from the profile's cookie. If cookies are |
- // blocked, access cloud print directly to list any potential devices. |
- scoped_refptr<CookieSettings> cookie_settings = |
- CookieSettings::Factory::GetForProfile(profile_); |
- if (cookie_settings && !cookie_settings->IsReadingCookieAllowed(url, url)) { |
- cloud_print_accessible_ = true; |
- RequestMobileListUpdate(); |
- return; |
- } |
- |
- account_info_request_.reset( |
- net::URLFetcher::Create(url, net::URLFetcher::GET, this)); |
- account_info_request_->SetRequestContext(profile_->GetRequestContext()); |
- account_info_request_->SetMaxRetries(kMaxRetries); |
- // This request sends the user's cookie to check the cloud print service flag. |
- account_info_request_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES); |
- account_info_request_->Start(); |
+ gaia_urls->oauth2_chrome_client_secret(), |
+ token_service->GetOAuth2LoginRefreshToken(), |
+ std::vector<std::string>(1, kCloudPrintAuth)); |
} |
void ChromeToMobileService::RequestSearch() { |
+ DCHECK(sync_invalidation_enabled_); |
DCHECK(!access_token_.empty()); |
- // Deny requests if cloud print is inaccessible, and deny concurrent requests. |
- if (!cloud_print_accessible_ || search_request_.get()) |
- return; |
- |
- PrefService* prefs = profile_->GetPrefs(); |
- base::TimeTicks previous_search_time = base::TimeTicks::FromInternalValue( |
- prefs->GetInt64(prefs::kChromeToMobileTimestamp)); |
- |
- // Deny requests before the delay period has passed since the last request. |
- base::TimeDelta elapsed_time = base::TimeTicks::Now() - previous_search_time; |
- if (!previous_search_time.is_null() && |
- elapsed_time.InHours() < kSearchRequestDelayHours) |
+ // Deny concurrent requests. |
+ if (search_request_.get()) |
return; |
LogMetric(DEVICES_REQUESTED); |
@@ -484,25 +504,6 @@ void ChromeToMobileService::RequestSearch() { |
search_request_->Start(); |
} |
-void ChromeToMobileService::HandleAccountInfoResponse() { |
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
- |
- std::string data; |
- account_info_request_->GetResponseAsString(&data); |
- account_info_request_.reset(); |
- |
- ListValue* services = NULL; |
- DictionaryValue* dictionary = NULL; |
- scoped_ptr<Value> json(base::JSONReader::Read(data)); |
- StringValue cloud_print_service(kCloudPrintSerivceValue); |
- if (json.get() && json->GetAsDictionary(&dictionary) && dictionary && |
- dictionary->GetList(kAccountServicesKey, &services) && services && |
- services->Find(cloud_print_service) != services->end()) { |
- cloud_print_accessible_ = true; |
- RequestMobileListUpdate(); |
- } |
-} |
- |
void ChromeToMobileService::HandleSearchResponse() { |
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
@@ -510,6 +511,10 @@ void ChromeToMobileService::HandleSearchResponse() { |
search_request_->GetResponseAsString(&data); |
search_request_.reset(); |
+ // TODO(msw): Ackowledge the sync invalidation when API exists. |
+ // TODO(msw): Detect, log, potentially retry on failure / bad responses. |
+ // TODO(msw): Handle removing all devices or cloud print account altogether. |
+ |
ListValue* list = NULL; |
DictionaryValue* dictionary = NULL; |
scoped_ptr<Value> json(base::JSONReader::Read(data)); |
@@ -537,22 +542,12 @@ void ChromeToMobileService::HandleSearchResponse() { |
} |
} |
- // Update the mobile list and timestamp in prefs. |
- PrefService* prefs = profile_->GetPrefs(); |
- prefs->Set(prefs::kChromeToMobileDeviceList, mobiles); |
- prefs->SetInt64(prefs::kChromeToMobileTimestamp, |
- base::TimeTicks::Now().ToInternalValue()); |
+ // Update the cached mobile device list in profile prefs. |
+ profile_->GetPrefs()->Set(prefs::kChromeToMobileDeviceList, mobiles); |
- const bool has_mobiles = HasMobiles(); |
- if (has_mobiles) |
+ if (HasMobiles()) |
LogMetric(DEVICES_AVAILABLE); |
- |
- for (BrowserList::const_iterator i = BrowserList::begin(); |
- i != BrowserList::end(); ++i) { |
- Browser* browser = *i; |
- if (browser->profile() == profile_) |
- browser->command_controller()->SendToMobileStateChanged(has_mobiles); |
- } |
+ UpdateCommandState(); |
} |
} |