1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/chromeos/login/user_image_manager_impl.h" 6 7 #include "base/bind.h" 8 #include "base/debug/trace_event.h" 9 #include "base/file_util.h" 10 #include "base/files/file_path.h" 11 #include "base/logging.h" 12 #include "base/metrics/histogram.h" 13 #include "base/path_service.h" 14 #include "base/prefs/pref_registry_simple.h" 15 #include "base/prefs/pref_service.h" 16 #include "base/rand_util.h" 17 #include "base/threading/worker_pool.h" 18 #include "base/time/time.h" 19 #include "base/values.h" 20 #include "chrome/browser/browser_process.h" 21 #include "chrome/browser/chrome_notification_types.h" 22 #include "chrome/browser/chromeos/login/default_user_images.h" 23 #include "chrome/browser/chromeos/login/helper.h" 24 #include "chrome/browser/chromeos/login/user_image.h" 25 #include "chrome/browser/chromeos/login/user_manager.h" 26 #include "chrome/browser/prefs/scoped_user_pref_update.h" 27 #include "chrome/browser/profiles/profile_downloader.h" 28 #include "chrome/browser/profiles/profile_manager.h" 29 #include "chrome/common/chrome_paths.h" 30 #include "content/public/browser/browser_thread.h" 31 #include "content/public/browser/notification_service.h" 32 #include "content/public/common/url_constants.h" 33 #include "ui/gfx/image/image_skia.h" 34 #include "ui/webui/web_ui_util.h" 35 36 using content::BrowserThread; 37 38 namespace chromeos { 39 40 namespace { 41 42 // A dictionary that maps usernames to old user image data with images stored in 43 // PNG format. Deprecated. 44 // TODO(ivankr): remove this const char after migration is gone. 45 const char kUserImages[] = "UserImages"; 46 47 // A dictionary that maps usernames to user image data with images stored in 48 // JPEG format. 49 const char kUserImageProperties[] = "user_image_info"; 50 51 // Names of user image properties. 52 const char kImagePathNodeName[] = "path"; 53 const char kImageIndexNodeName[] = "index"; 54 const char kImageURLNodeName[] = "url"; 55 56 // Delay betweeen user login and user image migration. 57 const int kUserImageMigrationDelaySec = 50; 58 59 // Delay betweeen user login and attempt to update user's profile data. 60 const int kProfileDataDownloadDelaySec = 10; 61 62 // Interval betweeen retries to update user's profile data. 63 const int kProfileDataDownloadRetryIntervalSec = 300; 64 65 // Delay betweeen subsequent profile refresh attempts (24 hrs). 66 const int kProfileRefreshIntervalSec = 24 * 3600; 67 68 const char kSafeImagePathExtension[] = ".jpg"; 69 70 // Enum for reporting histograms about profile picture download. 71 enum ProfileDownloadResult { 72 kDownloadSuccessChanged, 73 kDownloadSuccess, 74 kDownloadFailure, 75 kDownloadDefault, 76 kDownloadCached, 77 78 // Must be the last, convenient count. 79 kDownloadResultsCount 80 }; 81 82 // Time histogram prefix for a cached profile image download. 83 const char kProfileDownloadCachedTime[] = 84 "UserImage.ProfileDownloadTime.Cached"; 85 // Time histogram prefix for the default profile image download. 86 const char kProfileDownloadDefaultTime[] = 87 "UserImage.ProfileDownloadTime.Default"; 88 // Time histogram prefix for a failed profile image download. 89 const char kProfileDownloadFailureTime[] = 90 "UserImage.ProfileDownloadTime.Failure"; 91 // Time histogram prefix for a successful profile image download. 92 const char kProfileDownloadSuccessTime[] = 93 "UserImage.ProfileDownloadTime.Success"; 94 // Time histogram suffix for a profile image download after login. 95 const char kProfileDownloadReasonLoggedIn[] = "LoggedIn"; 96 // Time histogram suffix for a scheduled profile image download. 97 const char kProfileDownloadReasonScheduled[] = "Scheduled"; 98 // Time histogram suffix for a profile image download retry. 99 const char kProfileDownloadReasonRetry[] = "Retry"; 100 101 // Add a histogram showing the time it takes to download a profile image. 102 // Separate histograms are reported for each download |reason| and |result|. 103 void AddProfileImageTimeHistogram(ProfileDownloadResult result, 104 const std::string& download_reason, 105 const base::TimeDelta& time_delta) { 106 std::string histogram_name; 107 switch (result) { 108 case kDownloadFailure: 109 histogram_name = kProfileDownloadFailureTime; 110 break; 111 case kDownloadDefault: 112 histogram_name = kProfileDownloadDefaultTime; 113 break; 114 case kDownloadSuccess: 115 histogram_name = kProfileDownloadSuccessTime; 116 break; 117 case kDownloadCached: 118 histogram_name = kProfileDownloadCachedTime; 119 break; 120 default: 121 NOTREACHED(); 122 } 123 if (!download_reason.empty()) { 124 histogram_name += "."; 125 histogram_name += download_reason; 126 } 127 128 static const base::TimeDelta min_time = base::TimeDelta::FromMilliseconds(1); 129 static const base::TimeDelta max_time = base::TimeDelta::FromSeconds(50); 130 const size_t bucket_count(50); 131 132 base::HistogramBase* counter = base::Histogram::FactoryTimeGet( 133 histogram_name, min_time, max_time, bucket_count, 134 base::HistogramBase::kUmaTargetedHistogramFlag); 135 counter->AddTime(time_delta); 136 137 DVLOG(1) << "Profile image download time: " << time_delta.InSecondsF(); 138 } 139 140 // Deletes image file. 141 void DeleteImageFile(const std::string& image_path) { 142 if (image_path.empty()) 143 return; 144 base::FilePath fp(image_path); 145 BrowserThread::PostTask( 146 BrowserThread::FILE, 147 FROM_HERE, 148 base::Bind(base::IgnoreResult(&base::DeleteFile), 149 fp, /* recursive= */ false)); 150 } 151 152 // Converts |image_index| to UMA histogram value. 153 int ImageIndexToHistogramIndex(int image_index) { 154 switch (image_index) { 155 case User::kExternalImageIndex: 156 // TODO(ivankr): Distinguish this from selected from file. 157 return kHistogramImageFromCamera; 158 case User::kProfileImageIndex: 159 return kHistogramImageFromProfile; 160 default: 161 return image_index; 162 } 163 } 164 165 } // namespace 166 167 // static 168 int UserImageManagerImpl::user_image_migration_delay_sec = 169 kUserImageMigrationDelaySec; 170 171 // static 172 void UserImageManager::RegisterPrefs(PrefRegistrySimple* registry) { 173 registry->RegisterDictionaryPref(kUserImages); 174 registry->RegisterDictionaryPref(kUserImageProperties); 175 } 176 177 UserImageManagerImpl::UserImageManagerImpl() 178 : image_loader_(new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC)), 179 unsafe_image_loader_(new UserImageLoader(ImageDecoder::DEFAULT_CODEC)), 180 last_image_set_async_(false), 181 downloaded_profile_image_data_url_(content::kAboutBlankURL), 182 downloading_profile_image_(false), 183 migrate_current_user_on_load_(false) { 184 } 185 186 UserImageManagerImpl::~UserImageManagerImpl() { 187 } 188 189 void UserImageManagerImpl::LoadUserImages(const UserList& users) { 190 PrefService* local_state = g_browser_process->local_state(); 191 const DictionaryValue* prefs_images_unsafe = 192 local_state->GetDictionary(kUserImages); 193 const DictionaryValue* prefs_images = 194 local_state->GetDictionary(kUserImageProperties); 195 if (!prefs_images && !prefs_images_unsafe) 196 return; 197 198 for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) { 199 User* user = *it; 200 const base::DictionaryValue* image_properties = NULL; 201 bool needs_migration = false; // |true| if user has image in old format. 202 bool safe_source = false; // |true| if image loaded from safe source. 203 204 if (prefs_images_unsafe) { 205 needs_migration = prefs_images_unsafe->GetDictionaryWithoutPathExpansion( 206 user->email(), &image_properties); 207 } 208 if (prefs_images) { 209 safe_source = prefs_images->GetDictionaryWithoutPathExpansion( 210 user->email(), &image_properties); 211 } 212 213 if (needs_migration) 214 users_to_migrate_.insert(user->email()); 215 216 if (!image_properties) { 217 SetInitialUserImage(user->email()); 218 } else { 219 int image_index = User::kInvalidImageIndex; 220 image_properties->GetInteger(kImageIndexNodeName, &image_index); 221 222 if (image_index >= 0 && image_index < kDefaultImagesCount) { 223 user->SetImage(UserImage(GetDefaultImage(image_index)), 224 image_index); 225 } else if (image_index == User::kExternalImageIndex || 226 image_index == User::kProfileImageIndex) { 227 std::string image_path; 228 image_properties->GetString(kImagePathNodeName, &image_path); 229 // Path may be empty for profile images (meaning that the image 230 // hasn't been downloaded for the first time yet, in which case a 231 // download will be scheduled for |kProfileDataDownloadDelayMs| 232 // after user logs in). 233 DCHECK(!image_path.empty() || image_index == User::kProfileImageIndex); 234 std::string image_url; 235 image_properties->GetString(kImageURLNodeName, &image_url); 236 GURL image_gurl(image_url); 237 // Until image has been loaded, use the stub image (gray avatar). 238 user->SetStubImage(image_index, true); 239 user->SetImageURL(image_gurl); 240 if (!image_path.empty()) { 241 if (needs_migration) { 242 // Non-JPG image will be migrated once user logs in. 243 // Stub image will be used for now. Continue with other users. 244 continue; 245 } 246 DCHECK(safe_source); 247 if (!safe_source) 248 continue; 249 250 // Load user image asynchronously - at this point we are able to use 251 // JPEG image loaded since image comes from safe pref source 252 // i.e. converted to JPEG. 253 image_loader_->Start( 254 image_path, 0 /* no resize */, 255 base::Bind(&UserImageManagerImpl::SetUserImage, 256 base::Unretained(this), 257 user->email(), image_index, image_gurl)); 258 } 259 } else { 260 NOTREACHED(); 261 } 262 } 263 } 264 } 265 266 void UserImageManagerImpl::UserLoggedIn(const std::string& email, 267 bool user_is_new, 268 bool user_is_local) { 269 if (user_is_new) { 270 if (!user_is_local) 271 SetInitialUserImage(email); 272 } else { 273 User* user = UserManager::Get()->GetLoggedInUser(); 274 275 if (!user_is_local) { 276 // If current user image is profile image, it needs to be refreshed. 277 bool download_profile_image = 278 user->image_index() == User::kProfileImageIndex; 279 if (download_profile_image) 280 InitDownloadedProfileImage(); 281 282 // Download user's profile data (full name and optionally image) to see if 283 // it has changed. 284 BrowserThread::PostDelayedTask( 285 BrowserThread::UI, 286 FROM_HERE, 287 base::Bind(&UserImageManagerImpl::DownloadProfileData, 288 base::Unretained(this), 289 kProfileDownloadReasonLoggedIn, 290 download_profile_image), 291 base::TimeDelta::FromSeconds(kProfileDataDownloadDelaySec)); 292 } 293 294 UMA_HISTOGRAM_ENUMERATION("UserImage.LoggedIn", 295 ImageIndexToHistogramIndex(user->image_index()), 296 kHistogramImagesCount); 297 298 if (users_to_migrate_.count(email)) { 299 const DictionaryValue* prefs_images_unsafe = 300 g_browser_process->local_state()->GetDictionary(kUserImages); 301 const base::DictionaryValue* image_properties = NULL; 302 if (prefs_images_unsafe->GetDictionaryWithoutPathExpansion( 303 user->email(), &image_properties)) { 304 std::string image_path; 305 image_properties->GetString(kImagePathNodeName, &image_path); 306 if (!image_path.empty()) { 307 // User needs image format migration but 308 // first we need to load and decode that image. 309 LOG(INFO) << "Waiting for user image to load before migration"; 310 migrate_current_user_on_load_ = true; 311 unsafe_image_loader_->Start( 312 image_path, 0 /* no resize */, 313 base::Bind(&UserImageManagerImpl::SetUserImage, 314 base::Unretained(this), 315 user->email(), 316 user->image_index(), 317 user->image_url())); 318 } else { 319 // Otherwise migrate user image properties right away. 320 BrowserThread::PostDelayedTask( 321 BrowserThread::UI, 322 FROM_HERE, 323 base::Bind(&UserImageManagerImpl::MigrateUserImage, 324 base::Unretained(this)), 325 base::TimeDelta::FromSeconds(user_image_migration_delay_sec)); 326 } 327 } 328 } 329 } 330 331 if (!user_is_local) { 332 // Set up a repeating timer for refreshing the profile data. 333 profile_download_timer_.Start( 334 FROM_HERE, base::TimeDelta::FromSeconds(kProfileRefreshIntervalSec), 335 this, &UserImageManagerImpl::DownloadProfileDataScheduled); 336 } 337 } 338 339 void UserImageManagerImpl::SaveUserDefaultImageIndex( 340 const std::string& username, 341 int image_index) { 342 DCHECK(image_index >= 0 && image_index < kDefaultImagesCount); 343 SetUserImage(username, image_index, GURL(), 344 UserImage(GetDefaultImage(image_index))); 345 SaveImageToLocalState(username, "", image_index, GURL(), false); 346 } 347 348 void UserImageManagerImpl::SaveUserImage(const std::string& username, 349 const UserImage& user_image) { 350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 351 SaveUserImageInternal(username, User::kExternalImageIndex, 352 GURL(), user_image); 353 } 354 355 void UserImageManagerImpl::SaveUserImageFromFile(const std::string& username, 356 const base::FilePath& path) { 357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 358 // Always use unsafe image loader because we resize the image when saving 359 // anyway. 360 unsafe_image_loader_->Start( 361 path.value(), login::kMaxUserImageSize, 362 base::Bind(&UserImageManagerImpl::SaveUserImage, 363 base::Unretained(this), username)); 364 } 365 366 void UserImageManagerImpl::SaveUserImageFromProfileImage( 367 const std::string& username) { 368 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 369 if (!downloaded_profile_image_.isNull()) { 370 // Profile image has already been downloaded, so save it to file right now. 371 DCHECK(profile_image_url_.is_valid()); 372 SaveUserImageInternal( 373 username, 374 User::kProfileImageIndex, profile_image_url_, 375 UserImage::CreateAndEncode(downloaded_profile_image_)); 376 } else { 377 // No profile image - use the stub image (gray avatar). 378 SetUserImage(username, User::kProfileImageIndex, GURL(), UserImage()); 379 SaveImageToLocalState(username, "", User::kProfileImageIndex, 380 GURL(), false); 381 } 382 } 383 384 void UserImageManagerImpl::DeleteUserImage(const std::string& username) { 385 // Delete from the old dictionary, if present. 386 DeleteOldUserImage(username); 387 388 PrefService* prefs = g_browser_process->local_state(); 389 DictionaryPrefUpdate prefs_images_update(prefs, kUserImageProperties); 390 const base::DictionaryValue* image_properties; 391 if (prefs_images_update->GetDictionaryWithoutPathExpansion( 392 username, &image_properties)) { 393 std::string image_path; 394 image_properties->GetString(kImageURLNodeName, &image_path); 395 prefs_images_update->RemoveWithoutPathExpansion(username, NULL); 396 DeleteImageFile(image_path); 397 } 398 } 399 400 void UserImageManagerImpl::DownloadProfileImage(const std::string& reason) { 401 DownloadProfileData(reason, true); 402 } 403 404 void UserImageManagerImpl::Shutdown() { 405 profile_image_downloader_.reset(); 406 } 407 408 const gfx::ImageSkia& UserImageManagerImpl::DownloadedProfileImage() const { 409 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 410 return downloaded_profile_image_; 411 } 412 413 base::FilePath UserImageManagerImpl::GetImagePathForUser( 414 const std::string& username) { 415 std::string filename = username + kSafeImagePathExtension; 416 base::FilePath user_data_dir; 417 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); 418 return user_data_dir.AppendASCII(filename); 419 } 420 421 void UserImageManagerImpl::SetInitialUserImage(const std::string& username) { 422 // Choose a random default image. 423 int image_id = 424 base::RandInt(kFirstDefaultImageIndex, kDefaultImagesCount - 1); 425 SaveUserDefaultImageIndex(username, image_id); 426 } 427 428 void UserImageManagerImpl::SetUserImage(const std::string& username, 429 int image_index, 430 const GURL& image_url, 431 const UserImage& user_image) { 432 User* user = const_cast<User*>(UserManager::Get()->FindUser(username)); 433 // User may have been removed by now. 434 if (user) { 435 bool image_changed = user->image_index() != User::kInvalidImageIndex; 436 bool is_current_user = user == UserManager::Get()->GetLoggedInUser(); 437 if (!user_image.image().isNull()) 438 user->SetImage(user_image, image_index); 439 else 440 user->SetStubImage(image_index, false); 441 user->SetImageURL(image_url); 442 // For the logged-in user with a profile picture, initialize 443 // |downloaded_profile_picture_|. 444 if (is_current_user && image_index == User::kProfileImageIndex) { 445 InitDownloadedProfileImage(); 446 } 447 if (image_changed) { 448 // Unless this is first-time setting with |SetInitialUserImage|, 449 // send a notification about image change. 450 content::NotificationService::current()->Notify( 451 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, 452 content::Source<UserImageManager>(this), 453 content::Details<const User>(user)); 454 } 455 if (is_current_user && migrate_current_user_on_load_) 456 MigrateUserImage(); 457 } 458 } 459 460 void UserImageManagerImpl::SaveUserImageInternal(const std::string& username, 461 int image_index, 462 const GURL& image_url, 463 const UserImage& user_image) { 464 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 465 466 SetUserImage(username, image_index, image_url, user_image); 467 468 // Ignore if data stored or cached outside the user's cryptohome is to be 469 // treated as ephemeral. 470 if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(username)) 471 return; 472 473 base::FilePath image_path = GetImagePathForUser(username); 474 DVLOG(1) << "Saving user image to " << image_path.value(); 475 476 last_image_set_async_ = true; 477 478 base::WorkerPool::PostTask( 479 FROM_HERE, 480 base::Bind(&UserImageManagerImpl::SaveImageToFile, 481 base::Unretained(this), 482 username, user_image, image_path, image_index, image_url), 483 /* is_slow= */ false); 484 } 485 486 void UserImageManagerImpl::SaveImageToFile(const std::string& username, 487 const UserImage& user_image, 488 const base::FilePath& image_path, 489 int image_index, 490 const GURL& image_url) { 491 if (!SaveBitmapToFile(user_image, image_path)) 492 return; 493 494 BrowserThread::PostTask( 495 BrowserThread::UI, 496 FROM_HERE, 497 base::Bind(&UserImageManagerImpl::SaveImageToLocalState, 498 base::Unretained(this), 499 username, image_path.value(), image_index, image_url, true)); 500 } 501 502 void UserImageManagerImpl::SaveImageToLocalState(const std::string& username, 503 const std::string& image_path, 504 int image_index, 505 const GURL& image_url, 506 bool is_async) { 507 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 508 509 // Ignore if data stored or cached outside the user's cryptohome is to be 510 // treated as ephemeral. 511 if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(username)) 512 return; 513 514 // TODO(ivankr): use unique filenames for user images each time 515 // a new image is set so that only the last image update is saved 516 // to Local State and notified. 517 if (is_async && !last_image_set_async_) { 518 DVLOG(1) << "Ignoring saved image because it has changed"; 519 return; 520 } else if (!is_async) { 521 // Reset the async image save flag if called directly from the UI thread. 522 last_image_set_async_ = false; 523 } 524 525 PrefService* local_state = g_browser_process->local_state(); 526 DictionaryPrefUpdate images_update(local_state, kUserImageProperties); 527 base::DictionaryValue* image_properties = new base::DictionaryValue(); 528 image_properties->Set(kImagePathNodeName, new StringValue(image_path)); 529 image_properties->Set(kImageIndexNodeName, 530 new base::FundamentalValue(image_index)); 531 if (!image_url.is_empty()) { 532 image_properties->Set(kImageURLNodeName, 533 new StringValue(image_url.spec())); 534 } else { 535 image_properties->Remove(kImageURLNodeName, NULL); 536 } 537 images_update->SetWithoutPathExpansion(username, image_properties); 538 DVLOG(1) << "Saving path to user image in Local State."; 539 540 if (users_to_migrate_.count(username)) { 541 DeleteOldUserImage(username); 542 users_to_migrate_.erase(username); 543 } 544 545 UserManager::Get()->NotifyLocalStateChanged(); 546 } 547 548 bool UserImageManagerImpl::SaveBitmapToFile(const UserImage& user_image, 549 const base::FilePath& image_path) { 550 UserImage safe_image; 551 const UserImage::RawImage* encoded_image = NULL; 552 if (!user_image.is_safe_format()) { 553 safe_image = UserImage::CreateAndEncode(user_image.image()); 554 encoded_image = &safe_image.raw_image(); 555 UMA_HISTOGRAM_MEMORY_KB("UserImage.RecodedJpegSize", encoded_image->size()); 556 } else if (user_image.has_raw_image()) { 557 encoded_image = &user_image.raw_image(); 558 } else { 559 NOTREACHED() << "Raw image missing."; 560 return false; 561 } 562 563 if (file_util::WriteFile(image_path, 564 reinterpret_cast<const char*>(&(*encoded_image)[0]), 565 encoded_image->size()) == -1) { 566 LOG(ERROR) << "Failed to save image to file."; 567 return false; 568 } 569 return true; 570 } 571 572 void UserImageManagerImpl::InitDownloadedProfileImage() { 573 const User* logged_in_user = UserManager::Get()->GetLoggedInUser(); 574 DCHECK_EQ(logged_in_user->image_index(), User::kProfileImageIndex); 575 if (downloaded_profile_image_.isNull() && !logged_in_user->image_is_stub()) { 576 VLOG(1) << "Profile image initialized"; 577 downloaded_profile_image_ = logged_in_user->image(); 578 downloaded_profile_image_data_url_ = 579 webui::GetBitmapDataUrl(*downloaded_profile_image_.bitmap()); 580 profile_image_url_ = logged_in_user->image_url(); 581 } 582 } 583 584 void UserImageManagerImpl::DownloadProfileData(const std::string& reason, 585 bool download_image) { 586 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 587 588 // GAIA profiles exist for regular users only. 589 if (!UserManager::Get()->IsLoggedInAsRegularUser()) 590 return; 591 592 // Mark profile picture as needed. 593 downloading_profile_image_ |= download_image; 594 595 // Another download is already in progress 596 if (profile_image_downloader_.get()) 597 return; 598 599 profile_image_download_reason_ = reason; 600 profile_image_load_start_time_ = base::Time::Now(); 601 profile_image_downloader_.reset(new ProfileDownloader(this)); 602 profile_image_downloader_->Start(); 603 } 604 605 void UserImageManagerImpl::DownloadProfileDataScheduled() { 606 const User* logged_in_user = UserManager::Get()->GetLoggedInUser(); 607 // If current user image is profile image, it needs to be refreshed. 608 bool download_profile_image = 609 logged_in_user->image_index() == User::kProfileImageIndex; 610 DownloadProfileData(kProfileDownloadReasonScheduled, download_profile_image); 611 } 612 613 void UserImageManagerImpl::DownloadProfileDataRetry(bool download_image) { 614 DownloadProfileData(kProfileDownloadReasonRetry, download_image); 615 } 616 617 // ProfileDownloaderDelegate override. 618 bool UserImageManagerImpl::NeedsProfilePicture() const { 619 return downloading_profile_image_; 620 } 621 622 // ProfileDownloaderDelegate override. 623 int UserImageManagerImpl::GetDesiredImageSideLength() const { 624 return GetCurrentUserImageSize(); 625 } 626 627 // ProfileDownloaderDelegate override. 628 std::string UserImageManagerImpl::GetCachedPictureURL() const { 629 return profile_image_url_.spec(); 630 } 631 632 Profile* UserImageManagerImpl::GetBrowserProfile() { 633 return ProfileManager::GetDefaultProfile(); 634 } 635 636 void UserImageManagerImpl::OnProfileDownloadSuccess( 637 ProfileDownloader* downloader) { 638 // Make sure that |ProfileDownloader| gets deleted after return. 639 scoped_ptr<ProfileDownloader> profile_image_downloader( 640 profile_image_downloader_.release()); 641 DCHECK_EQ(downloader, profile_image_downloader.get()); 642 643 UserManager* user_manager = UserManager::Get(); 644 const User* user = user_manager->GetLoggedInUser(); 645 646 if (!downloader->GetProfileFullName().empty()) { 647 user_manager->SaveUserDisplayName( 648 user->email(), downloader->GetProfileFullName()); 649 } 650 651 bool requested_image = downloading_profile_image_; 652 downloading_profile_image_ = false; 653 if (!requested_image) 654 return; 655 656 ProfileDownloadResult result = kDownloadFailure; 657 switch (downloader->GetProfilePictureStatus()) { 658 case ProfileDownloader::PICTURE_SUCCESS: 659 result = kDownloadSuccess; 660 break; 661 case ProfileDownloader::PICTURE_CACHED: 662 result = kDownloadCached; 663 break; 664 case ProfileDownloader::PICTURE_DEFAULT: 665 result = kDownloadDefault; 666 break; 667 default: 668 NOTREACHED(); 669 } 670 671 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", 672 result, kDownloadResultsCount); 673 674 DCHECK(!profile_image_load_start_time_.is_null()); 675 base::TimeDelta delta = base::Time::Now() - profile_image_load_start_time_; 676 AddProfileImageTimeHistogram(result, profile_image_download_reason_, delta); 677 678 if (result == kDownloadDefault) { 679 content::NotificationService::current()->Notify( 680 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, 681 content::Source<UserImageManager>(this), 682 content::NotificationService::NoDetails()); 683 } 684 685 // Nothing to do if picture is cached or the default avatar. 686 if (result != kDownloadSuccess) 687 return; 688 689 // Check if this image is not the same as already downloaded. 690 SkBitmap new_bitmap(downloader->GetProfilePicture()); 691 std::string new_image_data_url = webui::GetBitmapDataUrl(new_bitmap); 692 if (!downloaded_profile_image_data_url_.empty() && 693 new_image_data_url == downloaded_profile_image_data_url_) 694 return; 695 696 downloaded_profile_image_data_url_ = new_image_data_url; 697 downloaded_profile_image_ = gfx::ImageSkia::CreateFrom1xBitmap( 698 downloader->GetProfilePicture()); 699 profile_image_url_ = GURL(downloader->GetProfilePictureURL()); 700 701 if (user->image_index() == User::kProfileImageIndex) { 702 VLOG(1) << "Updating profile image for logged-in user"; 703 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", 704 kDownloadSuccessChanged, 705 kDownloadResultsCount); 706 // This will persist |downloaded_profile_image_| to file. 707 SaveUserImageFromProfileImage(user->email()); 708 } 709 710 content::NotificationService::current()->Notify( 711 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED, 712 content::Source<UserImageManager>(this), 713 content::Details<const gfx::ImageSkia>(&downloaded_profile_image_)); 714 } 715 716 void UserImageManagerImpl::OnProfileDownloadFailure( 717 ProfileDownloader* downloader, 718 ProfileDownloaderDelegate::FailureReason reason) { 719 DCHECK_EQ(downloader, profile_image_downloader_.get()); 720 profile_image_downloader_.reset(); 721 722 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", 723 kDownloadFailure, kDownloadResultsCount); 724 725 DCHECK(!profile_image_load_start_time_.is_null()); 726 base::TimeDelta delta = base::Time::Now() - profile_image_load_start_time_; 727 AddProfileImageTimeHistogram(kDownloadFailure, profile_image_download_reason_, 728 delta); 729 730 // Retry download after some time if a network error has occured. 731 if (reason == ProfileDownloaderDelegate::NETWORK_ERROR) { 732 BrowserThread::PostDelayedTask( 733 BrowserThread::UI, 734 FROM_HERE, 735 base::Bind(&UserImageManagerImpl::DownloadProfileDataRetry, 736 base::Unretained(this), 737 downloading_profile_image_), 738 base::TimeDelta::FromSeconds(kProfileDataDownloadRetryIntervalSec)); 739 } 740 741 downloading_profile_image_ = false; 742 743 content::NotificationService::current()->Notify( 744 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, 745 content::Source<UserImageManager>(this), 746 content::NotificationService::NoDetails()); 747 } 748 749 void UserImageManagerImpl::MigrateUserImage() { 750 User* user = UserManager::Get()->GetLoggedInUser(); 751 if (user->image_is_loading()) { 752 LOG(INFO) << "Waiting for user image to load before migration"; 753 migrate_current_user_on_load_ = true; 754 return; 755 } 756 migrate_current_user_on_load_ = false; 757 if (user->has_raw_image() && user->image_is_safe_format()) { 758 // Nothing to migrate already, make sure we delete old image. 759 DeleteOldUserImage(user->email()); 760 users_to_migrate_.erase(user->email()); 761 return; 762 } 763 if (user->HasDefaultImage()) { 764 SaveUserDefaultImageIndex(user->email(), user->image_index()); 765 } else { 766 SaveUserImageInternal(user->email(), user->image_index(), 767 user->image_url(), user->user_image()); 768 } 769 UMA_HISTOGRAM_ENUMERATION("UserImage.Migration", 770 ImageIndexToHistogramIndex(user->image_index()), 771 kHistogramImagesCount); 772 } 773 774 void UserImageManagerImpl::DeleteOldUserImage(const std::string& username) { 775 PrefService* prefs = g_browser_process->local_state(); 776 DictionaryPrefUpdate prefs_images_update(prefs, kUserImages); 777 const base::DictionaryValue* image_properties; 778 if (prefs_images_update->GetDictionaryWithoutPathExpansion( 779 username, &image_properties)) { 780 std::string image_path; 781 image_properties->GetString(kImagePathNodeName, &image_path); 782 prefs_images_update->RemoveWithoutPathExpansion(username, NULL); 783 DeleteImageFile(image_path); 784 } 785 } 786 787 } // namespace chromeos 788