| OLD | NEW | 
|    1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |    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 |    2 // Use of this source code is governed by a BSD-style license that can be | 
|    3 // found in the LICENSE file. |    3 // found in the LICENSE file. | 
|    4  |    4  | 
|    5 #include "chrome/browser/chrome_to_mobile_service.h" |    5 #include "chrome/browser/chrome_to_mobile_service.h" | 
|    6  |    6  | 
|    7 #include "base/bind.h" |    7 #include "base/bind.h" | 
|    8 #include "base/command_line.h" |    8 #include "base/command_line.h" | 
|    9 #include "base/file_util.h" |    9 #include "base/file_util.h" | 
|   10 #include "base/guid.h" |   10 #include "base/guid.h" | 
|   11 #include "base/json/json_reader.h" |   11 #include "base/json/json_reader.h" | 
|   12 #include "base/json/json_writer.h" |   12 #include "base/json/json_writer.h" | 
|   13 #include "base/metrics/histogram.h" |   13 #include "base/metrics/histogram.h" | 
 |   14 #include "base/stringprintf.h" | 
|   14 #include "base/utf_string_conversions.h" |   15 #include "base/utf_string_conversions.h" | 
|   15 #include "chrome/app/chrome_command_ids.h" |   16 #include "chrome/app/chrome_command_ids.h" | 
 |   17 #include "chrome/browser/content_settings/cookie_settings.h" | 
