Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 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/login/user_image_manager_impl.h" | 5 #include "chrome/browser/chromeos/login/user_image_manager_impl.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/debug/trace_event.h" | 9 #include "base/debug/trace_event.h" |
| 10 #include "base/file_util.h" | 10 #include "base/file_util.h" |
| 11 #include "base/files/file_path.h" | 11 #include "base/files/file_path.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/metrics/histogram.h" | 13 #include "base/metrics/histogram.h" |
| 14 #include "base/path_service.h" | 14 #include "base/path_service.h" |
| 15 #include "base/prefs/pref_registry_simple.h" | 15 #include "base/prefs/pref_registry_simple.h" |
| 16 #include "base/prefs/pref_service.h" | 16 #include "base/prefs/pref_service.h" |
| 17 #include "base/prefs/scoped_user_pref_update.h" | 17 #include "base/prefs/scoped_user_pref_update.h" |
| 18 #include "base/rand_util.h" | 18 #include "base/rand_util.h" |
| 19 #include "base/sequenced_task_runner.h" | 19 #include "base/sequenced_task_runner.h" |
| 20 #include "base/task_runner_util.h" | |
| 20 #include "base/threading/sequenced_worker_pool.h" | 21 #include "base/threading/sequenced_worker_pool.h" |
| 21 #include "base/threading/worker_pool.h" | |
| 22 #include "base/time/time.h" | 22 #include "base/time/time.h" |
| 23 #include "base/values.h" | |
| 24 #include "chrome/browser/browser_process.h" | 23 #include "chrome/browser/browser_process.h" |
| 25 #include "chrome/browser/chrome_notification_types.h" | 24 #include "chrome/browser/chrome_notification_types.h" |
| 26 #include "chrome/browser/chromeos/login/default_user_images.h" | 25 #include "chrome/browser/chromeos/login/default_user_images.h" |
| 27 #include "chrome/browser/chromeos/login/helper.h" | 26 #include "chrome/browser/chromeos/login/helper.h" |
| 28 #include "chrome/browser/chromeos/login/user_image.h" | 27 #include "chrome/browser/chromeos/login/user_image.h" |
| 29 #include "chrome/browser/chromeos/login/user_image_sync_observer.h" | 28 #include "chrome/browser/chromeos/login/user_image_sync_observer.h" |
| 30 #include "chrome/browser/chromeos/login/user_manager.h" | 29 #include "chrome/browser/chromeos/login/user_manager.h" |
| 31 #include "chrome/browser/profiles/profile_downloader.h" | 30 #include "chrome/browser/profiles/profile_downloader.h" |
| 32 #include "chrome/browser/profiles/profile_manager.h" | 31 #include "chrome/browser/profiles/profile_manager.h" |
| 33 #include "chrome/common/chrome_paths.h" | 32 #include "chrome/common/chrome_paths.h" |
| 34 #include "chrome/common/chrome_switches.h" | 33 #include "chrome/common/chrome_switches.h" |
| 35 #include "chromeos/chromeos_switches.h" | 34 #include "chromeos/chromeos_switches.h" |
| 36 #include "content/public/browser/browser_thread.h" | 35 #include "content/public/browser/browser_thread.h" |
| 37 #include "content/public/browser/notification_service.h" | 36 #include "content/public/browser/notification_service.h" |
| 38 #include "content/public/common/url_constants.h" | |
| 39 #include "ui/base/webui/web_ui_util.h" | 37 #include "ui/base/webui/web_ui_util.h" |
| 40 #include "ui/gfx/image/image_skia.h" | 38 #include "ui/gfx/image/image_skia.h" |
| 41 | 39 |
| 42 using content::BrowserThread; | |
| 43 | |
| 44 namespace chromeos { | 40 namespace chromeos { |
| 45 | 41 |
| 46 namespace { | 42 namespace { |
| 47 | 43 |
| 48 // A dictionary that maps usernames to old user image data with images stored in | 44 // A dictionary that maps user_ids to old user image data with images stored in |
| 49 // PNG format. Deprecated. | 45 // PNG format. Deprecated. |
| 50 // TODO(ivankr): remove this const char after migration is gone. | 46 // TODO(ivankr): remove this const char after migration is gone. |
| 51 const char kUserImages[] = "UserImages"; | 47 const char kUserImages[] = "UserImages"; |
| 52 | 48 |
| 53 // A dictionary that maps usernames to user image data with images stored in | 49 // A dictionary that maps user_ids to user image data with images stored in |
| 54 // JPEG format. | 50 // JPEG format. |
| 55 const char kUserImageProperties[] = "user_image_info"; | 51 const char kUserImageProperties[] = "user_image_info"; |
| 56 | 52 |
| 57 // Names of user image properties. | 53 // Names of user image properties. |
| 58 const char kImagePathNodeName[] = "path"; | 54 const char kImagePathNodeName[] = "path"; |
| 59 const char kImageIndexNodeName[] = "index"; | 55 const char kImageIndexNodeName[] = "index"; |
| 60 const char kImageURLNodeName[] = "url"; | 56 const char kImageURLNodeName[] = "url"; |
| 61 | 57 |
| 62 // Delay betweeen user login and user image migration. | |
| 63 const int kUserImageMigrationDelaySec = 50; | |
|
bartfab (slow)
2013/11/13 23:12:09
I removed this because it was not working correctl
| |
| 64 | |
| 65 // Delay betweeen user login and attempt to update user's profile data. | 58 // Delay betweeen user login and attempt to update user's profile data. |
| 66 const int kProfileDataDownloadDelaySec = 10; | 59 const int kProfileDataDownloadDelaySec = 10; |
| 67 | 60 |
| 68 // Interval betweeen retries to update user's profile data. | 61 // Interval betweeen retries to update user's profile data. |
| 69 const int kProfileDataDownloadRetryIntervalSec = 300; | 62 const int kProfileDataDownloadRetryIntervalSec = 300; |
| 70 | 63 |
| 71 // Delay betweeen subsequent profile refresh attempts (24 hrs). | 64 // Delay betweeen subsequent profile refresh attempts (24 hrs). |
| 72 const int kProfileRefreshIntervalSec = 24 * 3600; | 65 const int kProfileRefreshIntervalSec = 24 * 3600; |
| 73 | 66 |
| 74 const char kSafeImagePathExtension[] = ".jpg"; | 67 const char kSafeImagePathExtension[] = ".jpg"; |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 97 // Time histogram prefix for a successful profile image download. | 90 // Time histogram prefix for a successful profile image download. |
| 98 const char kProfileDownloadSuccessTime[] = | 91 const char kProfileDownloadSuccessTime[] = |
| 99 "UserImage.ProfileDownloadTime.Success"; | 92 "UserImage.ProfileDownloadTime.Success"; |
| 100 // Time histogram suffix for a profile image download after login. | 93 // Time histogram suffix for a profile image download after login. |
| 101 const char kProfileDownloadReasonLoggedIn[] = "LoggedIn"; | 94 const char kProfileDownloadReasonLoggedIn[] = "LoggedIn"; |
| 102 // Time histogram suffix for a scheduled profile image download. | 95 // Time histogram suffix for a scheduled profile image download. |
| 103 const char kProfileDownloadReasonScheduled[] = "Scheduled"; | 96 const char kProfileDownloadReasonScheduled[] = "Scheduled"; |
| 104 // Time histogram suffix for a profile image download retry. | 97 // Time histogram suffix for a profile image download retry. |
| 105 const char kProfileDownloadReasonRetry[] = "Retry"; | 98 const char kProfileDownloadReasonRetry[] = "Retry"; |
| 106 | 99 |
| 107 // Add a histogram showing the time it takes to download a profile image. | 100 // Add a histogram showing the time it takes to download profile image. |
| 108 // Separate histograms are reported for each download |reason| and |result|. | 101 // Separate histograms are reported for each download |reason| and |result|. |
| 109 void AddProfileImageTimeHistogram(ProfileDownloadResult result, | 102 void AddProfileImageTimeHistogram(ProfileDownloadResult result, |
| 110 const std::string& download_reason, | 103 const std::string& download_reason, |
| 111 const base::TimeDelta& time_delta) { | 104 const base::TimeDelta& time_delta) { |
| 112 std::string histogram_name; | 105 std::string histogram_name; |
| 113 switch (result) { | 106 switch (result) { |
| 114 case kDownloadFailure: | 107 case kDownloadFailure: |
| 115 histogram_name = kProfileDownloadFailureTime; | 108 histogram_name = kProfileDownloadFailureTime; |
| 116 break; | 109 break; |
| 117 case kDownloadDefault: | 110 case kDownloadDefault: |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 136 const size_t bucket_count(50); | 129 const size_t bucket_count(50); |
| 137 | 130 |
| 138 base::HistogramBase* counter = base::Histogram::FactoryTimeGet( | 131 base::HistogramBase* counter = base::Histogram::FactoryTimeGet( |
| 139 histogram_name, min_time, max_time, bucket_count, | 132 histogram_name, min_time, max_time, bucket_count, |
| 140 base::HistogramBase::kUmaTargetedHistogramFlag); | 133 base::HistogramBase::kUmaTargetedHistogramFlag); |
| 141 counter->AddTime(time_delta); | 134 counter->AddTime(time_delta); |
| 142 | 135 |
| 143 DVLOG(1) << "Profile image download time: " << time_delta.InSecondsF(); | 136 DVLOG(1) << "Profile image download time: " << time_delta.InSecondsF(); |
| 144 } | 137 } |
| 145 | 138 |
| 146 // Deletes image file. | |
| 147 void DeleteImageFile(const std::string& image_path) { | |
| 148 if (image_path.empty()) | |
| 149 return; | |
| 150 base::FilePath fp(image_path); | |
| 151 BrowserThread::PostTask( | |
| 152 BrowserThread::FILE, | |
| 153 FROM_HERE, | |
| 154 base::Bind(base::IgnoreResult(&base::DeleteFile), | |
| 155 fp, /* recursive= */ false)); | |
| 156 } | |
| 157 | |
| 158 // Converts |image_index| to UMA histogram value. | 139 // Converts |image_index| to UMA histogram value. |
| 159 int ImageIndexToHistogramIndex(int image_index) { | 140 int ImageIndexToHistogramIndex(int image_index) { |
| 160 switch (image_index) { | 141 switch (image_index) { |
| 161 case User::kExternalImageIndex: | 142 case User::kExternalImageIndex: |
| 162 // TODO(ivankr): Distinguish this from selected from file. | 143 // TODO(ivankr): Distinguish this from selected from file. |
| 163 return kHistogramImageFromCamera; | 144 return kHistogramImageFromCamera; |
| 164 case User::kProfileImageIndex: | 145 case User::kProfileImageIndex: |
| 165 return kHistogramImageFromProfile; | 146 return kHistogramImageFromProfile; |
| 166 default: | 147 default: |
| 167 return image_index; | 148 return image_index; |
| 168 } | 149 } |
| 169 } | 150 } |
| 170 | 151 |
| 152 bool SaveImage(const UserImage& user_image, const base::FilePath& image_path) { | |
| 153 UserImage safe_image; | |
| 154 const UserImage::RawImage* encoded_image = NULL; | |
| 155 if (!user_image.is_safe_format()) { | |
| 156 safe_image = UserImage::CreateAndEncode(user_image.image()); | |
| 157 encoded_image = &safe_image.raw_image(); | |
| 158 UMA_HISTOGRAM_MEMORY_KB("UserImage.RecodedJpegSize", encoded_image->size()); | |
| 159 } else if (user_image.has_raw_image()) { | |
| 160 encoded_image = &user_image.raw_image(); | |
| 161 } else { | |
| 162 NOTREACHED() << "Raw image missing."; | |
| 163 return false; | |
| 164 } | |
| 165 | |
| 166 if (file_util::WriteFile(image_path, | |
| 167 reinterpret_cast<const char*>(&(*encoded_image)[0]), | |
| 168 encoded_image->size()) == -1) { | |
| 169 LOG(ERROR) << "Failed to save image to file."; | |
| 170 return false; | |
| 171 } | |
| 172 | |
| 173 return true; | |
| 174 } | |
| 175 | |
| 171 } // namespace | 176 } // namespace |
| 172 | 177 |
| 173 // static | 178 // static |
| 174 int UserImageManagerImpl::user_image_migration_delay_sec = | |
| 175 kUserImageMigrationDelaySec; | |
| 176 | |
| 177 // static | |
| 178 void UserImageManager::RegisterPrefs(PrefRegistrySimple* registry) { | 179 void UserImageManager::RegisterPrefs(PrefRegistrySimple* registry) { |
| 179 registry->RegisterDictionaryPref(kUserImages); | 180 registry->RegisterDictionaryPref(kUserImages); |
| 180 registry->RegisterDictionaryPref(kUserImageProperties); | 181 registry->RegisterDictionaryPref(kUserImageProperties); |
| 181 } | 182 } |
| 182 | 183 |
| 184 // Every image load or update is encapsulated by a Job. The Job is allowed to | |
| 185 // perform tasks on background threads or in helper processes but: | |
| 186 // * Changes to User objects and local state as well as any calls to the | |
| 187 // |parent_| must be performed on the thread that the Job is created on only. | |
| 188 // * File writes and deletions must be performed via the |parent_|'s | |
| 189 // |background_task_runner_| only. | |
| 190 // | |
| 191 // Only one of the Load*() and Set*() methods may be called per Job. | |
| 192 class UserImageManagerImpl::Job { | |
| 193 public: | |
| 194 // The |Job| will update the |user| object for |user_id|. | |
| 195 Job(UserImageManagerImpl* parent, const std::string& user_id); | |
| 196 ~Job(); | |
| 197 | |
| 198 // Loads the image at |image_path| or one of the default images, depending on | |
| 199 // |image_index|, and updates the |user| object for |user_id_| with the new | |
| 200 // image. | |
| 201 void LoadImage(base::FilePath image_path, | |
| 202 const int image_index, | |
| 203 const GURL& image_url); | |
| 204 | |
| 205 // Sets the user image for |user_id_| in local state to the default image | |
| 206 // indicated by |default_image_index|. Also updates the |user| object for | |
| 207 // |user_id_| with the new image. | |
| 208 void SetToDefaultImage(int default_image_index); | |
| 209 | |
| 210 // Saves the |user_image| to disk and sets the user image for |user_id_| in | |
| 211 // local state to that image. Also updates the |user| object for |user_id_| | |
| 212 // with the new image. | |
| 213 void SetToImage(int image_index, | |
| 214 const UserImage& user_image); | |
| 215 | |
| 216 // Loads the the image at |path|, transcodes it to JPEG format, saves the | |
| 217 // image to disk and sets the user image for |user_id_| in local state to that | |
| 218 // image. If |resize| is true, the image is cropped and resized before | |
| 219 // transcoding. Also updates the |user| object for |user_id_| with the new | |
| 220 // image. | |
| 221 void SetToPath(const base::FilePath& path, | |
| 222 int image_index, | |
| 223 const GURL& image_url, | |
| 224 bool resize); | |
| 225 | |
| 226 private: | |
| 227 // Called back after an image has been loaded from disk. | |
| 228 void OnLoadImageDone(bool save, const UserImage& user_image); | |
| 229 | |
| 230 // Updates the |user| object for |user_id_| with |user_image_|. | |
| 231 void UpdateUser(); | |
| 232 | |
| 233 // Saves |user_image_| to disk in JPEG format. Local state will be updated | |
| 234 // when a callback indicates that the save has been successful. | |
| 235 void SaveImageAndUpdateLocalState(); | |
| 236 | |
| 237 // Called back after the |user_image_| has been saved to disk. If |success| is | |
| 238 // true sets the user image for |user_id_| in local state to that image. | |
| 239 void OnSaveImageDone(bool success); | |
| 240 | |
| 241 // Updates the user image for |user_id_| in local state, setting it to | |
| 242 // one of the default images or the saved |user_image_|, depending on | |
| 243 // |image_index_|. | |
| 244 void UpdateLocalState(); | |
| 245 | |
| 246 // Notifies the |parent_| that the Job is done. | |
| 247 void NotifyJobDone(); | |
| 248 | |
| 249 UserImageManagerImpl* parent_; | |
| 250 const std::string user_id_; | |
| 251 | |
| 252 // Whether one of the Load*() or Set*() methods has been run already. | |
| 253 bool run_; | |
| 254 | |
| 255 int image_index_; | |
| 256 GURL image_url_; | |
| 257 base::FilePath image_path_; | |
| 258 | |
| 259 UserImage user_image_; | |
| 260 | |
| 261 base::WeakPtrFactory<Job> weak_factory_; | |
| 262 | |
| 263 DISALLOW_COPY_AND_ASSIGN(Job); | |
| 264 }; | |
| 265 | |
| 266 UserImageManagerImpl::Job::Job(UserImageManagerImpl* parent, | |
| 267 const std::string& user_id) | |
| 268 : parent_(parent), | |
| 269 user_id_(user_id), | |
| 270 run_(false), | |
| 271 weak_factory_(this) { | |
| 272 } | |
| 273 | |
| 274 UserImageManagerImpl::Job::~Job() { | |
| 275 } | |
| 276 | |
| 277 void UserImageManagerImpl::Job::LoadImage(base::FilePath image_path, | |
| 278 const int image_index, | |
| 279 const GURL& image_url) { | |
| 280 DCHECK(!run_); | |
| 281 run_ = true; | |
| 282 | |
| 283 image_index_ = image_index; | |
| 284 image_url_ = image_url; | |
| 285 image_path_ = image_path; | |
| 286 | |
| 287 if (image_index_ >= 0 && image_index_ < kDefaultImagesCount) { | |
| 288 // Load one of the default images. This happens synchronously. | |
| 289 user_image_ = UserImage(GetDefaultImage(image_index_)); | |
| 290 UpdateUser(); | |
| 291 NotifyJobDone(); | |
| 292 } else if (image_index_ == User::kExternalImageIndex || | |
| 293 image_index_ == User::kProfileImageIndex) { | |
| 294 // Load the user image from a file referenced by |image_path|. This happens | |
| 295 // asynchronously. The JPEG image loader can be used here because | |
| 296 // LoadImage() is called only for users whose user image has previously | |
| 297 // been set by one of the Set*() methods, which transcode to JPEG format. | |
| 298 parent_->image_loader_->Start(image_path_.value(), | |
|
Nikita (slow)
2013/11/14 15:17:13
DCHECK that image_path is not empty.
bartfab (slow)
2013/11/14 15:55:10
There actually is a DCHECK for this in the code th
| |
| 299 0, | |
| 300 base::Bind(&Job::OnLoadImageDone, | |
| 301 weak_factory_.GetWeakPtr(), | |
| 302 false)); | |
| 303 } else { | |
| 304 NOTREACHED(); | |
| 305 NotifyJobDone(); | |
| 306 } | |
| 307 } | |
| 308 | |
| 309 void UserImageManagerImpl::Job::SetToDefaultImage(int default_image_index) { | |
| 310 DCHECK(!run_); | |
| 311 run_ = true; | |
| 312 | |
| 313 DCHECK_LE(0, default_image_index); | |
| 314 DCHECK_GT(kDefaultImagesCount, default_image_index); | |
| 315 | |
| 316 image_index_ = default_image_index; | |
| 317 user_image_ = UserImage(GetDefaultImage(image_index_)); | |
| 318 | |
| 319 UpdateUser(); | |
| 320 UpdateLocalState(); | |
| 321 NotifyJobDone(); | |
| 322 } | |
| 323 | |
| 324 void UserImageManagerImpl::Job::SetToImage(int image_index, | |
| 325 const UserImage& user_image) { | |
| 326 DCHECK(!run_); | |
| 327 run_ = true; | |
| 328 | |
| 329 DCHECK(image_index == User::kExternalImageIndex || | |
| 330 image_index == User::kProfileImageIndex); | |
| 331 | |
| 332 image_index_ = image_index; | |
| 333 user_image_ = user_image; | |
| 334 | |
| 335 UpdateUser(); | |
| 336 SaveImageAndUpdateLocalState(); | |
| 337 } | |
| 338 | |
| 339 void UserImageManagerImpl::Job::SetToPath(const base::FilePath& path, | |
| 340 int image_index, | |
| 341 const GURL& image_url, | |
| 342 bool resize) { | |
| 343 DCHECK(!run_); | |
|
Nikita (slow)
2013/11/14 15:17:13
DCHECK path.
bartfab (slow)
2013/11/14 15:55:10
Done.
| |
| 344 run_ = true; | |
| 345 | |
| 346 image_index_ = image_index; | |
| 347 image_url_ = image_url; | |
| 348 | |
| 349 parent_->unsafe_image_loader_->Start(path.value(), | |
| 350 resize ? login::kMaxUserImageSize : 0, | |
| 351 base::Bind(&Job::OnLoadImageDone, | |
| 352 weak_factory_.GetWeakPtr(), | |
| 353 true)); | |
| 354 } | |
| 355 | |
| 356 void UserImageManagerImpl::Job::OnLoadImageDone(bool save, | |
| 357 const UserImage& user_image) { | |
| 358 user_image_ = user_image; | |
| 359 UpdateUser(); | |
| 360 if (save) | |
| 361 SaveImageAndUpdateLocalState(); | |
| 362 else | |
| 363 NotifyJobDone(); | |
| 364 } | |
| 365 | |
| 366 void UserImageManagerImpl::Job::UpdateUser() { | |
| 367 User* user = const_cast<User*>(UserManager::Get()->FindUser(user_id_)); | |
|
Nikita (slow)
2013/11/14 15:17:13
How can const_cast be avoided?
Declare this class
bartfab (slow)
2013/11/14 15:55:10
I actually inherited this case from the original i
| |
| 368 if (!user) | |
| 369 return; | |
| 370 | |
| 371 if (!user_image_.image().isNull()) | |
| 372 user->SetImage(user_image_, image_index_); | |
| 373 else | |
| 374 user->SetStubImage(image_index_, false); | |
| 375 user->SetImageURL(image_url_); | |
| 376 | |
| 377 parent_->OnJobChangedUserImage(user); | |
| 378 } | |
| 379 | |
| 380 void UserImageManagerImpl::Job::SaveImageAndUpdateLocalState() { | |
| 381 base::FilePath user_data_dir; | |
| 382 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); | |
| 383 image_path_ = user_data_dir.Append(user_id_ + kSafeImagePathExtension); | |
| 384 | |
| 385 base::PostTaskAndReplyWithResult( | |
| 386 parent_->background_task_runner_, | |
| 387 FROM_HERE, | |
| 388 base::Bind(&SaveImage, user_image_, image_path_), | |
| 389 base::Bind(&Job::OnSaveImageDone, weak_factory_.GetWeakPtr())); | |
| 390 } | |
| 391 | |
| 392 void UserImageManagerImpl::Job::OnSaveImageDone(bool success) { | |
| 393 if (success) | |
| 394 UpdateLocalState(); | |
| 395 NotifyJobDone(); | |
| 396 } | |
| 397 | |
| 398 void UserImageManagerImpl::Job::UpdateLocalState() { | |
| 399 // Ignore if data stored or cached outside the user's cryptohome is to be | |
| 400 // treated as ephemeral. | |
| 401 if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(user_id_)) | |
| 402 return; | |
| 403 | |
| 404 scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue); | |
| 405 entry->Set(kImagePathNodeName, new base::StringValue(image_path_.value())); | |
| 406 entry->Set(kImageIndexNodeName, new base::FundamentalValue(image_index_)); | |
| 407 if (!image_url_.is_empty()) | |
| 408 entry->Set(kImageURLNodeName, new StringValue(image_url_.spec())); | |
| 409 DictionaryPrefUpdate update(g_browser_process->local_state(), | |
| 410 kUserImageProperties); | |
| 411 update->SetWithoutPathExpansion(user_id_, entry.release()); | |
| 412 | |
| 413 UserManager::Get()->NotifyLocalStateChanged(); | |
| 414 } | |
| 415 | |
| 416 void UserImageManagerImpl::Job::NotifyJobDone() { | |
| 417 parent_->OnJobDone(user_id_); | |
| 418 // |parent_| drops ownership of |this| in OnJobDone(), requiring the Job to | |
| 419 // destroy itself. | |
| 420 delete this; | |
|
Nikita (slow)
2013/11/14 15:17:13
Any reasons why me might want to use DeleteSoon()
bartfab (slow)
2013/11/14 15:55:10
I was sure I had a good reason not to use DeleteSo
| |
| 421 } | |
| 422 | |
| 183 UserImageManagerImpl::UserImageManagerImpl() | 423 UserImageManagerImpl::UserImageManagerImpl() |
| 184 : last_image_set_async_(false), | 424 : downloading_profile_image_(false), |
| 185 downloaded_profile_image_data_url_(content::kAboutBlankURL), | 425 profile_image_requested_(false), |
| 186 downloading_profile_image_(false), | 426 weak_factory_(this) { |
| 187 migrate_current_user_on_load_(false) { | 427 base::SequencedWorkerPool* blocking_pool = |
| 188 base::SequencedWorkerPool* blocking_pool = BrowserThread::GetBlockingPool(); | 428 content::BrowserThread::GetBlockingPool(); |
| 189 // Background task runner on which file I/O, image decoding and resizing are | 429 background_task_runner_ = |
| 190 // done. | |
| 191 scoped_refptr<base::SequencedTaskRunner> task_runner = | |
| 192 blocking_pool->GetSequencedTaskRunnerWithShutdownBehavior( | 430 blocking_pool->GetSequencedTaskRunnerWithShutdownBehavior( |
| 193 blocking_pool->GetSequenceToken(), | 431 blocking_pool->GetSequenceToken(), |
| 194 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); | 432 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); |
| 195 image_loader_ = new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC, | 433 image_loader_ = new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC, |
| 196 task_runner); | 434 background_task_runner_); |
| 197 unsafe_image_loader_ = new UserImageLoader(ImageDecoder::DEFAULT_CODEC, | 435 unsafe_image_loader_ = new UserImageLoader(ImageDecoder::DEFAULT_CODEC, |
| 198 task_runner); | 436 background_task_runner_); |
| 199 } | 437 } |
| 200 | 438 |
| 201 UserImageManagerImpl::~UserImageManagerImpl() { | 439 UserImageManagerImpl::~UserImageManagerImpl() { |
| 202 } | 440 } |
| 203 | 441 |
| 204 void UserImageManagerImpl::LoadUserImages(const UserList& users) { | 442 void UserImageManagerImpl::LoadUserImages(const UserList& users) { |
| 205 PrefService* local_state = g_browser_process->local_state(); | 443 PrefService* local_state = g_browser_process->local_state(); |
| 206 const DictionaryValue* prefs_images_unsafe = | 444 const DictionaryValue* prefs_images_unsafe = |
| 207 local_state->GetDictionary(kUserImages); | 445 local_state->GetDictionary(kUserImages); |
| 208 const DictionaryValue* prefs_images = | 446 const DictionaryValue* prefs_images = |
| 209 local_state->GetDictionary(kUserImageProperties); | 447 local_state->GetDictionary(kUserImageProperties); |
| 210 if (!prefs_images && !prefs_images_unsafe) | 448 if (!prefs_images && !prefs_images_unsafe) |
| 211 return; | 449 return; |
| 212 | 450 |
| 213 for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) { | 451 for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) { |
| 214 User* user = *it; | 452 User* user = *it; |
| 453 const std::string& user_id = user->email(); | |
| 454 bool needs_migration = false; | |
| 455 | |
| 456 // If entries are found in both |prefs_images_unsafe| and |prefs_images|, | |
| 457 // |prefs_images| is honored for now but |prefs_images_unsafe| will be | |
| 458 // migrated, overwriting the |prefs_images| entry, when the user logs in. | |
| 215 const base::DictionaryValue* image_properties = NULL; | 459 const base::DictionaryValue* image_properties = NULL; |
| 216 bool needs_migration = false; // |true| if user has image in old format. | |
| 217 bool safe_source = false; // |true| if image loaded from safe source. | |
| 218 | |
| 219 if (prefs_images_unsafe) { | 460 if (prefs_images_unsafe) { |
| 220 needs_migration = prefs_images_unsafe->GetDictionaryWithoutPathExpansion( | 461 needs_migration = prefs_images_unsafe->GetDictionaryWithoutPathExpansion( |
| 221 user->email(), &image_properties); | 462 user_id, &image_properties); |
| 463 if (needs_migration) | |
| 464 users_to_migrate_.insert(user_id); | |
| 222 } | 465 } |
| 223 if (prefs_images) { | 466 if (prefs_images) { |
| 224 safe_source = prefs_images->GetDictionaryWithoutPathExpansion( | 467 prefs_images->GetDictionaryWithoutPathExpansion(user_id, |
|
Nikita (slow)
2013/11/14 15:17:13
Can you please describe how migration implementati
bartfab (slow)
2013/11/14 15:55:10
Added to CL description.
| |
| 225 user->email(), &image_properties); | 468 &image_properties); |
| 226 } | 469 } |
| 227 | 470 |
| 228 if (needs_migration) | 471 if (!image_properties) { |
| 229 users_to_migrate_.insert(user->email()); | 472 SetInitialUserImage(user_id); |
| 473 continue; | |
| 474 } | |
| 230 | 475 |
| 231 if (!image_properties) { | 476 int image_index = User::kInvalidImageIndex; |
| 232 SetInitialUserImage(user->email()); | 477 image_properties->GetInteger(kImageIndexNodeName, &image_index); |
| 233 } else { | 478 if (image_index >= 0 && image_index < kDefaultImagesCount) { |
| 234 int image_index = User::kInvalidImageIndex; | 479 user->SetImage(UserImage(GetDefaultImage(image_index)), |
| 235 image_properties->GetInteger(kImageIndexNodeName, &image_index); | 480 image_index); |
| 481 continue; | |
| 482 } | |
| 236 | 483 |
| 237 if (image_index >= 0 && image_index < kDefaultImagesCount) { | 484 if (image_index != User::kExternalImageIndex && |
| 238 user->SetImage(UserImage(GetDefaultImage(image_index)), | 485 image_index != User::kProfileImageIndex) { |
| 239 image_index); | 486 NOTREACHED(); |
| 240 } else if (image_index == User::kExternalImageIndex || | 487 continue; |
| 241 image_index == User::kProfileImageIndex) { | 488 } |
| 242 std::string image_path; | |
| 243 image_properties->GetString(kImagePathNodeName, &image_path); | |
| 244 // Path may be empty for profile images (meaning that the image | |
| 245 // hasn't been downloaded for the first time yet, in which case a | |
| 246 // download will be scheduled for |kProfileDataDownloadDelayMs| | |
| 247 // after user logs in). | |
| 248 DCHECK(!image_path.empty() || image_index == User::kProfileImageIndex); | |
| 249 std::string image_url; | |
| 250 image_properties->GetString(kImageURLNodeName, &image_url); | |
| 251 GURL image_gurl(image_url); | |
| 252 // Until image has been loaded, use the stub image (gray avatar). | |
| 253 user->SetStubImage(image_index, true); | |
| 254 user->SetImageURL(image_gurl); | |
| 255 if (!image_path.empty()) { | |
| 256 if (needs_migration) { | |
| 257 // Non-JPG image will be migrated once user logs in. | |
| 258 // Stub image will be used for now. Continue with other users. | |
| 259 continue; | |
| 260 } | |
| 261 DCHECK(safe_source); | |
| 262 if (!safe_source) | |
| 263 continue; | |
| 264 | 489 |
| 265 // Load user image asynchronously - at this point we are able to use | 490 std::string image_url_string; |
| 266 // JPEG image loaded since image comes from safe pref source | 491 image_properties->GetString(kImageURLNodeName, &image_url_string); |
| 267 // i.e. converted to JPEG. | 492 GURL image_url(image_url_string); |
| 268 image_loader_->Start( | 493 std::string image_path; |
| 269 image_path, 0 /* no resize */, | 494 image_properties->GetString(kImagePathNodeName, &image_path); |
| 270 base::Bind(&UserImageManagerImpl::SetUserImage, | 495 |
| 271 base::Unretained(this), | 496 user->SetImageURL(image_url); |
| 272 user->email(), image_index, image_gurl)); | 497 DCHECK(!image_path.empty() || image_index == User::kProfileImageIndex); |
| 273 } | 498 if (image_path.empty() || needs_migration) { |
| 274 } else { | 499 // Use a stub image (gray avatar) if either of the following is true: |
| 275 NOTREACHED(); | 500 // * The profile image is to be used but has not been downloaded yet. The |
| 276 } | 501 // profile image will be downloaded after login. |
| 502 // * The image needs migration. Migration will be performed after login. | |
| 503 user->SetStubImage(image_index, true); | |
| 504 continue; | |
| 277 } | 505 } |
| 506 | |
| 507 linked_ptr<Job>& job = jobs_[user_id]; | |
| 508 job.reset(new Job(this, user_id)); | |
| 509 job->LoadImage(base::FilePath(image_path), image_index, image_url); | |
| 278 } | 510 } |
| 279 } | 511 } |
| 280 | 512 |
| 281 void UserImageManagerImpl::UserLoggedIn(const std::string& email, | 513 void UserImageManagerImpl::UserLoggedIn(const std::string& user_id, |
| 282 bool user_is_new, | 514 bool user_is_new, |
| 283 bool user_is_local) { | 515 bool user_is_local) { |
| 284 User* user = UserManager::Get()->GetLoggedInUser(); | 516 User* user = UserManager::Get()->GetLoggedInUser(); |
| 285 if (user_is_new) { | 517 if (user_is_new) { |
| 286 if (!user_is_local) | 518 if (!user_is_local) |
| 287 SetInitialUserImage(email); | 519 SetInitialUserImage(user_id); |
| 288 } else { | 520 } else { |
| 289 if (!user_is_local) { | |
| 290 // If current user image is profile image, it needs to be refreshed. | |
| 291 bool download_profile_image = | |
| 292 user->image_index() == User::kProfileImageIndex; | |
| 293 if (download_profile_image) | |
| 294 InitDownloadedProfileImage(); | |
| 295 | |
| 296 // Download user's profile data (full name and optionally image) to see if | |
| 297 // it has changed. | |
| 298 BrowserThread::PostDelayedTask( | |
| 299 BrowserThread::UI, | |
| 300 FROM_HERE, | |
| 301 base::Bind(&UserImageManagerImpl::DownloadProfileData, | |
| 302 base::Unretained(this), | |
| 303 kProfileDownloadReasonLoggedIn, | |
| 304 download_profile_image), | |
| 305 base::TimeDelta::FromSeconds(kProfileDataDownloadDelaySec)); | |
| 306 } | |
| 307 | |
| 308 UMA_HISTOGRAM_ENUMERATION("UserImage.LoggedIn", | 521 UMA_HISTOGRAM_ENUMERATION("UserImage.LoggedIn", |
| 309 ImageIndexToHistogramIndex(user->image_index()), | 522 ImageIndexToHistogramIndex(user->image_index()), |
| 310 kHistogramImagesCount); | 523 kHistogramImagesCount); |
| 311 | 524 |
| 312 if (users_to_migrate_.count(email)) { | 525 if (users_to_migrate_.find(user_id) != users_to_migrate_.end()) { |
| 313 const DictionaryValue* prefs_images_unsafe = | 526 const DictionaryValue* prefs_images_unsafe = |
| 314 g_browser_process->local_state()->GetDictionary(kUserImages); | 527 g_browser_process->local_state()->GetDictionary(kUserImages); |
| 315 const base::DictionaryValue* image_properties = NULL; | 528 const base::DictionaryValue* image_properties = NULL; |
| 316 if (prefs_images_unsafe->GetDictionaryWithoutPathExpansion( | 529 if (prefs_images_unsafe->GetDictionaryWithoutPathExpansion( |
| 317 user->email(), &image_properties)) { | 530 user_id, &image_properties)) { |
| 318 std::string image_path; | 531 std::string image_path; |
| 319 image_properties->GetString(kImagePathNodeName, &image_path); | 532 image_properties->GetString(kImagePathNodeName, &image_path); |
| 533 linked_ptr<Job>& job = jobs_[user_id]; | |
| 534 job.reset(new Job(this, user_id)); | |
| 320 if (!image_path.empty()) { | 535 if (!image_path.empty()) { |
| 321 // User needs image format migration but | 536 LOG(INFO) << "Loading old user image, then migrating it."; |
| 322 // first we need to load and decode that image. | 537 job->SetToPath(base::FilePath(image_path), |
| 323 LOG(INFO) << "Waiting for user image to load before migration"; | 538 user->image_index(), |
| 324 migrate_current_user_on_load_ = true; | 539 user->image_url(), |
| 325 unsafe_image_loader_->Start( | 540 false); |
| 326 image_path, 0 /* no resize */, | |
| 327 base::Bind(&UserImageManagerImpl::SetUserImage, | |
| 328 base::Unretained(this), | |
| 329 user->email(), | |
| 330 user->image_index(), | |
| 331 user->image_url())); | |
| 332 } else { | 541 } else { |
| 333 // Otherwise migrate user image properties right away. | 542 job->SetToDefaultImage(user->image_index()); |
| 334 BrowserThread::PostDelayedTask( | |
| 335 BrowserThread::UI, | |
| 336 FROM_HERE, | |
| 337 base::Bind(&UserImageManagerImpl::MigrateUserImage, | |
| 338 base::Unretained(this)), | |
| 339 base::TimeDelta::FromSeconds(user_image_migration_delay_sec)); | |
| 340 } | 543 } |
| 341 } | 544 } |
| 342 } | 545 } |
| 343 } | 546 } |
| 344 | 547 |
| 345 if (!user_is_local) { | 548 // Reset the downloaded profile image as a new user logged in. |
| 346 // Set up a repeating timer for refreshing the profile data. | 549 downloaded_profile_image_ = gfx::ImageSkia(); |
| 347 profile_download_timer_.Start( | 550 downloaded_profile_image_data_url_.clear(); |
| 348 FROM_HERE, base::TimeDelta::FromSeconds(kProfileRefreshIntervalSec), | 551 profile_image_url_ = GURL(); |
| 349 this, &UserImageManagerImpl::DownloadProfileDataScheduled); | 552 profile_image_requested_ = false; |
| 553 | |
| 554 if (UserManager::Get()->IsLoggedInAsRegularUser()) { | |
| 555 TryToInitDownloadedProfileImage(); | |
| 556 | |
| 557 // Schedule an initial download of the profile data (full name and | |
| 558 // optionally image). | |
| 559 profile_download_one_shot_timer_.Start( | |
| 560 FROM_HERE, | |
| 561 base::TimeDelta::FromSeconds(kProfileDataDownloadDelaySec), | |
| 562 base::Bind(&UserImageManagerImpl::DownloadProfileData, | |
| 563 base::Unretained(this), | |
| 564 kProfileDownloadReasonLoggedIn)); | |
| 565 // Schedule periodic refreshes of the profile data. | |
| 566 profile_download_periodic_timer_.Start( | |
| 567 FROM_HERE, | |
| 568 base::TimeDelta::FromSeconds(kProfileRefreshIntervalSec), | |
| 569 base::Bind(&UserImageManagerImpl::DownloadProfileData, | |
| 570 base::Unretained(this), | |
| 571 kProfileDownloadReasonScheduled)); | |
| 572 } else { | |
| 573 profile_download_one_shot_timer_.Stop(); | |
| 574 profile_download_periodic_timer_.Stop(); | |
| 350 } | 575 } |
| 351 CommandLine* command_line = CommandLine::ForCurrentProcess(); | 576 |
| 577 const CommandLine* command_line = CommandLine::ForCurrentProcess(); | |
| 578 if (user_image_sync_observer_.get() && | |
| 579 !command_line->HasSwitch(::switches::kMultiProfiles)) { | |
| 580 NOTREACHED() << "User logged in more than once."; | |
| 581 } | |
| 582 | |
| 352 if (user->CanSyncImage() && | 583 if (user->CanSyncImage() && |
| 353 !command_line->HasSwitch(chromeos::switches::kDisableUserImageSync)) { | 584 !command_line->HasSwitch(chromeos::switches::kDisableUserImageSync)) { |
| 354 if (user_image_sync_observer_.get() && | |
| 355 !command_line->HasSwitch(::switches::kMultiProfiles)) | |
| 356 NOTREACHED() << "User logged in second time."; | |
| 357 user_image_sync_observer_.reset(new UserImageSyncObserver(user)); | 585 user_image_sync_observer_.reset(new UserImageSyncObserver(user)); |
| 586 } else { | |
| 587 user_image_sync_observer_.reset(); | |
| 358 } | 588 } |
| 359 } | 589 } |
| 360 | 590 |
| 361 void UserImageManagerImpl::SaveUserDefaultImageIndex( | 591 void UserImageManagerImpl::SaveUserDefaultImageIndex(const std::string& user_id, |
| 362 const std::string& username, | 592 int default_image_index) { |
| 363 int image_index) { | 593 linked_ptr<Job>& job = jobs_[user_id]; |
| 364 DCHECK(image_index >= 0 && image_index < kDefaultImagesCount); | 594 job.reset(new Job(this, user_id)); |
| 365 SetUserImage(username, image_index, GURL(), | 595 job->SetToDefaultImage(default_image_index); |
| 366 UserImage(GetDefaultImage(image_index))); | |
| 367 SaveImageToLocalState(username, "", image_index, GURL(), false); | |
| 368 } | 596 } |
| 369 | 597 |
| 370 void UserImageManagerImpl::SaveUserImage(const std::string& username, | 598 void UserImageManagerImpl::SaveUserImage(const std::string& user_id, |
| 371 const UserImage& user_image) { | 599 const UserImage& user_image) { |
| 372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 600 linked_ptr<Job>& job = jobs_[user_id]; |
| 373 SaveUserImageInternal(username, User::kExternalImageIndex, | 601 job.reset(new Job(this, user_id)); |
| 374 GURL(), user_image); | 602 job->SetToImage(User::kExternalImageIndex, user_image); |
| 375 } | 603 } |
| 376 | 604 |
| 377 void UserImageManagerImpl::SaveUserImageFromFile(const std::string& username, | 605 void UserImageManagerImpl::SaveUserImageFromFile(const std::string& user_id, |
| 378 const base::FilePath& path) { | 606 const base::FilePath& path) { |
| 379 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 607 linked_ptr<Job>& job = jobs_[user_id]; |
| 380 // Always use unsafe image loader because we resize the image when saving | 608 job.reset(new Job(this, user_id)); |
| 381 // anyway. | 609 job->SetToPath(path, User::kExternalImageIndex, GURL(), true); |
| 382 unsafe_image_loader_->Start( | |
| 383 path.value(), login::kMaxUserImageSize, | |
| 384 base::Bind(&UserImageManagerImpl::SaveUserImage, | |
| 385 base::Unretained(this), username)); | |
| 386 } | 610 } |
| 387 | 611 |
| 388 void UserImageManagerImpl::SaveUserImageFromProfileImage( | 612 void UserImageManagerImpl::SaveUserImageFromProfileImage( |
| 389 const std::string& username) { | 613 const std::string& user_id) { |
| 390 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 614 // Use the profile image if it has been downloaded already. Otherwise, use a |
| 391 if (!downloaded_profile_image_.isNull()) { | 615 // stub image (gray avatar). |
| 392 // Profile image has already been downloaded, so save it to file right now. | 616 linked_ptr<Job>& job = jobs_[user_id]; |
| 393 DCHECK(profile_image_url_.is_valid()); | 617 job.reset(new Job(this, user_id)); |
| 394 SaveUserImageInternal( | 618 job->SetToImage(User::kProfileImageIndex, |
| 395 username, | 619 downloaded_profile_image_.isNull() ? |
| 396 User::kProfileImageIndex, profile_image_url_, | 620 UserImage() : |
| 397 UserImage::CreateAndEncode(downloaded_profile_image_)); | 621 UserImage::CreateAndEncode(downloaded_profile_image_)); |
| 398 } else { | |
| 399 // No profile image - use the stub image (gray avatar). | |
| 400 SetUserImage(username, User::kProfileImageIndex, GURL(), UserImage()); | |
| 401 SaveImageToLocalState(username, "", User::kProfileImageIndex, | |
| 402 GURL(), false); | |
| 403 } | |
| 404 } | 622 } |
| 405 | 623 |
| 406 void UserImageManagerImpl::DeleteUserImage(const std::string& username) { | 624 void UserImageManagerImpl::DeleteUserImage(const std::string& user_id) { |
| 407 // Delete from the old dictionary, if present. | 625 jobs_.erase(user_id); |
| 408 DeleteOldUserImage(username); | 626 DeleteUserImageAndLocalStateEntry(user_id, kUserImages); |
| 409 | 627 DeleteUserImageAndLocalStateEntry(user_id, kUserImageProperties); |
| 410 PrefService* prefs = g_browser_process->local_state(); | |
| 411 DictionaryPrefUpdate prefs_images_update(prefs, kUserImageProperties); | |
| 412 const base::DictionaryValue* image_properties; | |
| 413 if (prefs_images_update->GetDictionaryWithoutPathExpansion( | |
| 414 username, &image_properties)) { | |
| 415 std::string image_path; | |
| 416 image_properties->GetString(kImageURLNodeName, &image_path); | |
| 417 prefs_images_update->RemoveWithoutPathExpansion(username, NULL); | |
| 418 DeleteImageFile(image_path); | |
| 419 } | |
| 420 } | 628 } |
| 421 | 629 |
| 422 void UserImageManagerImpl::DownloadProfileImage(const std::string& reason) { | 630 void UserImageManagerImpl::DownloadProfileImage(const std::string& reason) { |
| 423 DownloadProfileData(reason, true); | 631 profile_image_requested_ = true; |
| 632 DownloadProfileData(reason); | |
| 424 } | 633 } |
| 425 | 634 |
| 426 UserImageSyncObserver* UserImageManagerImpl::GetSyncObserver() const { | 635 UserImageSyncObserver* UserImageManagerImpl::GetSyncObserver() const { |
| 427 return user_image_sync_observer_.get(); | 636 return user_image_sync_observer_.get(); |
| 428 } | 637 } |
| 429 | 638 |
| 430 void UserImageManagerImpl::Shutdown() { | 639 void UserImageManagerImpl::Shutdown() { |
| 431 profile_image_downloader_.reset(); | 640 profile_downloader_.reset(); |
| 432 user_image_sync_observer_.reset(); | 641 user_image_sync_observer_.reset(); |
| 433 } | 642 } |
| 434 | 643 |
| 435 const gfx::ImageSkia& UserImageManagerImpl::DownloadedProfileImage() const { | 644 const gfx::ImageSkia& UserImageManagerImpl::DownloadedProfileImage() const { |
| 436 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 437 return downloaded_profile_image_; | 645 return downloaded_profile_image_; |
| 438 } | 646 } |
| 439 | 647 |
| 440 base::FilePath UserImageManagerImpl::GetImagePathForUser( | 648 void UserImageManagerImpl::SetInitialUserImage(const std::string& user_id) { |
| 441 const std::string& username) { | 649 // Choose a random default image. |
| 442 std::string filename = username + kSafeImagePathExtension; | 650 SaveUserDefaultImageIndex(user_id, |
| 443 base::FilePath user_data_dir; | 651 base::RandInt(kFirstDefaultImageIndex, |
| 444 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); | 652 kDefaultImagesCount - 1)); |
| 445 return user_data_dir.AppendASCII(filename); | |
| 446 } | 653 } |
| 447 | 654 |
| 448 void UserImageManagerImpl::SetInitialUserImage(const std::string& username) { | 655 void UserImageManagerImpl::TryToInitDownloadedProfileImage() { |
| 449 // Choose a random default image. | 656 const User* user = UserManager::Get()->GetLoggedInUser(); |
| 450 int image_id = | 657 if (user->image_index() == User::kProfileImageIndex && |
| 451 base::RandInt(kFirstDefaultImageIndex, kDefaultImagesCount - 1); | 658 downloaded_profile_image_.isNull() && |
| 452 SaveUserDefaultImageIndex(username, image_id); | 659 !user->image_is_stub()) { |
| 453 } | 660 // Initialize the |downloaded_profile_image_| for the currently logged-in |
| 454 | 661 // user if it has not been initialized already, the user image is the |
| 455 void UserImageManagerImpl::SetUserImage(const std::string& username, | 662 // profile image and the user image has been loaded successfully. |
| 456 int image_index, | 663 VLOG(1) << "Profile image initialized from disk."; |
| 457 const GURL& image_url, | 664 downloaded_profile_image_ = user->image(); |
| 458 const UserImage& user_image) { | 665 downloaded_profile_image_data_url_ = |
| 459 User* user = const_cast<User*>(UserManager::Get()->FindUser(username)); | 666 webui::GetBitmapDataUrl(*downloaded_profile_image_.bitmap()); |
| 460 // User may have been removed by now. | 667 profile_image_url_ = user->image_url(); |
| 461 if (user) { | |
| 462 bool image_changed = user->image_index() != User::kInvalidImageIndex; | |
| 463 bool is_current_user = user == UserManager::Get()->GetLoggedInUser(); | |
| 464 if (!user_image.image().isNull()) | |
| 465 user->SetImage(user_image, image_index); | |
| 466 else | |
| 467 user->SetStubImage(image_index, false); | |
| 468 user->SetImageURL(image_url); | |
| 469 // For the logged-in user with a profile picture, initialize | |
| 470 // |downloaded_profile_picture_|. | |
| 471 if (is_current_user && image_index == User::kProfileImageIndex) { | |
| 472 InitDownloadedProfileImage(); | |
| 473 } | |
| 474 if (image_changed) { | |
| 475 // Unless this is first-time setting with |SetInitialUserImage|, | |
| 476 // send a notification about image change. | |
| 477 content::NotificationService::current()->Notify( | |
| 478 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, | |
| 479 content::Source<UserImageManager>(this), | |
| 480 content::Details<const User>(user)); | |
| 481 } | |
| 482 if (is_current_user && migrate_current_user_on_load_) | |
| 483 MigrateUserImage(); | |
| 484 } | 668 } |
| 485 } | 669 } |
| 486 | 670 |
| 487 void UserImageManagerImpl::SaveUserImageInternal(const std::string& username, | 671 bool UserImageManagerImpl::NeedProfileImage() const { |
| 488 int image_index, | 672 return UserManager::Get()->IsLoggedInAsRegularUser() && |
| 489 const GURL& image_url, | 673 (UserManager::Get()->GetLoggedInUser()->image_index() == |
| 490 const UserImage& user_image) { | 674 User::kProfileImageIndex || |
| 491 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 675 profile_image_requested_); |
| 492 | |
| 493 SetUserImage(username, image_index, image_url, user_image); | |
| 494 | |
| 495 // Ignore if data stored or cached outside the user's cryptohome is to be | |
| 496 // treated as ephemeral. | |
| 497 if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(username)) | |
| 498 return; | |
| 499 | |
| 500 base::FilePath image_path = GetImagePathForUser(username); | |
| 501 DVLOG(1) << "Saving user image to " << image_path.value(); | |
| 502 | |
| 503 last_image_set_async_ = true; | |
| 504 | |
| 505 base::WorkerPool::PostTask( | |
| 506 FROM_HERE, | |
| 507 base::Bind(&UserImageManagerImpl::SaveImageToFile, | |
| 508 base::Unretained(this), | |
| 509 username, user_image, image_path, image_index, image_url), | |
| 510 /* is_slow= */ false); | |
| 511 } | 676 } |
| 512 | 677 |
| 513 void UserImageManagerImpl::SaveImageToFile(const std::string& username, | 678 void UserImageManagerImpl::DownloadProfileData(const std::string& reason) { |
| 514 const UserImage& user_image, | |
| 515 const base::FilePath& image_path, | |
| 516 int image_index, | |
| 517 const GURL& image_url) { | |
| 518 if (!SaveBitmapToFile(user_image, image_path)) | |
| 519 return; | |
| 520 | |
| 521 BrowserThread::PostTask( | |
| 522 BrowserThread::UI, | |
| 523 FROM_HERE, | |
| 524 base::Bind(&UserImageManagerImpl::SaveImageToLocalState, | |
| 525 base::Unretained(this), | |
| 526 username, image_path.value(), image_index, image_url, true)); | |
| 527 } | |
| 528 | |
| 529 void UserImageManagerImpl::SaveImageToLocalState(const std::string& username, | |
| 530 const std::string& image_path, | |
| 531 int image_index, | |
| 532 const GURL& image_url, | |
| 533 bool is_async) { | |
| 534 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 535 | |
| 536 // Ignore if data stored or cached outside the user's cryptohome is to be | |
| 537 // treated as ephemeral. | |
| 538 if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(username)) | |
| 539 return; | |
| 540 | |
| 541 // TODO(ivankr): use unique filenames for user images each time | |
| 542 // a new image is set so that only the last image update is saved | |
| 543 // to Local State and notified. | |
| 544 if (is_async && !last_image_set_async_) { | |
| 545 DVLOG(1) << "Ignoring saved image because it has changed"; | |
| 546 return; | |
| 547 } else if (!is_async) { | |
| 548 // Reset the async image save flag if called directly from the UI thread. | |
| 549 last_image_set_async_ = false; | |
| 550 } | |
| 551 | |
| 552 PrefService* local_state = g_browser_process->local_state(); | |
| 553 DictionaryPrefUpdate images_update(local_state, kUserImageProperties); | |
| 554 base::DictionaryValue* image_properties = new base::DictionaryValue(); | |
| 555 image_properties->Set(kImagePathNodeName, new StringValue(image_path)); | |
| 556 image_properties->Set(kImageIndexNodeName, | |
| 557 new base::FundamentalValue(image_index)); | |
| 558 if (!image_url.is_empty()) { | |
| 559 image_properties->Set(kImageURLNodeName, | |
| 560 new StringValue(image_url.spec())); | |
| 561 } else { | |
| 562 image_properties->Remove(kImageURLNodeName, NULL); | |
| 563 } | |
| 564 images_update->SetWithoutPathExpansion(username, image_properties); | |
| 565 DVLOG(1) << "Saving path to user image in Local State."; | |
| 566 | |
| 567 if (users_to_migrate_.count(username)) { | |
| 568 DeleteOldUserImage(username); | |
| 569 users_to_migrate_.erase(username); | |
| 570 } | |
| 571 | |
| 572 UserManager::Get()->NotifyLocalStateChanged(); | |
| 573 } | |
| 574 | |
| 575 bool UserImageManagerImpl::SaveBitmapToFile(const UserImage& user_image, | |
| 576 const base::FilePath& image_path) { | |
| 577 UserImage safe_image; | |
| 578 const UserImage::RawImage* encoded_image = NULL; | |
| 579 if (!user_image.is_safe_format()) { | |
| 580 safe_image = UserImage::CreateAndEncode(user_image.image()); | |
| 581 encoded_image = &safe_image.raw_image(); | |
| 582 UMA_HISTOGRAM_MEMORY_KB("UserImage.RecodedJpegSize", encoded_image->size()); | |
| 583 } else if (user_image.has_raw_image()) { | |
| 584 encoded_image = &user_image.raw_image(); | |
| 585 } else { | |
| 586 NOTREACHED() << "Raw image missing."; | |
| 587 return false; | |
| 588 } | |
| 589 | |
| 590 if (file_util::WriteFile(image_path, | |
| 591 reinterpret_cast<const char*>(&(*encoded_image)[0]), | |
| 592 encoded_image->size()) == -1) { | |
| 593 LOG(ERROR) << "Failed to save image to file."; | |
| 594 return false; | |
| 595 } | |
| 596 return true; | |
| 597 } | |
| 598 | |
| 599 void UserImageManagerImpl::InitDownloadedProfileImage() { | |
| 600 const User* logged_in_user = UserManager::Get()->GetLoggedInUser(); | |
| 601 DCHECK_EQ(logged_in_user->image_index(), User::kProfileImageIndex); | |
| 602 if (downloaded_profile_image_.isNull() && !logged_in_user->image_is_stub()) { | |
| 603 VLOG(1) << "Profile image initialized"; | |
| 604 downloaded_profile_image_ = logged_in_user->image(); | |
| 605 downloaded_profile_image_data_url_ = | |
| 606 webui::GetBitmapDataUrl(*downloaded_profile_image_.bitmap()); | |
| 607 profile_image_url_ = logged_in_user->image_url(); | |
| 608 } | |
| 609 } | |
| 610 | |
| 611 void UserImageManagerImpl::DownloadProfileData(const std::string& reason, | |
| 612 bool download_image) { | |
| 613 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 614 | |
| 615 // GAIA profiles exist for regular users only. | 679 // GAIA profiles exist for regular users only. |
| 616 if (!UserManager::Get()->IsLoggedInAsRegularUser()) | 680 if (!UserManager::Get()->IsLoggedInAsRegularUser()) |
| 617 return; | 681 return; |
| 618 | 682 |
| 619 // Mark profile picture as needed. | 683 // If a download is already in progress, allow it to continue, with one |
| 620 downloading_profile_image_ |= download_image; | 684 // exception: If the current download does not include the profile image but |
| 685 // the image has since become necessary, start a new download that includes | |
| 686 // the profile image. | |
| 687 if (profile_downloader_ && | |
| 688 (downloading_profile_image_ || !NeedProfileImage())) { | |
| 689 return; | |
| 690 } | |
| 621 | 691 |
| 622 // Another download is already in progress | 692 downloading_profile_image_ = NeedProfileImage(); |
| 623 if (profile_image_downloader_.get()) | |
| 624 return; | |
| 625 | |
| 626 profile_image_download_reason_ = reason; | 693 profile_image_download_reason_ = reason; |
| 627 profile_image_load_start_time_ = base::Time::Now(); | 694 profile_image_load_start_time_ = base::TimeTicks::Now(); |
| 628 profile_image_downloader_.reset(new ProfileDownloader(this)); | 695 profile_downloader_.reset(new ProfileDownloader(this)); |
| 629 profile_image_downloader_->Start(); | 696 profile_downloader_->Start(); |
| 630 } | 697 } |
| 631 | 698 |
| 632 void UserImageManagerImpl::DownloadProfileDataScheduled() { | |
| 633 const User* logged_in_user = UserManager::Get()->GetLoggedInUser(); | |
| 634 // If current user image is profile image, it needs to be refreshed. | |
| 635 bool download_profile_image = | |
| 636 logged_in_user->image_index() == User::kProfileImageIndex; | |
| 637 DownloadProfileData(kProfileDownloadReasonScheduled, download_profile_image); | |
| 638 } | |
| 639 | |
| 640 void UserImageManagerImpl::DownloadProfileDataRetry(bool download_image) { | |
| 641 DownloadProfileData(kProfileDownloadReasonRetry, download_image); | |
| 642 } | |
| 643 | |
| 644 // ProfileDownloaderDelegate override. | |
| 645 bool UserImageManagerImpl::NeedsProfilePicture() const { | 699 bool UserImageManagerImpl::NeedsProfilePicture() const { |
| 646 return downloading_profile_image_; | 700 return downloading_profile_image_; |
| 647 } | 701 } |
| 648 | 702 |
| 649 // ProfileDownloaderDelegate override. | |
| 650 int UserImageManagerImpl::GetDesiredImageSideLength() const { | 703 int UserImageManagerImpl::GetDesiredImageSideLength() const { |
| 651 return GetCurrentUserImageSize(); | 704 return GetCurrentUserImageSize(); |
| 652 } | 705 } |
| 653 | 706 |
| 654 // ProfileDownloaderDelegate override. | |
| 655 std::string UserImageManagerImpl::GetCachedPictureURL() const { | 707 std::string UserImageManagerImpl::GetCachedPictureURL() const { |
| 656 return profile_image_url_.spec(); | 708 return profile_image_url_.spec(); |
| 657 } | 709 } |
| 658 | 710 |
| 659 Profile* UserImageManagerImpl::GetBrowserProfile() { | 711 Profile* UserImageManagerImpl::GetBrowserProfile() { |
| 660 return ProfileManager::GetDefaultProfile(); | 712 return ProfileManager::GetDefaultProfile(); |
| 661 } | 713 } |
| 662 | 714 |
| 663 void UserImageManagerImpl::OnProfileDownloadSuccess( | 715 void UserImageManagerImpl::OnProfileDownloadSuccess( |
| 664 ProfileDownloader* downloader) { | 716 ProfileDownloader* downloader) { |
| 665 // Make sure that |ProfileDownloader| gets deleted after return. | 717 // Ensure that the |profile_downloader_| is deleted when this method returns. |
| 666 scoped_ptr<ProfileDownloader> profile_image_downloader( | 718 scoped_ptr<ProfileDownloader> profile_downloader( |
| 667 profile_image_downloader_.release()); | 719 profile_downloader_.release()); |
| 668 DCHECK_EQ(downloader, profile_image_downloader.get()); | 720 DCHECK_EQ(downloader, profile_downloader.get()); |
| 669 | 721 |
| 670 UserManager* user_manager = UserManager::Get(); | 722 const User* user = UserManager::Get()->GetLoggedInUser(); |
| 671 const User* user = user_manager->GetLoggedInUser(); | 723 const std::string& user_id = user->email(); |
| 672 | 724 |
| 673 user_manager->UpdateUserAccountData(user->email(), | 725 UserManager::Get()->UpdateUserAccountData(user_id, |
| 674 downloader->GetProfileFullName(), | 726 downloader->GetProfileFullName(), |
| 675 downloader->GetProfileLocale()); | 727 downloader->GetProfileLocale()); |
| 676 | 728 |
| 677 bool requested_image = downloading_profile_image_; | 729 if (!downloading_profile_image_) |
| 678 downloading_profile_image_ = false; | |
| 679 if (!requested_image) | |
| 680 return; | 730 return; |
| 681 | 731 |
| 682 ProfileDownloadResult result = kDownloadFailure; | 732 ProfileDownloadResult result = kDownloadFailure; |
| 683 switch (downloader->GetProfilePictureStatus()) { | 733 switch (downloader->GetProfilePictureStatus()) { |
| 684 case ProfileDownloader::PICTURE_SUCCESS: | 734 case ProfileDownloader::PICTURE_SUCCESS: |
| 685 result = kDownloadSuccess; | 735 result = kDownloadSuccess; |
| 686 break; | 736 break; |
| 687 case ProfileDownloader::PICTURE_CACHED: | 737 case ProfileDownloader::PICTURE_CACHED: |
| 688 result = kDownloadCached; | 738 result = kDownloadCached; |
| 689 break; | 739 break; |
| 690 case ProfileDownloader::PICTURE_DEFAULT: | 740 case ProfileDownloader::PICTURE_DEFAULT: |
| 691 result = kDownloadDefault; | 741 result = kDownloadDefault; |
| 692 break; | 742 break; |
| 693 default: | 743 default: |
| 694 NOTREACHED(); | 744 NOTREACHED(); |
| 695 } | 745 } |
| 696 | 746 |
| 697 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", | 747 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", |
| 698 result, kDownloadResultsCount); | 748 result, |
| 749 kDownloadResultsCount); | |
| 750 DCHECK(!profile_image_load_start_time_.is_null()); | |
| 751 AddProfileImageTimeHistogram( | |
| 752 result, | |
| 753 profile_image_download_reason_, | |
| 754 base::TimeTicks::Now() - profile_image_load_start_time_); | |
| 699 | 755 |
| 700 DCHECK(!profile_image_load_start_time_.is_null()); | 756 // Ignore the image if it is no longer needed. |
| 701 base::TimeDelta delta = base::Time::Now() - profile_image_load_start_time_; | 757 if (!NeedProfileImage()) |
| 702 AddProfileImageTimeHistogram(result, profile_image_download_reason_, delta); | 758 return; |
| 703 | 759 |
| 704 if (result == kDownloadDefault) { | 760 if (result == kDownloadDefault) { |
| 705 content::NotificationService::current()->Notify( | 761 content::NotificationService::current()->Notify( |
| 706 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, | 762 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, |
| 707 content::Source<UserImageManager>(this), | 763 content::Source<UserImageManager>(this), |
| 708 content::NotificationService::NoDetails()); | 764 content::NotificationService::NoDetails()); |
| 709 } | 765 } |
| 710 | 766 |
| 711 // Nothing to do if picture is cached or the default avatar. | 767 // Nothing to do if the picture is cached or is the default avatar. |
| 712 if (result != kDownloadSuccess) | 768 if (result != kDownloadSuccess) |
| 713 return; | 769 return; |
| 714 | 770 |
| 771 profile_image_requested_ = false; | |
| 772 | |
| 715 // Check if this image is not the same as already downloaded. | 773 // Check if this image is not the same as already downloaded. |
| 716 SkBitmap new_bitmap(downloader->GetProfilePicture()); | 774 const std::string new_image_data_url = |
| 717 std::string new_image_data_url = webui::GetBitmapDataUrl(new_bitmap); | 775 webui::GetBitmapDataUrl(SkBitmap(downloader->GetProfilePicture())); |
| 718 if (!downloaded_profile_image_data_url_.empty() && | 776 if (!downloaded_profile_image_data_url_.empty() && |
| 719 new_image_data_url == downloaded_profile_image_data_url_) | 777 new_image_data_url == downloaded_profile_image_data_url_) { |
| 720 return; | 778 return; |
| 779 } | |
| 721 | 780 |
| 722 downloaded_profile_image_data_url_ = new_image_data_url; | 781 downloaded_profile_image_data_url_ = new_image_data_url; |
| 723 downloaded_profile_image_ = gfx::ImageSkia::CreateFrom1xBitmap( | 782 downloaded_profile_image_ = gfx::ImageSkia::CreateFrom1xBitmap( |
| 724 downloader->GetProfilePicture()); | 783 downloader->GetProfilePicture()); |
| 725 profile_image_url_ = GURL(downloader->GetProfilePictureURL()); | 784 profile_image_url_ = GURL(downloader->GetProfilePictureURL()); |
| 726 | 785 |
| 727 if (user->image_index() == User::kProfileImageIndex) { | 786 if (user->image_index() == User::kProfileImageIndex) { |
| 728 VLOG(1) << "Updating profile image for logged-in user"; | 787 VLOG(1) << "Updating profile image for logged-in user."; |
| 729 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", | 788 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", |
| 730 kDownloadSuccessChanged, | 789 kDownloadSuccessChanged, |
| 731 kDownloadResultsCount); | 790 kDownloadResultsCount); |
| 732 // This will persist |downloaded_profile_image_| to file. | 791 // This will persist |downloaded_profile_image_| to disk. |
| 733 SaveUserImageFromProfileImage(user->email()); | 792 SaveUserImageFromProfileImage(user_id); |
| 734 } | 793 } |
| 735 | 794 |
| 736 content::NotificationService::current()->Notify( | 795 content::NotificationService::current()->Notify( |
| 737 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED, | 796 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED, |
| 738 content::Source<UserImageManager>(this), | 797 content::Source<UserImageManager>(this), |
| 739 content::Details<const gfx::ImageSkia>(&downloaded_profile_image_)); | 798 content::Details<const gfx::ImageSkia>(&downloaded_profile_image_)); |
| 740 } | 799 } |
| 741 | 800 |
| 742 void UserImageManagerImpl::OnProfileDownloadFailure( | 801 void UserImageManagerImpl::OnProfileDownloadFailure( |
| 743 ProfileDownloader* downloader, | 802 ProfileDownloader* downloader, |
| 744 ProfileDownloaderDelegate::FailureReason reason) { | 803 ProfileDownloaderDelegate::FailureReason reason) { |
| 745 DCHECK_EQ(downloader, profile_image_downloader_.get()); | 804 DCHECK_EQ(downloader, profile_downloader_.get()); |
| 746 profile_image_downloader_.reset(); | 805 profile_downloader_.reset(); |
| 747 | 806 |
| 748 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", | 807 if (downloading_profile_image_) { |
| 749 kDownloadFailure, kDownloadResultsCount); | 808 UMA_HISTOGRAM_ENUMERATION("UserImage.ProfileDownloadResult", |
| 750 | 809 kDownloadFailure, |
| 751 DCHECK(!profile_image_load_start_time_.is_null()); | 810 kDownloadResultsCount); |
| 752 base::TimeDelta delta = base::Time::Now() - profile_image_load_start_time_; | 811 DCHECK(!profile_image_load_start_time_.is_null()); |
| 753 AddProfileImageTimeHistogram(kDownloadFailure, profile_image_download_reason_, | 812 AddProfileImageTimeHistogram( |
| 754 delta); | 813 kDownloadFailure, |
| 814 profile_image_download_reason_, | |
| 815 base::TimeTicks::Now() - profile_image_load_start_time_); | |
| 816 } | |
| 755 | 817 |
| 756 UserManager* user_manager = UserManager::Get(); | 818 UserManager* user_manager = UserManager::Get(); |
| 757 const User* user = user_manager->GetLoggedInUser(); | 819 const User* user = user_manager->GetLoggedInUser(); |
| 758 | 820 |
| 759 // Need note that at least one attempt finished. | |
| 760 user_manager->UpdateUserAccountData(user->email(), string16(), ""); | 821 user_manager->UpdateUserAccountData(user->email(), string16(), ""); |
| 761 | 822 |
| 762 // Retry download after some time if a network error has occured. | |
| 763 if (reason == ProfileDownloaderDelegate::NETWORK_ERROR) { | 823 if (reason == ProfileDownloaderDelegate::NETWORK_ERROR) { |
| 764 BrowserThread::PostDelayedTask( | 824 // Retry download after a delay if a network error occurred. |
| 765 BrowserThread::UI, | 825 profile_download_one_shot_timer_.Start( |
| 766 FROM_HERE, | 826 FROM_HERE, |
| 767 base::Bind(&UserImageManagerImpl::DownloadProfileDataRetry, | 827 base::TimeDelta::FromSeconds(kProfileDataDownloadRetryIntervalSec), |
| 828 base::Bind(&UserImageManagerImpl::DownloadProfileData, | |
| 768 base::Unretained(this), | 829 base::Unretained(this), |
| 769 downloading_profile_image_), | 830 kProfileDownloadReasonRetry)); |
| 770 base::TimeDelta::FromSeconds(kProfileDataDownloadRetryIntervalSec)); | |
| 771 } | 831 } |
| 772 | 832 |
| 773 downloading_profile_image_ = false; | |
| 774 | |
| 775 content::NotificationService::current()->Notify( | 833 content::NotificationService::current()->Notify( |
| 776 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, | 834 chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED, |
| 777 content::Source<UserImageManager>(this), | 835 content::Source<UserImageManager>(this), |
| 778 content::NotificationService::NoDetails()); | 836 content::NotificationService::NoDetails()); |
| 779 } | 837 } |
| 780 | 838 |
| 781 void UserImageManagerImpl::MigrateUserImage() { | 839 void UserImageManagerImpl::DeleteUserImageAndLocalStateEntry( |
| 782 User* user = UserManager::Get()->GetLoggedInUser(); | 840 const std::string& user_id, |
| 783 if (user->image_is_loading()) { | 841 const char* prefs_dict_root) { |
| 784 LOG(INFO) << "Waiting for user image to load before migration"; | 842 DictionaryPrefUpdate update(g_browser_process->local_state(), |
| 785 migrate_current_user_on_load_ = true; | 843 prefs_dict_root); |
| 844 const base::DictionaryValue* image_properties; | |
| 845 if (!update->GetDictionaryWithoutPathExpansion(user_id, &image_properties)) | |
| 846 return; | |
| 847 | |
| 848 std::string image_path; | |
| 849 image_properties->GetString(kImagePathNodeName, &image_path); | |
| 850 if (!image_path.empty()) { | |
| 851 background_task_runner_->PostTask( | |
| 852 FROM_HERE, | |
| 853 base::Bind(base::IgnoreResult(&base::DeleteFile), | |
| 854 base::FilePath(image_path), | |
| 855 false)); | |
| 856 } | |
| 857 update->RemoveWithoutPathExpansion(user_id, NULL); | |
| 858 } | |
| 859 | |
| 860 void UserImageManagerImpl::OnJobChangedUserImage(const User* user) { | |
| 861 if (user == UserManager::Get()->GetLoggedInUser()) | |
| 862 TryToInitDownloadedProfileImage(); | |
| 863 | |
| 864 content::NotificationService::current()->Notify( | |
| 865 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED, | |
| 866 content::Source<UserImageManagerImpl>(this), | |
| 867 content::Details<const User>(user)); | |
| 868 } | |
| 869 | |
| 870 void UserImageManagerImpl::OnJobDone(const std::string& user_id) { | |
| 871 std::map<std::string, linked_ptr<Job> >::iterator it = | |
| 872 jobs_.find(user_id); | |
| 873 if (it != jobs_.end()) { | |
| 874 // The Job for |user_id| is done and must be destroyed. Since control will | |
| 875 // return to the Job when this method finishes, it cannot be destroyed from | |
| 876 // here. Instead, ownership is dropped and the Job destroys itself. | |
| 877 it->second.release(); | |
| 878 jobs_.erase(it); | |
| 879 } else { | |
| 880 NOTREACHED(); | |
| 881 } | |
| 882 | |
| 883 if (users_to_migrate_.find(user_id) == users_to_migrate_.end()) | |
| 884 return; | |
| 885 // Migration completed for |user_id|. | |
| 886 users_to_migrate_.erase(user_id); | |
| 887 | |
| 888 const DictionaryValue* prefs_images_unsafe = | |
| 889 g_browser_process->local_state()->GetDictionary(kUserImages); | |
| 890 const base::DictionaryValue* image_properties = NULL; | |
| 891 if (!prefs_images_unsafe->GetDictionaryWithoutPathExpansion( | |
| 892 user_id, &image_properties)) { | |
| 893 NOTREACHED(); | |
| 786 return; | 894 return; |
| 787 } | 895 } |
| 788 migrate_current_user_on_load_ = false; | 896 |
| 789 if (user->has_raw_image() && user->image_is_safe_format()) { | 897 int image_index = User::kInvalidImageIndex; |
| 790 // Nothing to migrate already, make sure we delete old image. | 898 image_properties->GetInteger(kImageIndexNodeName, &image_index); |
| 791 DeleteOldUserImage(user->email()); | 899 UMA_HISTOGRAM_ENUMERATION("UserImage.Migration", |
| 792 users_to_migrate_.erase(user->email()); | 900 ImageIndexToHistogramIndex(image_index), |
| 793 return; | 901 kHistogramImagesCount); |
| 794 } | 902 |
| 795 if (user->HasDefaultImage()) { | 903 std::string image_path; |
| 796 SaveUserDefaultImageIndex(user->email(), user->image_index()); | 904 image_properties->GetString(kImagePathNodeName, &image_path); |
| 905 if (!image_path.empty()) { | |
| 906 // If an old image exists, delete it and remove |user_id| from the old prefs | |
| 907 // dictionary only after the deletion has completed. This ensures that no | |
| 908 // orphaned image is left behind if the browser crashes before the deletion | |
| 909 // has been performed: In that case, local state will be unchanged and the | |
| 910 // migration will be run again on the user's next login. | |
| 911 background_task_runner_->PostTaskAndReply( | |
| 912 FROM_HERE, | |
| 913 base::Bind(base::IgnoreResult(&base::DeleteFile), | |
| 914 base::FilePath(image_path), | |
| 915 false), | |
| 916 base::Bind(&UserImageManagerImpl::UpdateLocalStateAfterMigration, | |
| 917 weak_factory_.GetWeakPtr(), | |
| 918 user_id)); | |
| 797 } else { | 919 } else { |
| 798 SaveUserImageInternal(user->email(), user->image_index(), | 920 // If no old image exists, remove |user_id| from the old prefs dictionary. |
| 799 user->image_url(), user->user_image()); | 921 UpdateLocalStateAfterMigration(user_id); |
| 800 } | |
| 801 UMA_HISTOGRAM_ENUMERATION("UserImage.Migration", | |
| 802 ImageIndexToHistogramIndex(user->image_index()), | |
| 803 kHistogramImagesCount); | |
| 804 } | |
| 805 | |
| 806 void UserImageManagerImpl::DeleteOldUserImage(const std::string& username) { | |
| 807 PrefService* prefs = g_browser_process->local_state(); | |
| 808 DictionaryPrefUpdate prefs_images_update(prefs, kUserImages); | |
| 809 const base::DictionaryValue* image_properties; | |
| 810 if (prefs_images_update->GetDictionaryWithoutPathExpansion( | |
| 811 username, &image_properties)) { | |
| 812 std::string image_path; | |
| 813 image_properties->GetString(kImagePathNodeName, &image_path); | |
| 814 prefs_images_update->RemoveWithoutPathExpansion(username, NULL); | |
| 815 DeleteImageFile(image_path); | |
| 816 } | 922 } |
| 817 } | 923 } |
| 818 | 924 |
| 925 void UserImageManagerImpl::UpdateLocalStateAfterMigration( | |
| 926 const std::string& user_id) { | |
| 927 DictionaryPrefUpdate update(g_browser_process->local_state(), | |
| 928 kUserImages); | |
| 929 update->RemoveWithoutPathExpansion(user_id, NULL); | |
| 930 } | |
| 931 | |
| 819 } // namespace chromeos | 932 } // namespace chromeos |
| OLD | NEW |