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

Side by Side Diff: chrome/browser/chromeos/login/user_image_manager_impl.cc

Issue 69863006: Address races in UserImageManagerImpl (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix WallpaperManager browser tests now that UserImageLoader contains a DCHECK(success). Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 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/message_loop/message_loop_proxy.h"
13 #include "base/metrics/histogram.h" 14 #include "base/metrics/histogram.h"
14 #include "base/path_service.h" 15 #include "base/path_service.h"
15 #include "base/prefs/pref_registry_simple.h" 16 #include "base/prefs/pref_registry_simple.h"
16 #include "base/prefs/pref_service.h" 17 #include "base/prefs/pref_service.h"
17 #include "base/prefs/scoped_user_pref_update.h" 18 #include "base/prefs/scoped_user_pref_update.h"
18 #include "base/rand_util.h" 19 #include "base/rand_util.h"
19 #include "base/sequenced_task_runner.h" 20 #include "base/sequenced_task_runner.h"
21 #include "base/task_runner_util.h"
20 #include "base/threading/sequenced_worker_pool.h" 22 #include "base/threading/sequenced_worker_pool.h"
21 #include "base/threading/worker_pool.h"
22 #include "base/time/time.h" 23 #include "base/time/time.h"
23 #include "base/values.h"
24 #include "chrome/browser/browser_process.h" 24 #include "chrome/browser/browser_process.h"
25 #include "chrome/browser/chrome_notification_types.h" 25 #include "chrome/browser/chrome_notification_types.h"
26 #include "chrome/browser/chromeos/login/default_user_images.h" 26 #include "chrome/browser/chromeos/login/default_user_images.h"
27 #include "chrome/browser/chromeos/login/helper.h" 27 #include "chrome/browser/chromeos/login/helper.h"
28 #include "chrome/browser/chromeos/login/user_image.h" 28 #include "chrome/browser/chromeos/login/user_image.h"
29 #include "chrome/browser/chromeos/login/user_image_sync_observer.h" 29 #include "chrome/browser/chromeos/login/user_image_sync_observer.h"
30 #include "chrome/browser/chromeos/login/user_manager.h" 30 #include "chrome/browser/chromeos/login/user_manager.h"
31 #include "chrome/browser/profiles/profile_downloader.h" 31 #include "chrome/browser/profiles/profile_downloader.h"
32 #include "chrome/browser/profiles/profile_manager.h" 32 #include "chrome/browser/profiles/profile_manager.h"
33 #include "chrome/common/chrome_paths.h" 33 #include "chrome/common/chrome_paths.h"
34 #include "chrome/common/chrome_switches.h" 34 #include "chrome/common/chrome_switches.h"
35 #include "chromeos/chromeos_switches.h" 35 #include "chromeos/chromeos_switches.h"
36 #include "content/public/browser/browser_thread.h" 36 #include "content/public/browser/browser_thread.h"
37 #include "content/public/browser/notification_service.h" 37 #include "content/public/browser/notification_service.h"
38 #include "content/public/common/url_constants.h"
39 #include "ui/base/webui/web_ui_util.h" 38 #include "ui/base/webui/web_ui_util.h"
40 #include "ui/gfx/image/image_skia.h" 39 #include "ui/gfx/image/image_skia.h"
41 40
42 using content::BrowserThread;
43
44 namespace chromeos { 41 namespace chromeos {
45 42
46 namespace { 43 namespace {
47 44
48 // A dictionary that maps usernames to old user image data with images stored in 45 // A dictionary that maps user_ids to old user image data with images stored in
49 // PNG format. Deprecated. 46 // PNG format. Deprecated.
50 // TODO(ivankr): remove this const char after migration is gone. 47 // TODO(ivankr): remove this const char after migration is gone.
51 const char kUserImages[] = "UserImages"; 48 const char kUserImages[] = "UserImages";
52 49
53 // A dictionary that maps usernames to user image data with images stored in 50 // A dictionary that maps user_ids to user image data with images stored in
54 // JPEG format. 51 // JPEG format.
55 const char kUserImageProperties[] = "user_image_info"; 52 const char kUserImageProperties[] = "user_image_info";
56 53
57 // Names of user image properties. 54 // Names of user image properties.
58 const char kImagePathNodeName[] = "path"; 55 const char kImagePathNodeName[] = "path";
59 const char kImageIndexNodeName[] = "index"; 56 const char kImageIndexNodeName[] = "index";
60 const char kImageURLNodeName[] = "url"; 57 const char kImageURLNodeName[] = "url";
61 58
62 // Delay betweeen user login and user image migration.
63 const int kUserImageMigrationDelaySec = 50;
64
65 // Delay betweeen user login and attempt to update user's profile data. 59 // Delay betweeen user login and attempt to update user's profile data.
66 const int kProfileDataDownloadDelaySec = 10; 60 const int kProfileDataDownloadDelaySec = 10;
67 61
68 // Interval betweeen retries to update user's profile data. 62 // Interval betweeen retries to update user's profile data.
69 const int kProfileDataDownloadRetryIntervalSec = 300; 63 const int kProfileDataDownloadRetryIntervalSec = 300;
70 64
71 // Delay betweeen subsequent profile refresh attempts (24 hrs). 65 // Delay betweeen subsequent profile refresh attempts (24 hrs).
72 const int kProfileRefreshIntervalSec = 24 * 3600; 66 const int kProfileRefreshIntervalSec = 24 * 3600;
73 67
74 const char kSafeImagePathExtension[] = ".jpg"; 68 const char kSafeImagePathExtension[] = ".jpg";
(...skipping 22 matching lines...) Expand all
97 // Time histogram prefix for a successful profile image download. 91 // Time histogram prefix for a successful profile image download.
98 const char kProfileDownloadSuccessTime[] = 92 const char kProfileDownloadSuccessTime[] =
99 "UserImage.ProfileDownloadTime.Success"; 93 "UserImage.ProfileDownloadTime.Success";
100 // Time histogram suffix for a profile image download after login. 94 // Time histogram suffix for a profile image download after login.
101 const char kProfileDownloadReasonLoggedIn[] = "LoggedIn"; 95 const char kProfileDownloadReasonLoggedIn[] = "LoggedIn";
102 // Time histogram suffix for a scheduled profile image download. 96 // Time histogram suffix for a scheduled profile image download.
103 const char kProfileDownloadReasonScheduled[] = "Scheduled"; 97 const char kProfileDownloadReasonScheduled[] = "Scheduled";
104 // Time histogram suffix for a profile image download retry. 98 // Time histogram suffix for a profile image download retry.
105 const char kProfileDownloadReasonRetry[] = "Retry"; 99 const char kProfileDownloadReasonRetry[] = "Retry";
106 100
107 // Add a histogram showing the time it takes to download a profile image. 101 // Add a histogram showing the time it takes to download profile image.
108 // Separate histograms are reported for each download |reason| and |result|. 102 // Separate histograms are reported for each download |reason| and |result|.
109 void AddProfileImageTimeHistogram(ProfileDownloadResult result, 103 void AddProfileImageTimeHistogram(ProfileDownloadResult result,
110 const std::string& download_reason, 104 const std::string& download_reason,
111 const base::TimeDelta& time_delta) { 105 const base::TimeDelta& time_delta) {
112 std::string histogram_name; 106 std::string histogram_name;
113 switch (result) { 107 switch (result) {
114 case kDownloadFailure: 108 case kDownloadFailure:
115 histogram_name = kProfileDownloadFailureTime; 109 histogram_name = kProfileDownloadFailureTime;
116 break; 110 break;
117 case kDownloadDefault: 111 case kDownloadDefault:
(...skipping 18 matching lines...) Expand all
136 const size_t bucket_count(50); 130 const size_t bucket_count(50);
137 131
138 base::HistogramBase* counter = base::Histogram::FactoryTimeGet( 132 base::HistogramBase* counter = base::Histogram::FactoryTimeGet(
139 histogram_name, min_time, max_time, bucket_count, 133 histogram_name, min_time, max_time, bucket_count,
140 base::HistogramBase::kUmaTargetedHistogramFlag); 134 base::HistogramBase::kUmaTargetedHistogramFlag);
141 counter->AddTime(time_delta); 135 counter->AddTime(time_delta);
142 136
143 DVLOG(1) << "Profile image download time: " << time_delta.InSecondsF(); 137 DVLOG(1) << "Profile image download time: " << time_delta.InSecondsF();
144 } 138 }
145 139
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. 140 // Converts |image_index| to UMA histogram value.
159 int ImageIndexToHistogramIndex(int image_index) { 141 int ImageIndexToHistogramIndex(int image_index) {
160 switch (image_index) { 142 switch (image_index) {
161 case User::kExternalImageIndex: 143 case User::kExternalImageIndex:
162 // TODO(ivankr): Distinguish this from selected from file. 144 // TODO(ivankr): Distinguish this from selected from file.
163 return kHistogramImageFromCamera; 145 return kHistogramImageFromCamera;
164 case User::kProfileImageIndex: 146 case User::kProfileImageIndex:
165 return kHistogramImageFromProfile; 147 return kHistogramImageFromProfile;
166 default: 148 default:
167 return image_index; 149 return image_index;
168 } 150 }
169 } 151 }
170 152
153 bool SaveImage(const UserImage& user_image, const base::FilePath& image_path) {
154 UserImage safe_image;
155 const UserImage::RawImage* encoded_image = NULL;
156 if (!user_image.is_safe_format()) {
157 safe_image = UserImage::CreateAndEncode(user_image.image());
158 encoded_image = &safe_image.raw_image();
159 UMA_HISTOGRAM_MEMORY_KB("UserImage.RecodedJpegSize", encoded_image->size());
160 } else if (user_image.has_raw_image()) {
161 encoded_image = &user_image.raw_image();
162 } else {
163 NOTREACHED() << "Raw image missing.";
164 return false;
165 }
166
167 if (file_util::WriteFile(image_path,
168 reinterpret_cast<const char*>(&(*encoded_image)[0]),
169 encoded_image->size()) == -1) {
170 LOG(ERROR) << "Failed to save image to file.";
171 return false;
172 }
173
174 return true;
175 }
176
171 } // namespace 177 } // namespace
172 178
173 // static 179 // static
174 int UserImageManagerImpl::user_image_migration_delay_sec =
175 kUserImageMigrationDelaySec;
176
177 // static
178 void UserImageManager::RegisterPrefs(PrefRegistrySimple* registry) { 180 void UserImageManager::RegisterPrefs(PrefRegistrySimple* registry) {
179 registry->RegisterDictionaryPref(kUserImages); 181 registry->RegisterDictionaryPref(kUserImages);
180 registry->RegisterDictionaryPref(kUserImageProperties); 182 registry->RegisterDictionaryPref(kUserImageProperties);
181 } 183 }
182 184
185 // Every image load or update is encapsulated by a Job. The Job is allowed to
186 // perform tasks on background threads or in helper processes but:
187 // * Changes to User objects and local state as well as any calls to the
188 // |parent_| must be performed on the thread that the Job is created on only.
189 // * File writes and deletions must be performed via the |parent_|'s
190 // |background_task_runner_| only.
191 //
192 // Only one of the Load*() and Set*() methods may be called per Job.
193 class UserImageManagerImpl::Job {
194 public:
195 // The |Job| will update the |user| object for |user_id|.
196 Job(UserImageManagerImpl* parent, const std::string& user_id);
197 ~Job();
198
199 // Loads the image at |image_path| or one of the default images, depending on
200 // |image_index|, and updates the |user| object for |user_id_| with the new
201 // image.
202 void LoadImage(base::FilePath image_path,
203 const int image_index,
204 const GURL& image_url);
205
206 // Sets the user image for |user_id_| in local state to the default image
207 // indicated by |default_image_index|. Also updates the |user| object for
208 // |user_id_| with the new image.
209 void SetToDefaultImage(int default_image_index);
210
211 // Saves the |user_image| to disk and sets the user image for |user_id_| in
212 // local state to that image. Also updates the |user| object for |user_id_|
213 // with the new image.
214 void SetToImage(int image_index,
215 const UserImage& user_image);
216
217 // Loads the the image at |path|, transcodes it to JPEG format, saves the
218 // image to disk and sets the user image for |user_id_| in local state to that
219 // image. If |resize| is true, the image is cropped and resized before
220 // transcoding. Also updates the |user| object for |user_id_| with the new
221 // image.
222 void SetToPath(const base::FilePath& path,
223 int image_index,
224 const GURL& image_url,
225 bool resize);
226
227 private:
228 // Called back after an image has been loaded from disk.
229 void OnLoadImageDone(bool save, const UserImage& user_image);
230
231 // Updates the |user| object for |user_id_| with |user_image_|.
232 void UpdateUser();
233
234 // Saves |user_image_| to disk in JPEG format. Local state will be updated
235 // when a callback indicates that the save has been successful.
236 void SaveImageAndUpdateLocalState();
237
238 // Called back after the |user_image_| has been saved to disk. If |success| is
239 // true sets the user image for |user_id_| in local state to that image.
240 void OnSaveImageDone(bool success);
241
242 // Updates the user image for |user_id_| in local state, setting it to
243 // one of the default images or the saved |user_image_|, depending on
244 // |image_index_|.
245 void UpdateLocalState();
246
247 // Notifies the |parent_| that the Job is done.
248 void NotifyJobDone();
249
250 UserImageManagerImpl* parent_;
251 const std::string user_id_;
252
253 // Whether one of the Load*() or Set*() methods has been run already.
254 bool run_;
255
256 int image_index_;
257 GURL image_url_;
258 base::FilePath image_path_;
259
260 UserImage user_image_;
261
262 base::WeakPtrFactory<Job> weak_factory_;
263
264 DISALLOW_COPY_AND_ASSIGN(Job);
265 };
266
267 UserImageManagerImpl::Job::Job(UserImageManagerImpl* parent,
268 const std::string& user_id)
269 : parent_(parent),
270 user_id_(user_id),
271 run_(false),
272 weak_factory_(this) {
273 }
274
275 UserImageManagerImpl::Job::~Job() {
276 }
277
278 void UserImageManagerImpl::Job::LoadImage(base::FilePath image_path,
279 const int image_index,
280 const GURL& image_url) {
281 DCHECK(!run_);
282 run_ = true;
283
284 image_index_ = image_index;
285 image_url_ = image_url;
286 image_path_ = image_path;
287
288 if (image_index_ >= 0 && image_index_ < kDefaultImagesCount) {
289 // Load one of the default images. This happens synchronously.
290 user_image_ = UserImage(GetDefaultImage(image_index_));
291 UpdateUser();
292 NotifyJobDone();
293 } else if (image_index_ == User::kExternalImageIndex ||
294 image_index_ == User::kProfileImageIndex) {
295 // Load the user image from a file referenced by |image_path|. This happens
296 // asynchronously. The JPEG image loader can be used here because
297 // LoadImage() is called only for users whose user image has previously
298 // been set by one of the Set*() methods, which transcode to JPEG format.
299 DCHECK(!image_path_.empty());
300 parent_->image_loader_->Start(image_path_.value(),
301 0,
302 base::Bind(&Job::OnLoadImageDone,
303 weak_factory_.GetWeakPtr(),
304 false));
305 } else {
306 NOTREACHED();
307 NotifyJobDone();
308 }
309 }
310
311 void UserImageManagerImpl::Job::SetToDefaultImage(int default_image_index) {
312 DCHECK(!run_);
313 run_ = true;
314
315 DCHECK_LE(0, default_image_index);
316 DCHECK_GT(kDefaultImagesCount, default_image_index);
317
318 image_index_ = default_image_index;
319 user_image_ = UserImage(GetDefaultImage(image_index_));
320
321 UpdateUser();
322 UpdateLocalState();
323 NotifyJobDone();
324 }
325
326 void UserImageManagerImpl::Job::SetToImage(int image_index,
327 const UserImage& user_image) {
328 DCHECK(!run_);
329 run_ = true;
330
331 DCHECK(image_index == User::kExternalImageIndex ||
332 image_index == User::kProfileImageIndex);
333
334 image_index_ = image_index;
335 user_image_ = user_image;
336
337 UpdateUser();
338 SaveImageAndUpdateLocalState();
339 }
340
341 void UserImageManagerImpl::Job::SetToPath(const base::FilePath& path,
342 int image_index,
343 const GURL& image_url,
344 bool resize) {
345 DCHECK(!run_);
346 run_ = true;
347
348 image_index_ = image_index;
349 image_url_ = image_url;
350
351 DCHECK(!path.empty());
352 parent_->unsafe_image_loader_->Start(path.value(),
353 resize ? login::kMaxUserImageSize : 0,
354 base::Bind(&Job::OnLoadImageDone,
355 weak_factory_.GetWeakPtr(),
356 true));
357 }
358
359 void UserImageManagerImpl::Job::OnLoadImageDone(bool save,
360 const UserImage& user_image) {
361 user_image_ = user_image;
362 UpdateUser();
363 if (save)
364 SaveImageAndUpdateLocalState();
365 else
366 NotifyJobDone();
367 }
368
369 void UserImageManagerImpl::Job::UpdateUser() {
370 User* user = UserManager::Get()->FindUserAndModify(user_id_);
371 if (!user)
372 return;
373
374 if (!user_image_.image().isNull())
375 user->SetImage(user_image_, image_index_);
376 else
377 user->SetStubImage(image_index_, false);
378 user->SetImageURL(image_url_);
379
380 parent_->OnJobChangedUserImage(user);
381 }
382
383 void UserImageManagerImpl::Job::SaveImageAndUpdateLocalState() {
384 base::FilePath user_data_dir;
385 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
386 image_path_ = user_data_dir.Append(user_id_ + kSafeImagePathExtension);
387
388 base::PostTaskAndReplyWithResult(
389 parent_->background_task_runner_,
390 FROM_HERE,
391 base::Bind(&SaveImage, user_image_, image_path_),
392 base::Bind(&Job::OnSaveImageDone, weak_factory_.GetWeakPtr()));
393 }
394
395 void UserImageManagerImpl::Job::OnSaveImageDone(bool success) {
396 if (success)
397 UpdateLocalState();
398 NotifyJobDone();
399 }
400
401 void UserImageManagerImpl::Job::UpdateLocalState() {
402 // Ignore if data stored or cached outside the user's cryptohome is to be
403 // treated as ephemeral.
404 if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(user_id_))
405 return;
406
407 scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue);
408 entry->Set(kImagePathNodeName, new base::StringValue(image_path_.value()));
409 entry->Set(kImageIndexNodeName, new base::FundamentalValue(image_index_));
410 if (!image_url_.is_empty())
411 entry->Set(kImageURLNodeName, new StringValue(image_url_.spec()));
412 DictionaryPrefUpdate update(g_browser_process->local_state(),
413 kUserImageProperties);
414 update->SetWithoutPathExpansion(user_id_, entry.release());
415
416 UserManager::Get()->NotifyLocalStateChanged();
417 }
418
419 void UserImageManagerImpl::Job::NotifyJobDone() {
420 parent_->OnJobDone(user_id_);
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,
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 base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE,
875 it->second.release());
876 jobs_.erase(it);
877 } else {
878 NOTREACHED();
879 }
880
881 if (users_to_migrate_.find(user_id) == users_to_migrate_.end())
882 return;
883 // Migration completed for |user_id|.
884 users_to_migrate_.erase(user_id);
885
886 const DictionaryValue* prefs_images_unsafe =
887 g_browser_process->local_state()->GetDictionary(kUserImages);
888 const base::DictionaryValue* image_properties = NULL;
889 if (!prefs_images_unsafe->GetDictionaryWithoutPathExpansion(
890 user_id, &image_properties)) {
891 NOTREACHED();
786 return; 892 return;
787 } 893 }
788 migrate_current_user_on_load_ = false; 894
789 if (user->has_raw_image() && user->image_is_safe_format()) { 895 int image_index = User::kInvalidImageIndex;
790 // Nothing to migrate already, make sure we delete old image. 896 image_properties->GetInteger(kImageIndexNodeName, &image_index);
791 DeleteOldUserImage(user->email()); 897 UMA_HISTOGRAM_ENUMERATION("UserImage.Migration",
792 users_to_migrate_.erase(user->email()); 898 ImageIndexToHistogramIndex(image_index),
793 return; 899 kHistogramImagesCount);
794 } 900
795 if (user->HasDefaultImage()) { 901 std::string image_path;
796 SaveUserDefaultImageIndex(user->email(), user->image_index()); 902 image_properties->GetString(kImagePathNodeName, &image_path);
903 if (!image_path.empty()) {
904 // If an old image exists, delete it and remove |user_id| from the old prefs
905 // dictionary only after the deletion has completed. This ensures that no
906 // orphaned image is left behind if the browser crashes before the deletion
907 // has been performed: In that case, local state will be unchanged and the
908 // migration will be run again on the user's next login.
909 background_task_runner_->PostTaskAndReply(
910 FROM_HERE,
911 base::Bind(base::IgnoreResult(&base::DeleteFile),
912 base::FilePath(image_path),
913 false),
914 base::Bind(&UserImageManagerImpl::UpdateLocalStateAfterMigration,
915 weak_factory_.GetWeakPtr(),
916 user_id));
797 } else { 917 } else {
798 SaveUserImageInternal(user->email(), user->image_index(), 918 // If no old image exists, remove |user_id| from the old prefs dictionary.
799 user->image_url(), user->user_image()); 919 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 } 920 }
817 } 921 }
818 922
923 void UserImageManagerImpl::UpdateLocalStateAfterMigration(
924 const std::string& user_id) {
925 DictionaryPrefUpdate update(g_browser_process->local_state(),
926 kUserImages);
927 update->RemoveWithoutPathExpansion(user_id, NULL);
928 }
929
819 } // namespace chromeos 930 } // namespace chromeos
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/login/user_image_manager_impl.h ('k') | chrome/browser/chromeos/login/user_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698