|   16 #include "chrome/browser/prefs/pref_service.h" |   18 #include "chrome/browser/prefs/pref_service.h" | 
|   17 #include "chrome/browser/printing/cloud_print/cloud_print_url.h" |   19 #include "chrome/browser/printing/cloud_print/cloud_print_url.h" | 
|   18 #include "chrome/browser/profiles/profile.h" |   20 #include "chrome/browser/profiles/profile.h" | 
|   19 #include "chrome/browser/signin/token_service.h" |   21 #include "chrome/browser/signin/token_service.h" | 
|   20 #include "chrome/browser/signin/token_service_factory.h" |   22 #include "chrome/browser/signin/token_service_factory.h" | 
|   21 #include "chrome/browser/sync/profile_sync_service.h" |  | 
|   22 #include "chrome/browser/sync/profile_sync_service_factory.h" |  | 
|   23 #include "chrome/browser/ui/browser.h" |   23 #include "chrome/browser/ui/browser.h" | 
|   24 #include "chrome/browser/ui/browser_command_controller.h" |   24 #include "chrome/browser/ui/browser_command_controller.h" | 
|   25 #include "chrome/browser/ui/browser_finder.h" |   25 #include "chrome/browser/ui/browser_finder.h" | 
|   26 #include "chrome/browser/ui/browser_list.h" |   26 #include "chrome/browser/ui/browser_list.h" | 
|   27 #include "chrome/browser/ui/browser_navigator.h" |   27 #include "chrome/browser/ui/browser_navigator.h" | 
|   28 #include "chrome/browser/ui/browser_tabstrip.h" |   28 #include "chrome/browser/ui/browser_tabstrip.h" | 
|   29 #include "chrome/common/chrome_notification_types.h" |   29 #include "chrome/common/chrome_notification_types.h" | 
|   30 #include "chrome/common/chrome_switches.h" |   30 #include "chrome/common/chrome_switches.h" | 
|   31 #include "chrome/common/cloud_print/cloud_print_helpers.h" |   31 #include "chrome/common/cloud_print/cloud_print_helpers.h" | 
|   32 #include "chrome/common/net/gaia/gaia_constants.h" |   32 #include "chrome/common/net/gaia/gaia_constants.h" | 
|   33 #include "chrome/common/net/gaia/gaia_urls.h" |   33 #include "chrome/common/net/gaia/gaia_urls.h" | 
|   34 #include "chrome/common/net/gaia/oauth2_access_token_fetcher.h" |   34 #include "chrome/common/net/gaia/oauth2_access_token_fetcher.h" | 
|   35 #include "chrome/common/pref_names.h" |   35 #include "chrome/common/pref_names.h" | 
|   36 #include "chrome/common/url_constants.h" |   36 #include "chrome/common/url_constants.h" | 
|   37 #include "content/public/browser/browser_thread.h" |   37 #include "content/public/browser/browser_thread.h" | 
|   38 #include "content/public/browser/notification_details.h" |   38 #include "content/public/browser/notification_details.h" | 
|   39 #include "content/public/browser/notification_source.h" |   39 #include "content/public/browser/notification_source.h" | 
|   40 #include "content/public/browser/web_contents.h" |   40 #include "content/public/browser/web_contents.h" | 
|   41 #include "google/cacheinvalidation/include/types.h" |  | 
|   42 #include "google/cacheinvalidation/types.pb.h" |  | 
|   43 #include "net/base/escape.h" |   41 #include "net/base/escape.h" | 
|   44 #include "net/base/load_flags.h" |   42 #include "net/base/load_flags.h" | 
|   45 #include "net/url_request/url_fetcher.h" |   43 #include "net/url_request/url_fetcher.h" | 
|   46 #include "net/url_request/url_request_context_getter.h" |   44 #include "net/url_request/url_request_context_getter.h" | 
|   47 #include "sync/notifier/invalidation_util.h" |  | 
|   48  |   45  | 
|   49 namespace { |   46 namespace { | 
|   50  |   47  | 
|   51 // The default enabled/disabled state of the Chrome To Mobile feature. |   48 // The default enabled/disabled state of the Chrome To Mobile feature. | 
|   52 const bool kChromeToMobileEnabled = true; |   49 const bool kChromeToMobileEnabled = true; | 
|   53  |   50  | 
|   54 // The maximum number of retries for the URLFetcher requests. |   51 // The maximum number of retries for the URLFetcher requests. | 
|   55 const size_t kMaxRetries = 1; |   52 const size_t kMaxRetries = 1; | 
|   56  |   53  | 
|   57 // The number of hours to delay before retrying authentication on failure. |   54 // The number of hours to delay before retrying authentication on failure. | 
|   58 const size_t kAuthRetryDelayHours = 6; |   55 const size_t kAuthRetryDelayHours = 6; | 
|   59  |   56  | 
|   60 // The number of hours before subsequent search requests are allowed. |   57 // The number of hours before subsequent search requests are allowed. | 
|   61 // This value is used to throttle expensive cloud print search requests. |   58 // This value is used to throttle expensive cloud print search requests. | 
|   62 // Note that this limitation does not hold across application restarts. |   59 // Note that this limitation does not hold across application restarts. | 
|   63 const int kSearchRequestDelayHours = 24; |   60 const int kSearchRequestDelayHours = 24; | 
|   64  |   61  | 
|   65 // The sync invalidation object ID for Chrome to Mobile's mobile device list. |  | 
|   66 // This corresponds with cloud print's server-side invalidation object ID. |  | 
|   67 // Meaning: "U" == "User", "CM" == "Chrome to Mobile", "MLST" == "Mobile LiST". |  | 
|   68 const char kSyncInvalidationObjectIdChromeToMobileDeviceList[] = "UCMMLST"; |  | 
|   69  |  | 
|   70 // The cloud print OAuth2 scope and 'printer' type of compatible mobile devices. |   62 // The cloud print OAuth2 scope and 'printer' type of compatible mobile devices. | 
|   71 const char kCloudPrintAuth[] = "https://www.googleapis.com/auth/cloudprint"; |   63 const char kCloudPrintAuth[] = "https://www.googleapis.com/auth/cloudprint"; | 
|   72 const char kTypeAndroid[] = "ANDROID_CHROME_SNAPSHOT"; |   64 const char kTypeAndroid[] = "ANDROID_CHROME_SNAPSHOT"; | 
|   73 const char kTypeIOS[] = "IOS_CHROME_SNAPSHOT"; |   65 const char kTypeIOS[] = "IOS_CHROME_SNAPSHOT"; | 
|   74  |   66  | 
 |   67 // The account info URL pattern and strings to check for cloud print access. | 
 |   68 // The 'key=' query parameter is used for caching; supply a random number. | 
 |   69 // The 'rv=2' query parameter requests a JSON response; use 'rv=1' for XML. | 
 |   70 const char kAccountInfoURL[] = | 
 |   71     "https://clients1.google.com/tbproxy/getaccountinfo?key=%s&rv=2&%s"; | 
 |   72 const char kAccountServicesKey[] = "services"; | 
 |   73 const char kCloudPrintSerivceValue[] = "cprt"; | 
 |   74  | 
|   75 // The Chrome To Mobile requestor type; used by services for filtering. |   75 // The Chrome To Mobile requestor type; used by services for filtering. | 
|   76 const char kChromeToMobileRequestor[] = "requestor=chrome-to-mobile"; |   76 const char kChromeToMobileRequestor[] = "requestor=chrome-to-mobile"; | 
|   77  |   77  | 
|   78 // Get the job type string for a cloud print job submission. |   78 // Get the job type string for a cloud print job submission. | 
|   79 std::string GetType(const ChromeToMobileService::JobData& data) { |   79 std::string GetType(const ChromeToMobileService::JobData& data) { | 
|   80   if (data.type == ChromeToMobileService::URL) |   80   if (data.type == ChromeToMobileService::URL) | 
|   81     return "url"; |   81     return "url"; | 
|   82   if (data.type == ChromeToMobileService::DELAYED_SNAPSHOT) |   82   if (data.type == ChromeToMobileService::DELAYED_SNAPSHOT) | 
|   83     return "url_with_delayed_snapshot"; |   83     return "url_with_delayed_snapshot"; | 
|   84   DCHECK_EQ(data.type, ChromeToMobileService::SNAPSHOT); |   84   DCHECK_EQ(data.type, ChromeToMobileService::SNAPSHOT); | 
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  118 // Utility function to call cloud_print::AddMultipartValueForUpload. |  118 // Utility function to call cloud_print::AddMultipartValueForUpload. | 
|  119 void AddValue(const std::string& value_name, |  119 void AddValue(const std::string& value_name, | 
|  120               const std::string& value, |  120               const std::string& value, | 
|  121               const std::string& mime_boundary, |  121               const std::string& mime_boundary, | 
|  122               std::string* post_data) { |  122               std::string* post_data) { | 
|  123   cloud_print::AddMultipartValueForUpload(value_name, value, mime_boundary, |  123   cloud_print::AddMultipartValueForUpload(value_name, value, mime_boundary, | 
|  124                                           std::string(), post_data); |  124                                           std::string(), post_data); | 
|  125 } |  125 } | 
|  126  |  126  | 
|  127 // Get the URL for cloud print device search; appends a requestor query param. |  127 // Get the URL for cloud print device search; appends a requestor query param. | 
|  128 GURL GetSearchURL(const GURL& cloud_print_url) { |  128 GURL GetSearchURL(const GURL& service_url) { | 
|  129   GURL search_url = cloud_print::GetUrlForSearch(cloud_print_url); |  129   GURL search_url = cloud_print::GetUrlForSearch(service_url); | 
|  130   GURL::Replacements replacements; |  130   GURL::Replacements replacements; | 
|  131   std::string query(kChromeToMobileRequestor); |  131   std::string query(kChromeToMobileRequestor); | 
|  132   replacements.SetQueryStr(query); |  132   replacements.SetQueryStr(query); | 
|  133   return search_url.ReplaceComponents(replacements); |  133   return search_url.ReplaceComponents(replacements); | 
|  134 } |  134 } | 
|  135  |  135  | 
|  136 // A callback to continue snapshot generation after creating the temp file. |  136 // A callback to continue snapshot generation after creating the temp file. | 
|  137 typedef base::Callback<void(const FilePath& path, bool success)> |  137 typedef base::Callback<void(const FilePath& path, bool success)> | 
|  138     CreateSnapshotFileCallback; |  138     CreateSnapshotFileCallback; | 
|  139  |  139  | 
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  173   if (command_line->HasSwitch(switches::kEnableChromeToMobile)) |  173   if (command_line->HasSwitch(switches::kEnableChromeToMobile)) | 
|  174     return true; |  174     return true; | 
|  175  |  175  | 
|  176   return kChromeToMobileEnabled; |  176   return kChromeToMobileEnabled; | 
|  177 } |  177 } | 
|  178  |  178  | 
|  179 // static |  179 // static | 
|  180 void ChromeToMobileService::RegisterUserPrefs(PrefService* prefs) { |  180 void ChromeToMobileService::RegisterUserPrefs(PrefService* prefs) { | 
|  181   prefs->RegisterListPref(prefs::kChromeToMobileDeviceList, |  181   prefs->RegisterListPref(prefs::kChromeToMobileDeviceList, | 
|  182                           PrefService::UNSYNCABLE_PREF); |  182                           PrefService::UNSYNCABLE_PREF); | 
 |  183   prefs->RegisterInt64Pref(prefs::kChromeToMobileTimestamp, 0, | 
 |  184                            PrefService::UNSYNCABLE_PREF); | 
|  183 } |  185 } | 
|  184  |  186  | 
|  185 ChromeToMobileService::ChromeToMobileService(Profile* profile) |  187 ChromeToMobileService::ChromeToMobileService(Profile* profile) | 
|  186     : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), |  188     : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), | 
|  187       profile_(profile), |  189       profile_(profile), | 
|  188       sync_invalidation_enabled_(false) { |  190       cloud_print_url_(new CloudPrintURL(profile)), | 
|  189   // TODO(msw): Unit tests do not provide profiles; see http://crbug.com/122183 |  191       cloud_print_accessible_(false) { | 
|  190   ProfileSyncService* profile_sync_service = |  192   // TODO(msw): Fix GMock tests, which lack profiles (http://crbug.com/122183). | 
|  191       profile_ ? ProfileSyncServiceFactory::GetForProfile(profile_) : NULL; |  193   if (profile_) { | 
|  192   if (profile_sync_service) { |  194     // Get an access token as soon as the Gaia login refresh token is available. | 
|  193     CloudPrintURL cloud_print_url(profile_); |  195     TokenService* service = TokenServiceFactory::GetForProfile(profile_); | 
|  194     cloud_print_url_ = cloud_print_url.GetCloudPrintServiceURL(); |  196     registrar_.Add(this, chrome::NOTIFICATION_TOKEN_AVAILABLE, | 
|  195     // Register for cloud print device list invalidation notifications. |  197                    content::Source<TokenService>(service)); | 
|  196     // TODO(msw|akalin): Initialize |sync_invalidation_enabled_| properly. |  198     if (service->HasOAuthLoginToken()) | 
|  197     profile_sync_service->RegisterInvalidationHandler(this); |  199       RequestAccessToken(); | 
|  198     syncer::ObjectIdSet ids; |  | 
|  199     ids.insert(invalidation::ObjectId( |  | 
|  200         ipc::invalidation::ObjectSource::CHROME_COMPONENTS, |  | 
|  201         kSyncInvalidationObjectIdChromeToMobileDeviceList)); |  | 
|  202     profile_sync_service->UpdateRegisteredInvalidationIds(this, ids); |  | 
|  203   } |  200   } | 
|  204 } |  201 } | 
|  205  |  202  | 
|  206 ChromeToMobileService::~ChromeToMobileService() { |  203 ChromeToMobileService::~ChromeToMobileService() { | 
|  207   while (!snapshots_.empty()) |  204   while (!snapshots_.empty()) | 
|  208     DeleteSnapshot(*snapshots_.begin()); |  205     DeleteSnapshot(*snapshots_.begin()); | 
|  209 } |  206 } | 
|  210  |  207  | 
|  211 bool ChromeToMobileService::HasMobiles() const { |  208 bool ChromeToMobileService::HasMobiles() const { | 
|  212   const base::ListValue* mobiles = GetMobiles(); |  209   return !GetMobiles()->empty(); | 
|  213   return mobiles && !mobiles->empty(); |  | 
|  214 } |  210 } | 
|  215  |  211  | 
|  216 const base::ListValue* ChromeToMobileService::GetMobiles() const { |  212 const base::ListValue* ChromeToMobileService::GetMobiles() const { | 
|  217   return sync_invalidation_enabled_ ? |  213   return profile_->GetPrefs()->GetList(prefs::kChromeToMobileDeviceList); | 
|  218       profile_->GetPrefs()->GetList(prefs::kChromeToMobileDeviceList) : NULL; |  214 } | 
 |  215  | 
 |  216 void ChromeToMobileService::RequestMobileListUpdate() { | 
 |  217   if (access_token_.empty()) | 
 |  218     RequestAccessToken(); | 
 |  219   else if (cloud_print_accessible_) | 
 |  220     RequestDeviceSearch(); | 
|  219 } |  221 } | 
|  220  |  222  | 
|  221 void ChromeToMobileService::GenerateSnapshot(Browser* browser, |  223 void ChromeToMobileService::GenerateSnapshot(Browser* browser, | 
|  222                                              base::WeakPtr<Observer> observer) { |  224                                              base::WeakPtr<Observer> observer) { | 
|  223   // Callback SnapshotFileCreated from CreateSnapshotFile to continue. |  225   // Callback SnapshotFileCreated from CreateSnapshotFile to continue. | 
|  224   CreateSnapshotFileCallback callback = |  226   CreateSnapshotFileCallback callback = | 
|  225       base::Bind(&ChromeToMobileService::SnapshotFileCreated, |  227       base::Bind(&ChromeToMobileService::SnapshotFileCreated, | 
|  226                  weak_ptr_factory_.GetWeakPtr(), observer, |  228                  weak_ptr_factory_.GetWeakPtr(), observer, | 
|  227                  browser->session_id().id()); |  229                  browser->session_id().id()); | 
|  228   // Create a temporary file via the blocking pool for snapshot storage. |  230   // Create a temporary file via the blocking pool for snapshot storage. | 
|  229   if (!content::BrowserThread::PostBlockingPoolTask(FROM_HERE, |  231   if (!content::BrowserThread::PostBlockingPoolTask(FROM_HERE, | 
|  230           base::Bind(&CreateSnapshotFile, callback))) { |  232           base::Bind(&CreateSnapshotFile, callback))) { | 
|  231     NOTREACHED(); |  233     NOTREACHED(); | 
|  232   } |  234   } | 
|  233 } |  235 } | 
|  234  |  236  | 
|  235 void ChromeToMobileService::SendToMobile(const base::DictionaryValue* mobile, |  237 void ChromeToMobileService::SendToMobile(const base::DictionaryValue& mobile, | 
|  236                                          const FilePath& snapshot, |  238                                          const FilePath& snapshot, | 
|  237                                          Browser* browser, |  239                                          Browser* browser, | 
|  238                                          base::WeakPtr<Observer> observer) { |  240                                          base::WeakPtr<Observer> observer) { | 
|  239   if (access_token_.empty()) { |  | 
|  240     // Enqueue this task to perform after obtaining an access token. |  | 
|  241     task_queue_.push(base::Bind(&ChromeToMobileService::SendToMobile, |  | 
|  242         weak_ptr_factory_.GetWeakPtr(), base::Owned(mobile->DeepCopy()), |  | 
|  243         snapshot, browser, observer)); |  | 
|  244     RequestAccessToken(); |  | 
|  245     return; |  | 
|  246   } |  | 
|  247  |  | 
|  248   LogMetric(SENDING_URL); |  241   LogMetric(SENDING_URL); | 
|  249  |  242  | 
|  250   JobData data; |  243   JobData data; | 
|  251   std::string mobile_os; |  244   std::string mobile_os; | 
|  252   if (!mobile->GetString("type", &mobile_os)) |  245   if (!mobile.GetString("type", &mobile_os)) | 
|  253     NOTREACHED(); |  246     NOTREACHED(); | 
|  254   data.mobile_os = (mobile_os.compare(kTypeAndroid) == 0) ? |  247   data.mobile_os = (mobile_os.compare(kTypeAndroid) == 0) ? | 
|  255       ChromeToMobileService::ANDROID : ChromeToMobileService::IOS; |  248       ChromeToMobileService::ANDROID : ChromeToMobileService::IOS; | 
|  256   if (!mobile->GetString("id", &data.mobile_id)) |  249   if (!mobile.GetString("id", &data.mobile_id)) | 
|  257     NOTREACHED(); |  250     NOTREACHED(); | 
|  258   content::WebContents* web_contents = chrome::GetActiveWebContents(browser); |  251   content::WebContents* web_contents = chrome::GetActiveWebContents(browser); | 
|  259   data.url = web_contents->GetURL(); |  252   data.url = web_contents->GetURL(); | 
|  260   data.title = web_contents->GetTitle(); |  253   data.title = web_contents->GetTitle(); | 
|  261   data.snapshot = snapshot; |  254   data.snapshot = snapshot; | 
|  262   data.snapshot_id = base::GenerateGUID(); |  255   data.snapshot_id = base::GenerateGUID(); | 
|  263   data.type = !snapshot.empty() ? DELAYED_SNAPSHOT : URL; |  256   data.type = !snapshot.empty() ? DELAYED_SNAPSHOT : URL; | 
|  264  |  257  | 
|  265   net::URLFetcher* submit_url = CreateRequest(data); |  258   net::URLFetcher* submit_url = CreateRequest(data); | 
|  266   request_observer_map_[submit_url] = observer; |  259   request_observer_map_[submit_url] = observer; | 
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  301 } |  294 } | 
|  302  |  295  | 
|  303 void ChromeToMobileService::LearnMore(Browser* browser) const { |  296 void ChromeToMobileService::LearnMore(Browser* browser) const { | 
|  304   LogMetric(LEARN_MORE_CLICKED); |  297   LogMetric(LEARN_MORE_CLICKED); | 
|  305   chrome::NavigateParams params(browser, |  298   chrome::NavigateParams params(browser, | 
|  306       GURL(chrome::kChromeToMobileLearnMoreURL), content::PAGE_TRANSITION_LINK); |  299       GURL(chrome::kChromeToMobileLearnMoreURL), content::PAGE_TRANSITION_LINK); | 
|  307   params.disposition = NEW_FOREGROUND_TAB; |  300   params.disposition = NEW_FOREGROUND_TAB; | 
|  308   chrome::Navigate(¶ms); |  301   chrome::Navigate(¶ms); | 
|  309 } |  302 } | 
|  310  |  303  | 
|  311 void ChromeToMobileService::Shutdown() { |  304 void ChromeToMobileService::OnURLFetchComplete( | 
|  312   // TODO(msw): Unit tests do not provide profiles; see http://crbug.com/122183 |  305     const net::URLFetcher* source) { | 
|  313   // Unregister for cloud print device list invalidation notifications. |  306   if (source == account_info_request_.get()) | 
|  314   ProfileSyncService* profile_sync_service = |  307     HandleAccountInfoResponse(); | 
|  315       profile_ ? ProfileSyncServiceFactory::GetForProfile(profile_) : NULL; |  308   else if (source == search_request_.get()) | 
|  316   if (profile_sync_service) |  309     HandleSearchResponse(); | 
|  317     profile_sync_service->UnregisterInvalidationHandler(this); |  | 
|  318 } |  | 
|  319  |  | 
|  320 void ChromeToMobileService::OnURLFetchComplete(const net::URLFetcher* source) { |  | 
|  321   if (source->GetURL() == GetSearchURL(cloud_print_url_)) |  | 
|  322     HandleSearchResponse(source); |  | 
|  323   else |  310   else | 
|  324     HandleSubmitResponse(source); |  311     HandleSubmitResponse(source); | 
|  325 } |  312 } | 
|  326  |  313  | 
|  327 void ChromeToMobileService::Observe( |  314 void ChromeToMobileService::Observe( | 
|  328     int type, |  315     int type, | 
|  329     const content::NotificationSource& source, |  316     const content::NotificationSource& source, | 
|  330     const content::NotificationDetails& details) { |  317     const content::NotificationDetails& details) { | 
|  331   DCHECK_EQ(type, chrome::NOTIFICATION_TOKEN_AVAILABLE); |  318   DCHECK_EQ(type, chrome::NOTIFICATION_TOKEN_AVAILABLE); | 
|  332   TokenService::TokenAvailableDetails* token_details = |  319   TokenService::TokenAvailableDetails* token_details = | 
|  333       content::Details<TokenService::TokenAvailableDetails>(details).ptr(); |  320       content::Details<TokenService::TokenAvailableDetails>(details).ptr(); | 
|  334   // Invalidate the cloud print access token on Gaia login token updates. |  | 
|  335   if (token_details->service() == GaiaConstants::kGaiaOAuth2LoginRefreshToken) |  321   if (token_details->service() == GaiaConstants::kGaiaOAuth2LoginRefreshToken) | 
|  336     access_token_.clear(); |  322     RequestAccessToken(); | 
|  337 } |  323 } | 
|  338  |  324  | 
|  339 void ChromeToMobileService::OnGetTokenSuccess( |  325 void ChromeToMobileService::OnGetTokenSuccess( | 
|  340     const std::string& access_token, |  326     const std::string& access_token, | 
|  341     const base::Time& expiration_time) { |  327     const base::Time& expiration_time) { | 
|  342   DCHECK(!access_token.empty()); |  328   DCHECK(!access_token.empty()); | 
|  343   access_token_fetcher_.reset(); |  329   access_token_fetcher_.reset(); | 
|  344   auth_retry_timer_.Stop(); |  330   auth_retry_timer_.Stop(); | 
|  345   access_token_ = access_token; |  331   access_token_ = access_token; | 
|  346  |  332   RequestAccountInfo(); | 
|  347   while (!task_queue_.empty()) { |  | 
|  348     // Post all tasks that were queued and waiting on a valid access token. |  | 
|  349     if (!content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |  | 
|  350                                           task_queue_.front())) { |  | 
|  351       NOTREACHED(); |  | 
|  352     } |  | 
|  353     task_queue_.pop(); |  | 
|  354   } |  | 
|  355 } |  333 } | 
|  356  |  334  | 
|  357 void ChromeToMobileService::OnGetTokenFailure( |  335 void ChromeToMobileService::OnGetTokenFailure( | 
|  358     const GoogleServiceAuthError& error) { |  336     const GoogleServiceAuthError& error) { | 
|  359   access_token_fetcher_.reset(); |  337   access_token_fetcher_.reset(); | 
|  360   auth_retry_timer_.Stop(); |  338   auth_retry_timer_.Stop(); | 
|  361  |  | 
|  362   auth_retry_timer_.Start( |  339   auth_retry_timer_.Start( | 
|  363       FROM_HERE, base::TimeDelta::FromHours(kAuthRetryDelayHours), |  340       FROM_HERE, base::TimeDelta::FromHours(kAuthRetryDelayHours), | 
|  364       this, &ChromeToMobileService::RequestAccessToken); |  341       this, &ChromeToMobileService::RequestAccessToken); | 
|  365 } |  342 } | 
|  366  |  343  | 
|  367 void ChromeToMobileService::OnNotificationsEnabled() { |  | 
|  368   sync_invalidation_enabled_ = true; |  | 
|  369   UpdateCommandState(); |  | 
|  370 } |  | 
|  371  |  | 
|  372 void ChromeToMobileService::OnNotificationsDisabled( |  | 
|  373     syncer::NotificationsDisabledReason reason) { |  | 
|  374   sync_invalidation_enabled_ = false; |  | 
|  375   UpdateCommandState(); |  | 
|  376 } |  | 
|  377  |  | 
|  378 void ChromeToMobileService::OnIncomingNotification( |  | 
|  379     const syncer::ObjectIdPayloadMap& id_payloads, |  | 
|  380     syncer::IncomingNotificationSource source) { |  | 
|  381   DCHECK_EQ(id_payloads.size(), 1U); |  | 
|  382   DCHECK_EQ(id_payloads.count(invalidation::ObjectId( |  | 
|  383       ipc::invalidation::ObjectSource::CHROME_COMPONENTS, |  | 
|  384       kSyncInvalidationObjectIdChromeToMobileDeviceList)), 1U); |  | 
|  385   RequestDeviceSearch(); |  | 
|  386 } |  | 
|  387  |  | 
|  388 const std::string& ChromeToMobileService::GetAccessTokenForTest() const { |  | 
|  389   return access_token_; |  | 
|  390 } |  | 
|  391  |  | 
|  392 void ChromeToMobileService::SetAccessTokenForTest( |  | 
|  393     const std::string& access_token) { |  | 
|  394   access_token_ = access_token; |  | 
|  395 } |  | 
|  396  |  | 
|  397 void ChromeToMobileService::UpdateCommandState() const { |  344 void ChromeToMobileService::UpdateCommandState() const { | 
|  398   // Ensure the feature is not disabled by commandline options. |  345   // Ensure the feature is not disabled by commandline options. | 
|  399   DCHECK(IsChromeToMobileEnabled()); |  346   DCHECK(IsChromeToMobileEnabled()); | 
|  400   const bool has_mobiles = HasMobiles(); |  347   const bool has_mobiles = HasMobiles(); | 
|  401   for (BrowserList::const_iterator i = BrowserList::begin(); |  348   for (BrowserList::const_iterator i = BrowserList::begin(); | 
|  402        i != BrowserList::end(); ++i) { |  349        i != BrowserList::end(); ++i) { | 
|  403     Browser* browser = *i; |  350     Browser* browser = *i; | 
|  404     if (browser->profile() == profile_) |  351     if (browser->profile() == profile_) | 
|  405       browser->command_controller()->SendToMobileStateChanged(has_mobiles); |  352       browser->command_controller()->SendToMobileStateChanged(has_mobiles); | 
|  406   } |  353   } | 
| (...skipping 13 matching lines...) Expand all  Loading... | 
|  420     // Generate the snapshot and have the observer be called back on completion. |  367     // Generate the snapshot and have the observer be called back on completion. | 
|  421     chrome::GetActiveWebContents(browser)->GenerateMHTML(path, |  368     chrome::GetActiveWebContents(browser)->GenerateMHTML(path, | 
|  422         base::Bind(&Observer::SnapshotGenerated, observer)); |  369         base::Bind(&Observer::SnapshotGenerated, observer)); | 
|  423   } else if (observer.get()) { |  370   } else if (observer.get()) { | 
|  424     // Signal snapshot generation failure. |  371     // Signal snapshot generation failure. | 
|  425     observer->SnapshotGenerated(FilePath(), 0); |  372     observer->SnapshotGenerated(FilePath(), 0); | 
|  426   } |  373   } | 
|  427 } |  374 } | 
|  428  |  375  | 
|  429 net::URLFetcher* ChromeToMobileService::CreateRequest(const JobData& data) { |  376 net::URLFetcher* ChromeToMobileService::CreateRequest(const JobData& data) { | 
 |  377   const GURL service_url(cloud_print_url_->GetCloudPrintServiceURL()); | 
|  430   net::URLFetcher* request = net::URLFetcher::Create( |  378   net::URLFetcher* request = net::URLFetcher::Create( | 
|  431       cloud_print::GetUrlForSubmit(cloud_print_url_), |  379       cloud_print::GetUrlForSubmit(service_url), net::URLFetcher::POST, this); | 
|  432       net::URLFetcher::POST, this); |  | 
|  433   InitRequest(request); |  380   InitRequest(request); | 
|  434   return request; |  381   return request; | 
|  435 } |  382 } | 
|  436  |  383  | 
|  437 void ChromeToMobileService::InitRequest(net::URLFetcher* request) { |  384 void ChromeToMobileService::InitRequest(net::URLFetcher* request) { | 
|  438   request->SetRequestContext(profile_->GetRequestContext()); |  385   request->SetRequestContext(profile_->GetRequestContext()); | 
|  439   request->SetMaxRetries(kMaxRetries); |  386   request->SetMaxRetries(kMaxRetries); | 
|  440   DCHECK(!access_token_.empty()); |  387   DCHECK(!access_token_.empty()); | 
|  441   request->SetExtraRequestHeaders("Authorization: OAuth " + |  388   request->SetExtraRequestHeaders("Authorization: OAuth " + | 
|  442       access_token_ + "\r\n" + cloud_print::kChromeCloudPrintProxyHeader); |  389       access_token_ + "\r\n" + cloud_print::kChromeCloudPrintProxyHeader); | 
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  481   } |  428   } | 
|  482   cloud_print::AddMultipartValueForUpload("content", file, bound, |  429   cloud_print::AddMultipartValueForUpload("content", file, bound, | 
|  483                                           "text/mhtml", &post); |  430                                           "text/mhtml", &post); | 
|  484  |  431  | 
|  485   post.append("--" + bound + "--\r\n"); |  432   post.append("--" + bound + "--\r\n"); | 
|  486   request->SetUploadData("multipart/form-data; boundary=" + bound, post); |  433   request->SetUploadData("multipart/form-data; boundary=" + bound, post); | 
|  487   request->Start(); |  434   request->Start(); | 
|  488 } |  435 } | 
|  489  |  436  | 
|  490 void ChromeToMobileService::RequestAccessToken() { |  437 void ChromeToMobileService::RequestAccessToken() { | 
|  491   // Register to observe Gaia login refresh token updates. |  438   // Deny concurrent requests and bail without a valid Gaia login refresh token. | 
|  492   TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); |  439   TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); | 
|  493   if (registrar_.IsEmpty()) |  | 
|  494     registrar_.Add(this, chrome::NOTIFICATION_TOKEN_AVAILABLE, |  | 
|  495                    content::Source<TokenService>(token_service)); |  | 
|  496  |  | 
|  497   // Deny concurrent requests and bail without a valid Gaia login refresh token. |  | 
|  498   if (access_token_fetcher_.get() || !token_service->HasOAuthLoginToken()) |  440   if (access_token_fetcher_.get() || !token_service->HasOAuthLoginToken()) | 
|  499     return; |  441     return; | 
|  500  |  442  | 
|  501   auth_retry_timer_.Stop(); |  443   auth_retry_timer_.Stop(); | 
|  502   access_token_fetcher_.reset( |  444   access_token_fetcher_.reset( | 
|  503       new OAuth2AccessTokenFetcher(this, profile_->GetRequestContext())); |  445       new OAuth2AccessTokenFetcher(this, profile_->GetRequestContext())); | 
|  504   GaiaUrls* gaia_urls = GaiaUrls::GetInstance(); |  446   GaiaUrls* gaia_urls = GaiaUrls::GetInstance(); | 
|  505   access_token_fetcher_->Start(gaia_urls->oauth2_chrome_client_id(), |  447   access_token_fetcher_->Start(gaia_urls->oauth2_chrome_client_id(), | 
|  506                                gaia_urls->oauth2_chrome_client_secret(), |  448                                gaia_urls->oauth2_chrome_client_secret(), | 
|  507                                token_service->GetOAuth2LoginRefreshToken(), |  449                                token_service->GetOAuth2LoginRefreshToken(), | 
|  508                                std::vector<std::string>(1, kCloudPrintAuth)); |  450                                std::vector<std::string>(1, kCloudPrintAuth)); | 
|  509 } |  451 } | 
|  510  |  452  | 
|  511 void ChromeToMobileService::RequestDeviceSearch() { |  453 void ChromeToMobileService::RequestAccountInfo() { | 
|  512   DCHECK(sync_invalidation_enabled_); |  454   // Deny concurrent requests. | 
|  513   if (access_token_.empty()) { |  455   if (account_info_request_.get()) | 
|  514     // Enqueue this task to perform after obtaining an access token. |  456     return; | 
|  515     task_queue_.push(base::Bind(&ChromeToMobileService::RequestDeviceSearch, |  457  | 
|  516                                 weak_ptr_factory_.GetWeakPtr())); |  458   std::string url_string = StringPrintf(kAccountInfoURL, | 
|  517     RequestAccessToken(); |  459       base::GenerateGUID().c_str(), kChromeToMobileRequestor); | 
 |  460   GURL url(url_string); | 
 |  461  | 
 |  462   // Account information is read from the profile's cookie. If cookies are | 
 |  463   // blocked, access cloud print directly to list any potential devices. | 
 |  464   scoped_refptr<CookieSettings> cookie_settings = | 
 |  465       CookieSettings::Factory::GetForProfile(profile_); | 
 |  466   if (cookie_settings && !cookie_settings->IsReadingCookieAllowed(url, url)) { | 
 |  467     cloud_print_accessible_ = true; | 
 |  468     RequestMobileListUpdate(); | 
|  518     return; |  469     return; | 
|  519   } |  470   } | 
|  520  |  471  | 
 |  472   account_info_request_.reset( | 
 |  473       net::URLFetcher::Create(url, net::URLFetcher::GET, this)); | 
 |  474   account_info_request_->SetRequestContext(profile_->GetRequestContext()); | 
 |  475   account_info_request_->SetMaxRetries(kMaxRetries); | 
 |  476   // This request sends the user's cookie to check the cloud print service flag. | 
 |  477   account_info_request_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES); | 
 |  478   account_info_request_->Start(); | 
 |  479 } | 
 |  480  | 
 |  481 void ChromeToMobileService::RequestDeviceSearch() { | 
 |  482   // Deny requests if cloud print is inaccessible, and deny concurrent requests. | 
 |  483   if (!cloud_print_accessible_ || search_request_.get()) | 
 |  484     return; | 
 |  485  | 
 |  486   PrefService* prefs = profile_->GetPrefs(); | 
 |  487   base::TimeTicks previous_search_time = base::TimeTicks::FromInternalValue( | 
 |  488       prefs->GetInt64(prefs::kChromeToMobileTimestamp)); | 
 |  489  | 
 |  490   // Deny requests before the delay period has passed since the last request. | 
 |  491   base::TimeDelta elapsed_time = base::TimeTicks::Now() - previous_search_time; | 
 |  492   if (!previous_search_time.is_null() && | 
 |  493       elapsed_time.InHours() < kSearchRequestDelayHours) | 
 |  494     return; | 
 |  495  | 
|  521   LogMetric(DEVICES_REQUESTED); |  496   LogMetric(DEVICES_REQUESTED); | 
|  522  |  497  | 
|  523   net::URLFetcher* search_request = net::URLFetcher::Create( |  498   const GURL service_url(cloud_print_url_->GetCloudPrintServiceURL()); | 
|  524       GetSearchURL(cloud_print_url_), net::URLFetcher::GET, this); |  499   search_request_.reset(net::URLFetcher::Create(GetSearchURL(service_url), | 
|  525   InitRequest(search_request); |  500                                                 net::URLFetcher::GET, this)); | 
|  526   search_request->Start(); |  501   InitRequest(search_request_.get()); | 
 |  502   search_request_->Start(); | 
|  527 } |  503 } | 
|  528  |  504  | 
|  529 void ChromeToMobileService::HandleSearchResponse( |  505 void ChromeToMobileService::HandleAccountInfoResponse() { | 
|  530     const net::URLFetcher* source) { |  | 
|  531   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |  506   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 
|  532   DCHECK_EQ(source->GetURL(), GetSearchURL(cloud_print_url_)); |  | 
|  533  |  507  | 
|  534   std::string data; |  508   std::string data; | 
 |  509   account_info_request_->GetResponseAsString(&data); | 
 |  510   account_info_request_.reset(); | 
 |  511  | 
 |  512   ListValue* services = NULL; | 
 |  513   DictionaryValue* dictionary = NULL; | 
 |  514   scoped_ptr<Value> json(base::JSONReader::Read(data)); | 
 |  515   StringValue cloud_print_service(kCloudPrintSerivceValue); | 
 |  516   if (json.get() && json->GetAsDictionary(&dictionary) && dictionary && | 
 |  517       dictionary->GetList(kAccountServicesKey, &services) && services && | 
 |  518       services->Find(cloud_print_service) != services->end()) { | 
 |  519     cloud_print_accessible_ = true; | 
 |  520     RequestMobileListUpdate(); | 
 |  521   } | 
 |  522 } | 
 |  523  | 
 |  524 void ChromeToMobileService::HandleSearchResponse() { | 
 |  525   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 
 |  526  | 
 |  527   std::string data; | 
 |  528   search_request_->GetResponseAsString(&data); | 
 |  529   search_request_.reset(); | 
 |  530  | 
|  535   ListValue* list = NULL; |  531   ListValue* list = NULL; | 
|  536   DictionaryValue* dictionary = NULL; |  532   DictionaryValue* dictionary = NULL; | 
|  537   source->GetResponseAsString(&data); |  | 
|  538   scoped_ptr<Value> json(base::JSONReader::Read(data)); |  533   scoped_ptr<Value> json(base::JSONReader::Read(data)); | 
|  539   if (json.get() && json->GetAsDictionary(&dictionary) && dictionary && |  534   if (json.get() && json->GetAsDictionary(&dictionary) && dictionary && | 
|  540       dictionary->GetList(cloud_print::kPrinterListValue, &list)) { |  535       dictionary->GetList(cloud_print::kPrinterListValue, &list)) { | 
|  541     ListValue mobiles; |  536     ListValue mobiles; | 
|  542     std::string type, name, id; |  537     std::string type, name, id; | 
|  543     DictionaryValue* printer = NULL; |  538     DictionaryValue* printer = NULL; | 
|  544     DictionaryValue* mobile = NULL; |  539     DictionaryValue* mobile = NULL; | 
|  545     for (size_t index = 0; index < list->GetSize(); ++index) { |  540     for (size_t index = 0; index < list->GetSize(); ++index) { | 
|  546       if (list->GetDictionary(index, &printer) && |  541       if (list->GetDictionary(index, &printer) && | 
|  547           printer->GetString("type", &type) && |  542           printer->GetString("type", &type) && | 
|  548           (type.compare(kTypeAndroid) == 0 || type.compare(kTypeIOS) == 0)) { |  543           (type.compare(kTypeAndroid) == 0 || type.compare(kTypeIOS) == 0)) { | 
|  549         // Copy just the requisite values from the full |printer| definition. |  544         // Copy just the requisite values from the full |printer| definition. | 
|  550         if (printer->GetString("displayName", &name) && |  545         if (printer->GetString("displayName", &name) && | 
|  551             printer->GetString("id", &id)) { |  546             printer->GetString("id", &id)) { | 
|  552           mobile = new DictionaryValue(); |  547           mobile = new DictionaryValue(); | 
|  553           mobile->SetString("type", type); |  548           mobile->SetString("type", type); | 
|  554           mobile->SetString("name", name); |  549           mobile->SetString("name", name); | 
|  555           mobile->SetString("id", id); |  550           mobile->SetString("id", id); | 
|  556           mobiles.Append(mobile); |  551           mobiles.Append(mobile); | 
|  557         } else { |  552         } else { | 
|  558           NOTREACHED(); |  553           NOTREACHED(); | 
|  559         } |  554         } | 
|  560       } |  555       } | 
|  561     } |  556     } | 
|  562  |  557  | 
|  563     // Update the cached mobile device list in profile prefs. |  558     // Update the mobile list and timestamp in prefs. | 
|  564     profile_->GetPrefs()->Set(prefs::kChromeToMobileDeviceList, mobiles); |  559     PrefService* prefs = profile_->GetPrefs(); | 
 |  560     prefs->Set(prefs::kChromeToMobileDeviceList, mobiles); | 
 |  561     prefs->SetInt64(prefs::kChromeToMobileTimestamp, | 
 |  562                     base::TimeTicks::Now().ToInternalValue()); | 
|  565  |  563  | 
|  566     if (HasMobiles()) |  564     if (HasMobiles()) | 
|  567       LogMetric(DEVICES_AVAILABLE); |  565       LogMetric(DEVICES_AVAILABLE); | 
|  568     UpdateCommandState(); |  566     UpdateCommandState(); | 
|  569   } |  567   } | 
|  570 } |  568 } | 
|  571  |  569  | 
|  572 void ChromeToMobileService::HandleSubmitResponse( |  570 void ChromeToMobileService::HandleSubmitResponse( | 
|  573     const net::URLFetcher* source) { |  571     const net::URLFetcher* source) { | 
|  574   // Get the observer for this response; bail if there is none or it is NULL. |  572   // Get the observer for this response; bail if there is none or it is NULL. | 
| (...skipping 24 matching lines...) Expand all  Loading... | 
|  599  |  597  | 
|  600       // Ensure a second response is not sent after reporting failure below. |  598       // Ensure a second response is not sent after reporting failure below. | 
|  601       request_observer_map_.erase(other); |  599       request_observer_map_.erase(other); | 
|  602       break; |  600       break; | 
|  603     } |  601     } | 
|  604   } |  602   } | 
|  605  |  603  | 
|  606   LogMetric(success ? SEND_SUCCESS : SEND_ERROR); |  604   LogMetric(success ? SEND_SUCCESS : SEND_ERROR); | 
|  607   observer->OnSendComplete(success); |  605   observer->OnSendComplete(success); | 
|  608 } |  606 } | 
| OLD | NEW |