Chromium Code Reviews| 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/chromeos/gdata/gdata_contacts_service.h" | 5 #include "chrome/browser/chromeos/gdata/gdata_contacts_service.h" |
| 6 | 6 |
| 7 #include <cstring> | 7 #include <cstring> |
| 8 #include <string> | 8 #include <string> |
| 9 #include <map> | 9 #include <map> |
| 10 #include <utility> | 10 #include <utility> |
| 11 | 11 |
| 12 #include "base/json/json_value_converter.h" | |
| 12 #include "base/json/json_writer.h" | 13 #include "base/json/json_writer.h" |
| 13 #include "base/logging.h" | 14 #include "base/logging.h" |
| 14 #include "base/memory/weak_ptr.h" | 15 #include "base/memory/weak_ptr.h" |
| 15 #include "base/stl_util.h" | 16 #include "base/stl_util.h" |
| 16 #include "base/time.h" | 17 #include "base/time.h" |
| 17 #include "base/timer.h" | 18 #include "base/timer.h" |
| 18 #include "base/values.h" | 19 #include "base/values.h" |
| 19 #include "chrome/browser/chromeos/contacts/contact.pb.h" | 20 #include "chrome/browser/chromeos/contacts/contact.pb.h" |
| 20 #include "chrome/browser/chromeos/gdata/gdata_operation_registry.h" | 21 #include "chrome/browser/chromeos/gdata/gdata_operation_registry.h" |
| 21 #include "chrome/browser/chromeos/gdata/gdata_operation_runner.h" | 22 #include "chrome/browser/chromeos/gdata/gdata_operation_runner.h" |
| 22 #include "chrome/browser/chromeos/gdata/gdata_operations.h" | 23 #include "chrome/browser/chromeos/gdata/gdata_operations.h" |
| 23 #include "chrome/browser/chromeos/gdata/gdata_params.h" | 24 #include "chrome/browser/chromeos/gdata/gdata_params.h" |
| 24 #include "chrome/browser/chromeos/gdata/gdata_util.h" | 25 #include "chrome/browser/chromeos/gdata/gdata_util.h" |
| 25 #include "content/public/browser/browser_thread.h" | 26 #include "content/public/browser/browser_thread.h" |
| 26 | 27 |
| 27 using content::BrowserThread; | 28 using content::BrowserThread; |
| 28 | 29 |
| 29 namespace gdata { | 30 namespace gdata { |
| 30 | 31 |
| 31 namespace { | 32 namespace { |
| 32 | 33 |
| 33 // Maximum number of profile photos that we'll download per second. | 34 // Maximum number of profile photos that we'll download per second. |
| 34 // At values above 10, Google starts returning 503 errors. | 35 // At values above 10, Google starts returning 503 errors. |
| 35 const int kMaxPhotoDownloadsPerSecond = 10; | 36 const int kMaxPhotoDownloadsPerSecond = 10; |
| 36 | 37 |
| 38 // Hardcoded system group ID for the "My Contacts" group, per | |
| 39 // https://developers.google.com/google-apps/contacts/v3/#contact_group_entry. | |
| 40 const char kMyContactsSystemGroupId[] = "Contacts"; | |
| 41 | |
| 42 // Top-level field in a contact groups feed containing the list of entries. | |
| 43 const char kGroupEntryField[] = "feed.entry"; | |
| 44 | |
| 45 // Field in group entries containing the system group ID (e.g. ID "Contacts" | |
| 46 // for the "My Contacts" system group). See | |
| 47 // https://developers.google.com/google-apps/contacts/v3/#contact_group_entry | |
| 48 // for more details. | |
| 49 const char kSystemGroupIdField[] = "gContact$systemGroup.id"; | |
| 50 | |
| 37 // Field in the top-level object containing the contacts feed. | 51 // Field in the top-level object containing the contacts feed. |
| 38 const char kFeedField[] = "feed"; | 52 const char kFeedField[] = "feed"; |
| 39 | 53 |
| 40 // Field in the contacts feed containing a list of category information, along | 54 // Field in the contacts feed containing a list of category information, along |
| 41 // with fields within the dictionaries contained in the list and expected | 55 // with fields within the dictionaries contained in the list and expected |
| 42 // values. | 56 // values. |
| 43 const char kCategoryField[] = "category"; | 57 const char kCategoryField[] = "category"; |
| 44 const char kCategorySchemeField[] = "scheme"; | 58 const char kCategorySchemeField[] = "scheme"; |
| 45 const char kCategorySchemeValue[] = "http://schemas.google.com/g/2005#kind"; | 59 const char kCategorySchemeValue[] = "http://schemas.google.com/g/2005#kind"; |
| 46 const char kCategoryTermField[] = "term"; | 60 const char kCategoryTermField[] = "term"; |
| 47 const char kCategoryTermValue[] = | 61 const char kCategoryTermValue[] = |
| 48 "http://schemas.google.com/contact/2008#contact"; | 62 "http://schemas.google.com/contact/2008#contact"; |
| 49 | 63 |
| 50 // Field in the contacts feed containing a list of contact entries. | 64 // Field in the contacts feed containing a list of contact entries. |
| 51 const char kEntryField[] = "entry"; | 65 const char kEntryField[] = "entry"; |
| 52 | 66 |
| 67 // Field in group and contact entries containing the item's ID. | |
| 68 const char kIdField[] = "id.$t"; | |
| 69 | |
| 53 // Top-level fields in contact entries. | 70 // Top-level fields in contact entries. |
| 54 const char kIdField[] = "id.$t"; | |
| 55 const char kDeletedField[] = "gd$deleted"; | 71 const char kDeletedField[] = "gd$deleted"; |
| 56 const char kFullNameField[] = "gd$name.gd$fullName.$t"; | 72 const char kFullNameField[] = "gd$name.gd$fullName.$t"; |
| 57 const char kGivenNameField[] = "gd$name.gd$givenName.$t"; | 73 const char kGivenNameField[] = "gd$name.gd$givenName.$t"; |
| 58 const char kAdditionalNameField[] = "gd$name.gd$additionalName.$t"; | 74 const char kAdditionalNameField[] = "gd$name.gd$additionalName.$t"; |
| 59 const char kFamilyNameField[] = "gd$name.gd$familyName.$t"; | 75 const char kFamilyNameField[] = "gd$name.gd$familyName.$t"; |
| 60 const char kNamePrefixField[] = "gd$name.gd$namePrefix.$t"; | 76 const char kNamePrefixField[] = "gd$name.gd$namePrefix.$t"; |
| 61 const char kNameSuffixField[] = "gd$name.gd$nameSuffix.$t"; | 77 const char kNameSuffixField[] = "gd$name.gd$nameSuffix.$t"; |
| 62 const char kEmailField[] = "gd$email"; | 78 const char kEmailField[] = "gd$email"; |
| 63 const char kPhoneField[] = "gd$phoneNumber"; | 79 const char kPhoneField[] = "gd$phoneNumber"; |
| 64 const char kPostalAddressField[] = "gd$structuredPostalAddress"; | 80 const char kPostalAddressField[] = "gd$structuredPostalAddress"; |
| (...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 299 } | 315 } |
| 300 im->set_primary(IsAddressPrimary(*im_dict)); | 316 im->set_primary(IsAddressPrimary(*im_dict)); |
| 301 InitAddressType(*im_dict, im->mutable_type()); | 317 InitAddressType(*im_dict, im->mutable_type()); |
| 302 im->set_protocol(GetInstantMessagingProtocol(*im_dict)); | 318 im->set_protocol(GetInstantMessagingProtocol(*im_dict)); |
| 303 } | 319 } |
| 304 } | 320 } |
| 305 | 321 |
| 306 return true; | 322 return true; |
| 307 } | 323 } |
| 308 | 324 |
| 325 // Structure into which we parse the contact groups feed using | |
| 326 // JSONValueConverter. | |
| 327 struct ContactGroups { | |
| 328 struct ContactGroup { | |
| 329 std::string group_id; | |
| 330 std::string system_group_id; | |
|
satorux1
2012/08/08 13:37:16
What are the difference between the two IDs? Examp
Daniel Erat
2012/08/08 14:30:50
Described just below in GetGroupIdForSystemGroup()
| |
| 331 }; | |
| 332 | |
| 333 // Given a system group ID (e.g. "Contacts" for the "My Contacts" group), | |
| 334 // returns the corresponding group ID (e.g. | |
| 335 // "http://www.google.com/m8/feeds/groups/user%40gmail.com/base/6"), or an | |
| 336 // empty string if the requested system group wasn't present. | |
| 337 std::string GetGroupIdForSystemGroup(const std::string& system_group_id) { | |
|
satorux1
2012/08/08 13:37:16
Is this used?
Daniel Erat
2012/08/08 14:30:50
Whoops, I added this but somehow didn't call it la
| |
| 338 for (size_t i = 0; i < groups.size(); ++i) { | |
| 339 const ContactGroup& group = *groups[i]; | |
| 340 if (group.system_group_id == system_group_id) | |
| 341 return group.group_id; | |
| 342 } | |
| 343 return std::string(); | |
| 344 } | |
| 345 | |
| 346 // Given |value| corresponding to a dictionary in a contact group feed's | |
| 347 // "entry" list, fills |result| with information about the group. | |
| 348 static bool GetContactGroup(const base::Value* value, ContactGroup* result) { | |
| 349 DCHECK(value); | |
| 350 DCHECK(result); | |
| 351 const base::DictionaryValue* dict = NULL; | |
| 352 if (!value->GetAsDictionary(&dict)) | |
| 353 return false; | |
| 354 | |
| 355 dict->GetString(kIdField, &result->group_id); | |
| 356 dict->GetString(kSystemGroupIdField, &result->system_group_id); | |
| 357 return true; | |
| 358 } | |
| 359 | |
| 360 static void RegisterJSONConverter( | |
| 361 base::JSONValueConverter<ContactGroups>* converter) { | |
| 362 DCHECK(converter); | |
| 363 converter->RegisterRepeatedCustomValue<ContactGroup>( | |
| 364 kGroupEntryField, &ContactGroups::groups, &GetContactGroup); | |
| 365 } | |
| 366 | |
| 367 ScopedVector<ContactGroup> groups; | |
| 368 }; | |
| 369 | |
| 309 } // namespace | 370 } // namespace |
| 310 | 371 |
| 311 // This class handles a single request to download all of a user's contacts. | 372 // This class handles a single request to download all of a user's contacts. |
| 312 // | 373 // |
| 313 // First, the contacts feed is downloaded via GetContactsOperation and parsed. | 374 // First, the feed containing the user's contact groups is downloaded via |
| 375 // GetContactGroupsOperation and examined to find the ID for the "My Contacts" | |
| 376 // group (by default, the contacts API also returns suggested contacts). The | |
| 377 // group ID is cached in GDataContactsService so that this step can be skipped | |
| 378 // by later DownloadContactRequests. | |
| 379 // | |
| 380 // Next, the contacts feed is downloaded via GetContactsOperation and parsed. | |
| 314 // Individual contacts::Contact objects are created using the data from the | 381 // Individual contacts::Contact objects are created using the data from the |
| 315 // feed. Next, GetContactPhotoOperations are created and used to start | 382 // feed. |
| 316 // downloading contacts' photos in parallel. When all photos have been | 383 // |
| 317 // downloaded, the contacts are passed to the passed-in callback. | 384 // Finally, GetContactPhotoOperations are created and used to start downloading |
| 318 class GDataContactsService::DownloadContactsRequest | 385 // contacts' photos in parallel. When all photos have been downloaded, the |
| 319 : public base::SupportsWeakPtr<DownloadContactsRequest> { | 386 // contacts are passed to the passed-in callback. |
| 387 class GDataContactsService::DownloadContactsRequest { | |
| 320 public: | 388 public: |
| 321 DownloadContactsRequest(GDataContactsService* service, | 389 DownloadContactsRequest(GDataContactsService* service, |
| 322 GDataOperationRunner* runner, | 390 GDataOperationRunner* runner, |
| 323 SuccessCallback success_callback, | 391 SuccessCallback success_callback, |
| 324 FailureCallback failure_callback, | 392 FailureCallback failure_callback, |
| 325 const base::Time& min_update_time) | 393 const base::Time& min_update_time) |
| 326 : service_(service), | 394 : service_(service), |
| 327 runner_(runner), | 395 runner_(runner), |
| 328 success_callback_(success_callback), | 396 success_callback_(success_callback), |
| 329 failure_callback_(failure_callback), | 397 failure_callback_(failure_callback), |
| 330 min_update_time_(min_update_time), | 398 min_update_time_(min_update_time), |
| 331 contacts_(new ScopedVector<contacts::Contact>), | 399 contacts_(new ScopedVector<contacts::Contact>), |
| 400 my_contacts_group_id_(service->cached_my_contacts_group_id_), | |
| 332 num_in_progress_photo_downloads_(0), | 401 num_in_progress_photo_downloads_(0), |
| 333 photo_download_failed_(false) { | 402 photo_download_failed_(false), |
| 403 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { | |
| 404 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 334 DCHECK(service_); | 405 DCHECK(service_); |
| 335 DCHECK(runner_); | 406 DCHECK(runner_); |
| 336 } | 407 } |
| 337 | 408 |
| 338 ~DownloadContactsRequest() { | 409 ~DownloadContactsRequest() { |
| 339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 410 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 340 service_ = NULL; | 411 service_ = NULL; |
| 341 runner_ = NULL; | 412 runner_ = NULL; |
| 342 } | 413 } |
| 343 | 414 |
| 344 // Issues the initial request to download the contact feed. | 415 const std::string my_contacts_group_id() const { |
| 416 return my_contacts_group_id_; | |
| 417 } | |
| 418 | |
| 419 // Begins the contacts-downloading process. If the ID for the "My Contacts" | |
| 420 // group has previously been cached, then the contacts download is started. | |
| 421 // Otherwise, the contact groups download is started. | |
| 345 void Run() { | 422 void Run() { |
| 423 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 424 if (!my_contacts_group_id_.empty()) { | |
| 425 StartContactsDownload(); | |
| 426 } else { | |
| 427 GetContactGroupsOperation* operation = | |
| 428 new GetContactGroupsOperation( | |
| 429 runner_->operation_registry(), | |
| 430 base::Bind(&DownloadContactsRequest::HandleGroupsFeedData, | |
| 431 weak_ptr_factory_.GetWeakPtr())); | |
| 432 if (!service_->groups_feed_url_for_testing_.is_empty()) { | |
| 433 operation->set_feed_url_for_testing( | |
| 434 service_->groups_feed_url_for_testing_); | |
| 435 } | |
| 436 runner_->StartOperationWithRetry(operation); | |
| 437 } | |
| 438 } | |
| 439 | |
| 440 private: | |
| 441 // Invokes the failure callback and notifies GDataContactsService that the | |
| 442 // request is done. | |
| 443 void ReportFailure() { | |
| 444 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 445 failure_callback_.Run(); | |
| 446 service_->OnRequestComplete(this); | |
| 447 } | |
| 448 | |
| 449 // Callback for GetContactGroupsOperation calls. Starts downloading the | |
| 450 // actual contacts after finding the "My Contacts" group ID. | |
| 451 void HandleGroupsFeedData(GDataErrorCode error, | |
| 452 scoped_ptr<base::Value> feed_data) { | |
| 453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 454 if (error != HTTP_SUCCESS) { | |
| 455 LOG(WARNING) << "Got error " << error << " while downloading groups"; | |
| 456 ReportFailure(); | |
| 457 return; | |
| 458 } | |
| 459 | |
| 460 VLOG(2) << "Got groups feed data:\n" | |
| 461 << PrettyPrintValue(*(feed_data.get())); | |
| 462 ContactGroups groups; | |
| 463 base::JSONValueConverter<ContactGroups> converter; | |
| 464 if (!converter.Convert(*feed_data, &groups)) { | |
| 465 LOG(WARNING) << "Unable to parse groups feed"; | |
| 466 ReportFailure(); | |
| 467 return; | |
| 468 } | |
| 469 | |
| 470 for (size_t i = 0; i < groups.groups.size(); ++i) { | |
| 471 const ContactGroups::ContactGroup& group = *groups.groups[i]; | |
| 472 if (group.system_group_id == kMyContactsSystemGroupId && | |
| 473 !group.group_id.empty()) { | |
| 474 my_contacts_group_id_ = group.group_id; | |
| 475 StartContactsDownload(); | |
| 476 return; | |
| 477 } | |
| 478 } | |
| 479 | |
| 480 LOG(WARNING) << "Unable to find ID for \"My Contacts\" group"; | |
| 481 ReportFailure(); | |
| 482 } | |
| 483 | |
| 484 // Starts a download of the contacts from the "My Contacts" group. | |
| 485 void StartContactsDownload() { | |
| 486 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 346 GetContactsOperation* operation = | 487 GetContactsOperation* operation = |
| 347 new GetContactsOperation( | 488 new GetContactsOperation( |
| 348 runner_->operation_registry(), | 489 runner_->operation_registry(), |
| 490 my_contacts_group_id_, | |
|
satorux1
2012/08/08 13:37:16
Since we know that this will always be "Contacts",
Daniel Erat
2012/08/08 14:30:50
"Contacts" is the "My Contacts" group's system gro
satorux1
2012/08/08 14:58:10
Ah I see. That explains.
| |
| 349 min_update_time_, | 491 min_update_time_, |
| 350 base::Bind(&DownloadContactsRequest::HandleFeedData, | 492 base::Bind(&DownloadContactsRequest::HandleContactsFeedData, |
| 351 base::Unretained(this))); | 493 weak_ptr_factory_.GetWeakPtr())); |
| 352 if (!service_->feed_url_for_testing_.is_empty()) | 494 if (!service_->contacts_feed_url_for_testing_.is_empty()) { |
| 353 operation->set_feed_url_for_testing(service_->feed_url_for_testing_); | 495 operation->set_feed_url_for_testing( |
| 354 | 496 service_->contacts_feed_url_for_testing_); |
| 497 } | |
| 355 runner_->StartOperationWithRetry(operation); | 498 runner_->StartOperationWithRetry(operation); |
| 356 } | 499 } |
| 357 | 500 |
| 358 private: | |
| 359 // Callback for GetContactsOperation calls. | 501 // Callback for GetContactsOperation calls. |
| 360 void HandleFeedData(GDataErrorCode error, | 502 void HandleContactsFeedData(GDataErrorCode error, |
| 361 scoped_ptr<base::Value> feed_data) { | 503 scoped_ptr<base::Value> feed_data) { |
| 362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 504 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 363 if (error != HTTP_SUCCESS) { | 505 if (error != HTTP_SUCCESS) { |
| 364 LOG(WARNING) << "Got error " << error << " while downloading contacts"; | 506 LOG(WARNING) << "Got error " << error << " while downloading contacts"; |
| 365 failure_callback_.Run(); | 507 ReportFailure(); |
| 366 service_->OnRequestComplete(this); | |
| 367 return; | 508 return; |
| 368 } | 509 } |
| 369 | 510 |
| 370 VLOG(2) << "Got feed data:\n" << PrettyPrintValue(*(feed_data.get())); | 511 VLOG(2) << "Got contacts feed data:\n" |
| 371 if (!ProcessFeedData(*feed_data.get())) { | 512 << PrettyPrintValue(*(feed_data.get())); |
| 372 LOG(WARNING) << "Unable to process feed data"; | 513 if (!ProcessContactsFeedData(*feed_data.get())) { |
| 373 failure_callback_.Run(); | 514 LOG(WARNING) << "Unable to process contacts feed data"; |
| 374 service_->OnRequestComplete(this); | 515 ReportFailure(); |
| 375 return; | 516 return; |
| 376 } | 517 } |
| 377 | 518 |
| 378 StartPhotoDownloads(); | 519 StartPhotoDownloads(); |
| 379 photo_download_timer_.Start( | 520 photo_download_timer_.Start( |
| 380 FROM_HERE, service_->photo_download_timer_interval_, | 521 FROM_HERE, service_->photo_download_timer_interval_, |
| 381 this, &DownloadContactsRequest::StartPhotoDownloads); | 522 this, &DownloadContactsRequest::StartPhotoDownloads); |
| 382 CheckCompletion(); | 523 CheckCompletion(); |
| 383 } | 524 } |
| 384 | 525 |
| 385 // Processes the raw contacts feed from |feed_data| and fills |contacts_|. | 526 // Processes the raw contacts feed from |feed_data| and fills |contacts_|. |
| 386 // Returns true on success. | 527 // Returns true on success. |
| 387 bool ProcessFeedData(const base::Value& feed_data) { | 528 bool ProcessContactsFeedData(const base::Value& feed_data) { |
| 529 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 388 const DictionaryValue* toplevel_dict = NULL; | 530 const DictionaryValue* toplevel_dict = NULL; |
| 389 if (!feed_data.GetAsDictionary(&toplevel_dict)) { | 531 if (!feed_data.GetAsDictionary(&toplevel_dict)) { |
| 390 LOG(WARNING) << "Top-level object is not a dictionary"; | 532 LOG(WARNING) << "Top-level object is not a dictionary"; |
| 391 return false; | 533 return false; |
| 392 } | 534 } |
| 393 | 535 |
| 394 const DictionaryValue* feed_dict = NULL; | 536 const DictionaryValue* feed_dict = NULL; |
| 395 if (!toplevel_dict->GetDictionary(kFeedField, &feed_dict)) { | 537 if (!toplevel_dict->GetDictionary(kFeedField, &feed_dict)) { |
| 396 LOG(WARNING) << "Feed dictionary missing"; | 538 LOG(WARNING) << "Feed dictionary missing"; |
| 397 return false; | 539 return false; |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 466 | 608 |
| 467 // If we're done downloading photos, invokes a callback and deletes |this|. | 609 // If we're done downloading photos, invokes a callback and deletes |this|. |
| 468 // Otherwise, starts one or more downloads of URLs from | 610 // Otherwise, starts one or more downloads of URLs from |
| 469 // |contacts_needing_photo_downloads_|. | 611 // |contacts_needing_photo_downloads_|. |
| 470 void CheckCompletion() { | 612 void CheckCompletion() { |
| 471 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 613 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 472 if (contacts_needing_photo_downloads_.empty() && | 614 if (contacts_needing_photo_downloads_.empty() && |
| 473 num_in_progress_photo_downloads_ == 0) { | 615 num_in_progress_photo_downloads_ == 0) { |
| 474 VLOG(1) << "Done downloading photos; invoking callback"; | 616 VLOG(1) << "Done downloading photos; invoking callback"; |
| 475 photo_download_timer_.Stop(); | 617 photo_download_timer_.Stop(); |
| 476 if (photo_download_failed_) | 618 if (photo_download_failed_) { |
| 477 failure_callback_.Run(); | 619 ReportFailure(); |
| 478 else | 620 } else { |
| 479 success_callback_.Run(contacts_.Pass()); | 621 success_callback_.Run(contacts_.Pass()); |
| 480 service_->OnRequestComplete(this); | 622 service_->OnRequestComplete(this); |
| 623 } | |
| 481 return; | 624 return; |
| 482 } | 625 } |
| 483 } | 626 } |
| 484 | 627 |
| 485 // Starts photo downloads for contacts in |contacts_needing_photo_downloads_|. | 628 // Starts photo downloads for contacts in |contacts_needing_photo_downloads_|. |
| 486 // Should be invoked only once per second. | 629 // Should be invoked only once per second. |
| 487 void StartPhotoDownloads() { | 630 void StartPhotoDownloads() { |
| 631 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 488 while (!contacts_needing_photo_downloads_.empty() && | 632 while (!contacts_needing_photo_downloads_.empty() && |
| 489 (num_in_progress_photo_downloads_ < | 633 (num_in_progress_photo_downloads_ < |
| 490 service_->max_photo_downloads_per_second_)) { | 634 service_->max_photo_downloads_per_second_)) { |
| 491 contacts::Contact* contact = contacts_needing_photo_downloads_.back(); | 635 contacts::Contact* contact = contacts_needing_photo_downloads_.back(); |
| 492 contacts_needing_photo_downloads_.pop_back(); | 636 contacts_needing_photo_downloads_.pop_back(); |
| 493 DCHECK(contact_photo_urls_.count(contact)); | 637 DCHECK(contact_photo_urls_.count(contact)); |
| 494 std::string url = contact_photo_urls_[contact]; | 638 std::string url = contact_photo_urls_[contact]; |
| 495 | 639 |
| 496 VLOG(1) << "Starting download of photo " << url << " for " | 640 VLOG(1) << "Starting download of photo " << url << " for " |
| 497 << contact->provider_id(); | 641 << contact->provider_id(); |
| 498 runner_->StartOperationWithRetry( | 642 runner_->StartOperationWithRetry( |
| 499 new GetContactPhotoOperation( | 643 new GetContactPhotoOperation( |
| 500 runner_->operation_registry(), | 644 runner_->operation_registry(), |
| 501 GURL(url), | 645 GURL(url), |
| 502 base::Bind(&DownloadContactsRequest::HandlePhotoData, | 646 base::Bind(&DownloadContactsRequest::HandlePhotoData, |
| 503 AsWeakPtr(), contact))); | 647 weak_ptr_factory_.GetWeakPtr(), |
| 648 contact))); | |
| 504 num_in_progress_photo_downloads_++; | 649 num_in_progress_photo_downloads_++; |
| 505 } | 650 } |
| 506 } | 651 } |
| 507 | 652 |
| 508 // Callback for GetContactPhotoOperation calls. Updates the associated | 653 // Callback for GetContactPhotoOperation calls. Updates the associated |
| 509 // Contact and checks for completion. | 654 // Contact and checks for completion. |
| 510 void HandlePhotoData(contacts::Contact* contact, | 655 void HandlePhotoData(contacts::Contact* contact, |
| 511 GDataErrorCode error, | 656 GDataErrorCode error, |
| 512 scoped_ptr<std::string> download_data) { | 657 scoped_ptr<std::string> download_data) { |
| 513 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 658 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 537 // Make sure we don't start any more downloads. | 682 // Make sure we don't start any more downloads. |
| 538 contacts_needing_photo_downloads_.clear(); | 683 contacts_needing_photo_downloads_.clear(); |
| 539 CheckCompletion(); | 684 CheckCompletion(); |
| 540 return; | 685 return; |
| 541 } | 686 } |
| 542 | 687 |
| 543 contact->set_raw_untrusted_photo(*download_data); | 688 contact->set_raw_untrusted_photo(*download_data); |
| 544 CheckCompletion(); | 689 CheckCompletion(); |
| 545 } | 690 } |
| 546 | 691 |
| 547 private: | |
| 548 typedef std::map<contacts::Contact*, std::string> ContactPhotoUrls; | 692 typedef std::map<contacts::Contact*, std::string> ContactPhotoUrls; |
| 549 | 693 |
| 550 GDataContactsService* service_; // not owned | 694 GDataContactsService* service_; // not owned |
| 551 GDataOperationRunner* runner_; // not owned | 695 GDataOperationRunner* runner_; // not owned |
| 552 | 696 |
| 553 SuccessCallback success_callback_; | 697 SuccessCallback success_callback_; |
| 554 FailureCallback failure_callback_; | 698 FailureCallback failure_callback_; |
| 555 | 699 |
| 556 base::Time min_update_time_; | 700 base::Time min_update_time_; |
| 557 | 701 |
| 558 scoped_ptr<ScopedVector<contacts::Contact> > contacts_; | 702 scoped_ptr<ScopedVector<contacts::Contact> > contacts_; |
| 559 | 703 |
| 704 // ID of the "My Contacts" contacts group. | |
| 705 std::string my_contacts_group_id_; | |
| 706 | |
| 560 // Map from a contact to the URL at which its photo is located. | 707 // Map from a contact to the URL at which its photo is located. |
| 561 // Contacts without photos do not appear in this map. | 708 // Contacts without photos do not appear in this map. |
| 562 ContactPhotoUrls contact_photo_urls_; | 709 ContactPhotoUrls contact_photo_urls_; |
| 563 | 710 |
| 564 // Invokes StartPhotoDownloads() once per second. | 711 // Invokes StartPhotoDownloads() once per second. |
| 565 base::RepeatingTimer<DownloadContactsRequest> photo_download_timer_; | 712 base::RepeatingTimer<DownloadContactsRequest> photo_download_timer_; |
| 566 | 713 |
| 567 // Contacts that have photos that we still need to start downloading. | 714 // Contacts that have photos that we still need to start downloading. |
| 568 // When we start a download, the contact is removed from this list. | 715 // When we start a download, the contact is removed from this list. |
| 569 std::vector<contacts::Contact*> contacts_needing_photo_downloads_; | 716 std::vector<contacts::Contact*> contacts_needing_photo_downloads_; |
| 570 | 717 |
| 571 // Number of in-progress photo downloads. | 718 // Number of in-progress photo downloads. |
| 572 int num_in_progress_photo_downloads_; | 719 int num_in_progress_photo_downloads_; |
| 573 | 720 |
| 574 // Did we encounter a fatal error while downloading a photo? | 721 // Did we encounter a fatal error while downloading a photo? |
| 575 bool photo_download_failed_; | 722 bool photo_download_failed_; |
| 576 | 723 |
| 724 // Note: This should remain the last member so it'll be destroyed and | |
| 725 // invalidate its weak pointers before any other members are destroyed. | |
| 726 base::WeakPtrFactory<DownloadContactsRequest> weak_ptr_factory_; | |
| 727 | |
| 577 DISALLOW_COPY_AND_ASSIGN(DownloadContactsRequest); | 728 DISALLOW_COPY_AND_ASSIGN(DownloadContactsRequest); |
| 578 }; | 729 }; |
| 579 | 730 |
| 580 GDataContactsService::GDataContactsService(Profile* profile) | 731 GDataContactsService::GDataContactsService(Profile* profile) |
| 581 : runner_(new GDataOperationRunner(profile)), | 732 : runner_(new GDataOperationRunner(profile)), |
| 582 max_photo_downloads_per_second_(kMaxPhotoDownloadsPerSecond), | 733 max_photo_downloads_per_second_(kMaxPhotoDownloadsPerSecond), |
| 583 photo_download_timer_interval_(base::TimeDelta::FromSeconds(1)) { | 734 photo_download_timer_interval_(base::TimeDelta::FromSeconds(1)) { |
| 584 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 735 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 585 } | 736 } |
| 586 | 737 |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 612 min_update_time); | 763 min_update_time); |
| 613 VLOG(1) << "Starting contacts download with request " << request; | 764 VLOG(1) << "Starting contacts download with request " << request; |
| 614 requests_.insert(request); | 765 requests_.insert(request); |
| 615 request->Run(); | 766 request->Run(); |
| 616 } | 767 } |
| 617 | 768 |
| 618 void GDataContactsService::OnRequestComplete(DownloadContactsRequest* request) { | 769 void GDataContactsService::OnRequestComplete(DownloadContactsRequest* request) { |
| 619 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 770 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 620 DCHECK(request); | 771 DCHECK(request); |
| 621 VLOG(1) << "Download request " << request << " complete"; | 772 VLOG(1) << "Download request " << request << " complete"; |
| 773 if (!request->my_contacts_group_id().empty()) | |
| 774 cached_my_contacts_group_id_ = request->my_contacts_group_id(); | |
| 622 requests_.erase(request); | 775 requests_.erase(request); |
| 623 delete request; | 776 delete request; |
| 624 } | 777 } |
| 625 | 778 |
| 626 } // namespace contacts | 779 } // namespace contacts |
| OLD | NEW |