Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(728)

Side by Side Diff: chrome/browser/chromeos/gdata/gdata_contacts_service.cc

Issue 10834220: contacts: Only download contacts from "My Contacts" group. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: minor changes Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698