1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/login/wallpaper_manager.h"
6
7 #include <vector>
8
9 #include "ash/shell.h"
10 #include "base/command_line.h"
11 #include "base/debug/trace_event.h"
12 #include "base/file_util.h"
13 #include "base/files/file_enumerator.h"
14 #include "base/files/file_path.h"
15 #include "base/logging.h"
16 #include "base/metrics/histogram.h"
17 #include "base/path_service.h"
18 #include "base/prefs/pref_registry_simple.h"
19 #include "base/prefs/pref_service.h"
20 #include "base/prefs/scoped_user_pref_update.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/threading/worker_pool.h"
25 #include "base/time/time.h"
26 #include "base/values.h"
27 #include "chrome/browser/browser_process.h"
28 #include "chrome/browser/chrome_notification_types.h"
29 #include "chrome/browser/chromeos/login/startup_utils.h"
30 #include "chrome/browser/chromeos/login/user.h"
31 #include "chrome/browser/chromeos/login/user_manager.h"
32 #include "chrome/browser/chromeos/login/wizard_controller.h"
33 #include "chrome/browser/chromeos/settings/cros_settings.h"
34 #include "chrome/common/chrome_paths.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/common/pref_names.h"
37 #include "chromeos/chromeos_switches.h"
38 #include "chromeos/dbus/dbus_thread_manager.h"
39 #include "content/public/browser/browser_thread.h"
40 #include "content/public/browser/notification_service.h"
41 #include "ui/base/resource/resource_bundle.h"
42 #include "ui/gfx/codec/jpeg_codec.h"
43 #include "ui/gfx/image/image_skia_operations.h"
44 #include "ui/gfx/skia_util.h"
45
46 using content::BrowserThread;
47
48 namespace {
49
50 // The amount of delay before starts to move custom wallpapers to the new place.
51 const int kMoveCustomWallpaperDelaySeconds = 30;
52
53 // Default quality for encoding wallpaper.
54 const int kDefaultEncodingQuality = 90;
55
56 // A dictionary pref that maps usernames to file paths to their wallpapers.
57 // Deprecated. Will remove this const char after done migration.
58 const char kUserWallpapers[] = "UserWallpapers";
59
60 const int kCacheWallpaperDelayMs = 500;
61
62 // A dictionary pref that maps usernames to wallpaper properties.
63 const char kUserWallpapersProperties[] = "UserWallpapersProperties";
64
65 // Names of nodes with info about wallpaper in |kUserWallpapersProperties|
66 // dictionary.
67 const char kNewWallpaperDateNodeName[] = "date";
68 const char kNewWallpaperLayoutNodeName[] = "layout";
69 const char kNewWallpaperFileNodeName[] = "file";
70 const char kNewWallpaperTypeNodeName[] = "type";
71
72 // File path suffix of the original custom wallpaper.
73 const char kOriginalCustomWallpaperSuffix[] = "_wallpaper";
74
75 // Maximum number of wallpapers cached by CacheUsersWallpapers().
76 const int kMaxWallpapersToCache = 3;
77
78 // For our scaling ratios we need to round positive numbers.
RoundPositive(double x)79 int RoundPositive(double x) {
80 return static_cast<int>(floor(x + 0.5));
81 }
82
83 // Returns custom wallpaper directory by appending corresponding |sub_dir|.
GetCustomWallpaperDir(const char * sub_dir)84 base::FilePath GetCustomWallpaperDir(const char* sub_dir) {
85 base::FilePath custom_wallpaper_dir;
86 CHECK(PathService::Get(chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS,
87 &custom_wallpaper_dir));
88 return custom_wallpaper_dir.Append(sub_dir);
89 }
90
MoveCustomWallpaperDirectory(const char * sub_dir,const std::string & email,const std::string & user_id_hash)91 bool MoveCustomWallpaperDirectory(const char* sub_dir,
92 const std::string& email,
93 const std::string& user_id_hash) {
94 base::FilePath base_path = GetCustomWallpaperDir(sub_dir);
95 base::FilePath to_path = base_path.Append(user_id_hash);
96 base::FilePath from_path = base_path.Append(email);
97 if (base::PathExists(from_path))
98 return base::Move(from_path, to_path);
99 return false;
100 }
101
102 } // namespace
103
104 namespace chromeos {
105
106 const char kWallpaperSequenceTokenName[] = "wallpaper-sequence";
107
108 const char kSmallWallpaperSuffix[] = "_small";
109 const char kLargeWallpaperSuffix[] = "_large";
110
111 const char kSmallWallpaperSubDir[] = "small";
112 const char kLargeWallpaperSubDir[] = "large";
113 const char kOriginalWallpaperSubDir[] = "original";
114 const char kThumbnailWallpaperSubDir[] = "thumb";
115
116 static WallpaperManager* g_wallpaper_manager = NULL;
117
118 // WallpaperManager, public: ---------------------------------------------------
119
120 // TestApi. For testing purpose
TestApi(WallpaperManager * wallpaper_manager)121 WallpaperManager::TestApi::TestApi(WallpaperManager* wallpaper_manager)
122 : wallpaper_manager_(wallpaper_manager) {
123 }
124
~TestApi()125 WallpaperManager::TestApi::~TestApi() {
126 }
127
current_wallpaper_path()128 base::FilePath WallpaperManager::TestApi::current_wallpaper_path() {
129 return wallpaper_manager_->current_wallpaper_path_;
130 }
131
132 // static
Get()133 WallpaperManager* WallpaperManager::Get() {
134 if (!g_wallpaper_manager)
135 g_wallpaper_manager = new WallpaperManager();
136 return g_wallpaper_manager;
137 }
138
WallpaperManager()139 WallpaperManager::WallpaperManager()
140 : loaded_wallpapers_(0),
141 command_line_for_testing_(NULL),
142 should_cache_wallpaper_(false),
143 weak_factory_(this) {
144 registrar_.Add(this,
145 chrome::NOTIFICATION_LOGIN_USER_CHANGED,
146 content::NotificationService::AllSources());
147 registrar_.Add(this,
148 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
149 content::NotificationService::AllSources());
150 registrar_.Add(this,
151 chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED,
152 content::NotificationService::AllSources());
153 sequence_token_ = BrowserThread::GetBlockingPool()->
154 GetNamedSequenceToken(kWallpaperSequenceTokenName);
155 task_runner_ = BrowserThread::GetBlockingPool()->
156 GetSequencedTaskRunnerWithShutdownBehavior(
157 sequence_token_,
158 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
159 wallpaper_loader_ = new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC,
160 task_runner_);
161 }
162
~WallpaperManager()163 WallpaperManager::~WallpaperManager() {
164 // TODO(bshe): Lifetime of WallpaperManager needs more consideration.
165 // http://crbug.com/171694
166 DCHECK(!show_user_name_on_signin_subscription_);
167 ClearObsoleteWallpaperPrefs();
168 weak_factory_.InvalidateWeakPtrs();
169 }
170
Shutdown()171 void WallpaperManager::Shutdown() {
172 show_user_name_on_signin_subscription_.reset();
173 }
174
175 // static
RegisterPrefs(PrefRegistrySimple * registry)176 void WallpaperManager::RegisterPrefs(PrefRegistrySimple* registry) {
177 registry->RegisterDictionaryPref(prefs::kUsersWallpaperInfo);
178 registry->RegisterDictionaryPref(kUserWallpapers);
179 registry->RegisterDictionaryPref(kUserWallpapersProperties);
180 }
181
AddObservers()182 void WallpaperManager::AddObservers() {
183 show_user_name_on_signin_subscription_ =
184 CrosSettings::Get()->AddSettingsObserver(
185 kAccountsPrefShowUserNamesOnSignIn,
186 base::Bind(&WallpaperManager::InitializeRegisteredDeviceWallpaper,
187 base::Unretained(this)));
188 }
189
EnsureLoggedInUserWallpaperLoaded()190 void WallpaperManager::EnsureLoggedInUserWallpaperLoaded() {
191 // Some browser tests do not have a shell instance. As no wallpaper is needed
192 // in these tests anyway, avoid loading one, preventing crashes and speeding
193 // up the tests.
194 if (!ash::Shell::HasInstance())
195 return;
196
197 WallpaperInfo info;
198 if (GetLoggedInUserWallpaperInfo(&info)) {
199 // TODO(sschmitz): We need an index for default wallpapers for the new UI.
200 RecordUma(info.type, -1);
201 if (info == current_user_wallpaper_info_)
202 return;
203 }
204 SetUserWallpaper(UserManager::Get()->GetLoggedInUser()->email());
205 }
206
ClearWallpaperCache()207 void WallpaperManager::ClearWallpaperCache() {
208 // Cancel callback for previous cache requests.
209 weak_factory_.InvalidateWeakPtrs();
210 wallpaper_cache_.clear();
211 }
212
GetCustomWallpaperPath(const char * sub_dir,const std::string & user_id_hash,const std::string & file)213 base::FilePath WallpaperManager::GetCustomWallpaperPath(
214 const char* sub_dir,
215 const std::string& user_id_hash,
216 const std::string& file) {
217 base::FilePath custom_wallpaper_path = GetCustomWallpaperDir(sub_dir);
218 return custom_wallpaper_path.Append(user_id_hash).Append(file);
219 }
220
GetWallpaperFromCache(const std::string & email,gfx::ImageSkia * wallpaper)221 bool WallpaperManager::GetWallpaperFromCache(const std::string& email,
222 gfx::ImageSkia* wallpaper) {
223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
224 CustomWallpaperMap::const_iterator it = wallpaper_cache_.find(email);
225 if (it != wallpaper_cache_.end()) {
226 *wallpaper = (*it).second;
227 return true;
228 }
229 return false;
230 }
231
GetOriginalWallpaperPathForUser(const std::string & username)232 base::FilePath WallpaperManager::GetOriginalWallpaperPathForUser(
233 const std::string& username) {
234 std::string filename = username + kOriginalCustomWallpaperSuffix;
235 base::FilePath user_data_dir;
236 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
237 return user_data_dir.AppendASCII(filename);
238 }
239
GetLoggedInUserWallpaperInfo(WallpaperInfo * info)240 bool WallpaperManager::GetLoggedInUserWallpaperInfo(WallpaperInfo* info) {
241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
242
243 if (UserManager::Get()->IsLoggedInAsStub()) {
244 info->file = current_user_wallpaper_info_.file = "";
245 info->layout = current_user_wallpaper_info_.layout =
246 ash::WALLPAPER_LAYOUT_CENTER_CROPPED;
247 info->type = current_user_wallpaper_info_.type = User::DEFAULT;
248 return true;
249 }
250
251 return GetUserWallpaperInfo(UserManager::Get()->GetLoggedInUser()->email(),
252 info);
253 }
254
InitializeWallpaper()255 void WallpaperManager::InitializeWallpaper() {
256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
257 UserManager* user_manager = UserManager::Get();
258
259 CommandLine* command_line = GetComandLine();
260 if (command_line->HasSwitch(chromeos::switches::kGuestSession)) {
261 // Guest wallpaper should be initialized when guest login.
262 // Note: This maybe called before login. So IsLoggedInAsGuest can not be
263 // used here to determine if current user is guest.
264 return;
265 }
266
267 if (command_line->HasSwitch(::switches::kTestType))
268 WizardController::SetZeroDelays();
269
270 // Zero delays is also set in autotests.
271 if (WizardController::IsZeroDelayEnabled()) {
272 // Ensure tests have some sort of wallpaper.
273 ash::Shell::GetInstance()->desktop_background_controller()->
274 CreateEmptyWallpaper();
275 return;
276 }
277
278 if (!user_manager->IsUserLoggedIn()) {
279 if (!StartupUtils::IsDeviceRegistered())
280 SetDefaultWallpaper();
281 else
282 InitializeRegisteredDeviceWallpaper();
283 return;
284 }
285 SetUserWallpaper(user_manager->GetLoggedInUser()->email());
286 }
287
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)288 void WallpaperManager::Observe(int type,
289 const content::NotificationSource& source,
290 const content::NotificationDetails& details) {
291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
292 switch (type) {
293 case chrome::NOTIFICATION_LOGIN_USER_CHANGED: {
294 ClearWallpaperCache();
295 BrowserThread::PostDelayedTask(
296 BrowserThread::UI,
297 FROM_HERE,
298 base::Bind(&WallpaperManager::MoveLoggedInUserCustomWallpaper,
299 weak_factory_.GetWeakPtr()),
300 base::TimeDelta::FromSeconds(kMoveCustomWallpaperDelaySeconds));
301 break;
302 }
303 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: {
304 if (!GetComandLine()->HasSwitch(switches::kDisableBootAnimation)) {
305 BrowserThread::PostDelayedTask(
306 BrowserThread::UI, FROM_HERE,
307 base::Bind(&WallpaperManager::CacheUsersWallpapers,
308 weak_factory_.GetWeakPtr()),
309 base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs));
310 } else {
311 should_cache_wallpaper_ = true;
312 }
313 break;
314 }
315 case chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED: {
316 NotifyAnimationFinished();
317 if (should_cache_wallpaper_) {
318 BrowserThread::PostDelayedTask(
319 BrowserThread::UI, FROM_HERE,
320 base::Bind(&WallpaperManager::CacheUsersWallpapers,
321 weak_factory_.GetWeakPtr()),
322 base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs));
323 should_cache_wallpaper_ = false;
324 }
325 break;
326 }
327 default:
328 NOTREACHED() << "Unexpected notification " << type;
329 }
330 }
331
RemoveUserWallpaperInfo(const std::string & email)332 void WallpaperManager::RemoveUserWallpaperInfo(const std::string& email) {
333 WallpaperInfo info;
334 GetUserWallpaperInfo(email, &info);
335 PrefService* prefs = g_browser_process->local_state();
336 DictionaryPrefUpdate prefs_wallpapers_info_update(prefs,
337 prefs::kUsersWallpaperInfo);
338 prefs_wallpapers_info_update->RemoveWithoutPathExpansion(email, NULL);
339 DeleteUserWallpapers(email, info.file);
340 }
341
ResizeWallpaper(const UserImage & wallpaper,ash::WallpaperLayout layout,int preferred_width,int preferred_height,scoped_refptr<base::RefCountedBytes> * output)342 bool WallpaperManager::ResizeWallpaper(
343 const UserImage& wallpaper,
344 ash::WallpaperLayout layout,
345 int preferred_width,
346 int preferred_height,
347 scoped_refptr<base::RefCountedBytes>* output) {
348 DCHECK(BrowserThread::GetBlockingPool()->
349 IsRunningSequenceOnCurrentThread(sequence_token_));
350 int width = wallpaper.image().width();
351 int height = wallpaper.image().height();
352 int resized_width;
353 int resized_height;
354 *output = new base::RefCountedBytes();
355
356 if (layout == ash::WALLPAPER_LAYOUT_CENTER_CROPPED) {
357 // Do not resize custom wallpaper if it is smaller than preferred size.
358 if (!(width > preferred_width && height > preferred_height))
359 return false;
360
361 double horizontal_ratio = static_cast<double>(preferred_width) / width;
362 double vertical_ratio = static_cast<double>(preferred_height) / height;
363 if (vertical_ratio > horizontal_ratio) {
364 resized_width =
365 RoundPositive(static_cast<double>(width) * vertical_ratio);
366 resized_height = preferred_height;
367 } else {
368 resized_width = preferred_width;
369 resized_height =
370 RoundPositive(static_cast<double>(height) * horizontal_ratio);
371 }
372 } else if (layout == ash::WALLPAPER_LAYOUT_STRETCH) {
373 resized_width = preferred_width;
374 resized_height = preferred_height;
375 } else {
376 resized_width = width;
377 resized_height = height;
378 }
379
380 gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage(
381 wallpaper.image(),
382 skia::ImageOperations::RESIZE_LANCZOS3,
383 gfx::Size(resized_width, resized_height));
384
385 SkBitmap image = *(resized_image.bitmap());
386 SkAutoLockPixels lock_input(image);
387 gfx::JPEGCodec::Encode(
388 reinterpret_cast<unsigned char*>(image.getAddr32(0, 0)),
389 gfx::JPEGCodec::FORMAT_SkBitmap,
390 image.width(),
391 image.height(),
392 image.width() * image.bytesPerPixel(),
393 kDefaultEncodingQuality, &(*output)->data());
394 return true;
395 }
396
ResizeAndSaveWallpaper(const UserImage & wallpaper,const base::FilePath & path,ash::WallpaperLayout layout,int preferred_width,int preferred_height)397 void WallpaperManager::ResizeAndSaveWallpaper(const UserImage& wallpaper,
398 const base::FilePath& path,
399 ash::WallpaperLayout layout,
400 int preferred_width,
401 int preferred_height) {
402 if (layout == ash::WALLPAPER_LAYOUT_CENTER) {
403 // TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
404 if (base::PathExists(path))
405 base::DeleteFile(path, false);
406 return;
407 }
408 scoped_refptr<base::RefCountedBytes> data;
409 if (ResizeWallpaper(wallpaper, layout, preferred_width, preferred_height,
410 &data)) {
411 SaveWallpaperInternal(path,
412 reinterpret_cast<const char*>(data->front()),
413 data->size());
414 }
415 }
416
SetCustomWallpaper(const std::string & username,const std::string & user_id_hash,const std::string & file,ash::WallpaperLayout layout,User::WallpaperType type,const UserImage & wallpaper)417 void WallpaperManager::SetCustomWallpaper(const std::string& username,
418 const std::string& user_id_hash,
419 const std::string& file,
420 ash::WallpaperLayout layout,
421 User::WallpaperType type,
422 const UserImage& wallpaper) {
423 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
424
425 base::FilePath wallpaper_path =
426 GetCustomWallpaperPath(kOriginalWallpaperSubDir, user_id_hash, file);
427
428 // If decoded wallpaper is empty, we are probably failed to decode the file.
429 // Use default wallpaper in this case.
430 if (wallpaper.image().isNull()) {
431 SetDefaultWallpaper();
432 return;
433 }
434
435 bool is_persistent =
436 !UserManager::Get()->IsUserNonCryptohomeDataEphemeral(username);
437
438 wallpaper.image().EnsureRepsForSupportedScales();
439 scoped_ptr<gfx::ImageSkia> deep_copy(wallpaper.image().DeepCopy());
440
441 WallpaperInfo wallpaper_info = {
442 wallpaper_path.value(),
443 layout,
444 type,
445 // Date field is not used.
446 base::Time::Now().LocalMidnight()
447 };
448 // Block shutdown on this task. Otherwise, we may lost the custom wallpaper
449 // user selected.
450 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner =
451 BrowserThread::GetBlockingPool()->
452 GetSequencedTaskRunnerWithShutdownBehavior(sequence_token_,
453 base::SequencedWorkerPool::BLOCK_SHUTDOWN);
454 // TODO(bshe): This may break if RawImage becomes RefCountedMemory.
455 blocking_task_runner->PostTask(FROM_HERE,
456 base::Bind(&WallpaperManager::ProcessCustomWallpaper,
457 base::Unretained(this),
458 user_id_hash,
459 is_persistent,
460 wallpaper_info,
461 base::Passed(&deep_copy),
462 wallpaper.raw_image()));
463 ash::Shell::GetInstance()->desktop_background_controller()->
464 SetCustomWallpaper(wallpaper.image(), layout);
465
466 std::string relative_path = base::FilePath(user_id_hash).Append(file).value();
467 // User's custom wallpaper path is determined by relative path and the
468 // appropriate wallpaper resolution in GetCustomWallpaperInternal.
469 WallpaperInfo info = {
470 relative_path,
471 layout,
472 User::CUSTOMIZED,
473 base::Time::Now().LocalMidnight()
474 };
475 SetUserWallpaperInfo(username, info, is_persistent);
476 }
477
SetDefaultWallpaper()478 void WallpaperManager::SetDefaultWallpaper() {
479 current_wallpaper_path_.clear();
480 if (ash::Shell::GetInstance()->desktop_background_controller()->
481 SetDefaultWallpaper(UserManager::Get()->IsLoggedInAsGuest()))
482 loaded_wallpapers_++;
483 }
484
SetInitialUserWallpaper(const std::string & username,bool is_persistent)485 void WallpaperManager::SetInitialUserWallpaper(const std::string& username,
486 bool is_persistent) {
487 current_user_wallpaper_info_.file = "";
488 current_user_wallpaper_info_.layout = ash::WALLPAPER_LAYOUT_CENTER_CROPPED;
489 current_user_wallpaper_info_.type = User::DEFAULT;
490 current_user_wallpaper_info_.date = base::Time::Now().LocalMidnight();
491
492 WallpaperInfo info = current_user_wallpaper_info_;
493 SetUserWallpaperInfo(username, info, is_persistent);
494 SetLastSelectedUser(username);
495
496 // Some browser tests do not have a shell instance. As no wallpaper is needed
497 // in these tests anyway, avoid loading one, preventing crashes and speeding
498 // up the tests.
499 if (ash::Shell::HasInstance())
500 SetDefaultWallpaper();
501 }
502
SetUserWallpaperInfo(const std::string & username,const WallpaperInfo & info,bool is_persistent)503 void WallpaperManager::SetUserWallpaperInfo(const std::string& username,
504 const WallpaperInfo& info,
505 bool is_persistent) {
506 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
507 current_user_wallpaper_info_ = info;
508 if (!is_persistent)
509 return;
510
511 PrefService* local_state = g_browser_process->local_state();
512 DictionaryPrefUpdate wallpaper_update(local_state,
513 prefs::kUsersWallpaperInfo);
514
515 base::DictionaryValue* wallpaper_info_dict = new base::DictionaryValue();
516 wallpaper_info_dict->SetString(kNewWallpaperDateNodeName,
517 base::Int64ToString(info.date.ToInternalValue()));
518 wallpaper_info_dict->SetString(kNewWallpaperFileNodeName, info.file);
519 wallpaper_info_dict->SetInteger(kNewWallpaperLayoutNodeName, info.layout);
520 wallpaper_info_dict->SetInteger(kNewWallpaperTypeNodeName, info.type);
521 wallpaper_update->SetWithoutPathExpansion(username, wallpaper_info_dict);
522 }
523
SetLastSelectedUser(const std::string & last_selected_user)524 void WallpaperManager::SetLastSelectedUser(
525 const std::string& last_selected_user) {
526 last_selected_user_ = last_selected_user;
527 }
528
SetUserWallpaper(const std::string & email)529 void WallpaperManager::SetUserWallpaper(const std::string& email) {
530 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
531 if (email == UserManager::kGuestUserName) {
532 SetDefaultWallpaper();
533 return;
534 }
535
536 if (!UserManager::Get()->IsKnownUser(email))
537 return;
538
539 SetLastSelectedUser(email);
540
541 WallpaperInfo info;
542
543 if (GetUserWallpaperInfo(email, &info)) {
544 gfx::ImageSkia user_wallpaper;
545 current_user_wallpaper_info_ = info;
546 if (GetWallpaperFromCache(email, &user_wallpaper)) {
547 ash::Shell::GetInstance()->desktop_background_controller()->
548 SetCustomWallpaper(user_wallpaper, info.layout);
549 } else {
550 if (info.type == User::CUSTOMIZED) {
551 ash::WallpaperResolution resolution = ash::Shell::GetInstance()->
552 desktop_background_controller()->GetAppropriateResolution();
553 const char* sub_dir = (resolution == ash::WALLPAPER_RESOLUTION_SMALL) ?
554 kSmallWallpaperSubDir : kLargeWallpaperSubDir;
555 // Wallpaper is not resized when layout is ash::WALLPAPER_LAYOUT_CENTER.
556 // Original wallpaper should be used in this case.
557 // TODO(bshe): Generates cropped custom wallpaper for CENTER layout.
558 if (info.layout == ash::WALLPAPER_LAYOUT_CENTER)
559 sub_dir = kOriginalWallpaperSubDir;
560 base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir);
561 wallpaper_path = wallpaper_path.Append(info.file);
562 if (current_wallpaper_path_ == wallpaper_path)
563 return;
564 current_wallpaper_path_ = wallpaper_path;
565 loaded_wallpapers_++;
566
567 task_runner_->PostTask(FROM_HERE,
568 base::Bind(&WallpaperManager::GetCustomWallpaperInternal,
569 base::Unretained(this), email, info, wallpaper_path,
570 true /* update wallpaper */));
571 return;
572 }
573
574 if (info.file.empty()) {
575 // Uses default built-in wallpaper when file is empty. Eventually, we
576 // will only ship one built-in wallpaper in ChromeOS image.
577 SetDefaultWallpaper();
578 return;
579 }
580
581 // Load downloaded ONLINE or converted DEFAULT wallpapers.
582 LoadWallpaper(email, info, true /* update wallpaper */);
583 }
584 } else {
585 SetInitialUserWallpaper(email, true);
586 }
587 }
588
SetWallpaperFromImageSkia(const gfx::ImageSkia & wallpaper,ash::WallpaperLayout layout)589 void WallpaperManager::SetWallpaperFromImageSkia(
590 const gfx::ImageSkia& wallpaper,
591 ash::WallpaperLayout layout) {
592 ash::Shell::GetInstance()->desktop_background_controller()->
593 SetCustomWallpaper(wallpaper, layout);
594 }
595
UpdateWallpaper()596 void WallpaperManager::UpdateWallpaper() {
597 ClearWallpaperCache();
598 current_wallpaper_path_.clear();
599 // For GAIA login flow, the last_selected_user_ may not be set before user
600 // login. If UpdateWallpaper is called at GAIA login screen, no wallpaper will
601 // be set. It could result a black screen on external monitors.
602 // See http://crbug.com/265689 for detail.
603 if (last_selected_user_.empty()) {
604 SetDefaultWallpaper();
605 return;
606 }
607 SetUserWallpaper(last_selected_user_);
608 }
609
AddObserver(WallpaperManager::Observer * observer)610 void WallpaperManager::AddObserver(WallpaperManager::Observer* observer) {
611 observers_.AddObserver(observer);
612 }
613
RemoveObserver(WallpaperManager::Observer * observer)614 void WallpaperManager::RemoveObserver(WallpaperManager::Observer* observer) {
615 observers_.RemoveObserver(observer);
616 }
617
NotifyAnimationFinished()618 void WallpaperManager::NotifyAnimationFinished() {
619 FOR_EACH_OBSERVER(
620 Observer, observers_, OnWallpaperAnimationFinished(last_selected_user_));
621 }
622
623 // WallpaperManager, private: --------------------------------------------------
624
CacheUsersWallpapers()625 void WallpaperManager::CacheUsersWallpapers() {
626 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
627 UserList users = UserManager::Get()->GetUsers();
628
629 if (!users.empty()) {
630 UserList::const_iterator it = users.begin();
631 // Skip the wallpaper of first user in the list. It should have been cached.
632 it++;
633 for (int cached = 0;
634 it != users.end() && cached < kMaxWallpapersToCache;
635 ++it, ++cached) {
636 std::string user_email = (*it)->email();
637 CacheUserWallpaper(user_email);
638 }
639 }
640 }
641
CacheUserWallpaper(const std::string & email)642 void WallpaperManager::CacheUserWallpaper(const std::string& email) {
643 if (wallpaper_cache_.find(email) == wallpaper_cache_.end())
644 return;
645 WallpaperInfo info;
646 if (GetUserWallpaperInfo(email, &info)) {
647 base::FilePath wallpaper_dir;
648 base::FilePath wallpaper_path;
649 if (info.type == User::CUSTOMIZED) {
650 ash::WallpaperResolution resolution = ash::Shell::GetInstance()->
651 desktop_background_controller()->GetAppropriateResolution();
652 const char* sub_dir = (resolution == ash::WALLPAPER_RESOLUTION_SMALL) ?
653 kSmallWallpaperSubDir : kLargeWallpaperSubDir;
654 base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir);
655 wallpaper_path = wallpaper_path.Append(info.file);
656 task_runner_->PostTask(FROM_HERE,
657 base::Bind(&WallpaperManager::GetCustomWallpaperInternal,
658 base::Unretained(this), email, info, wallpaper_path,
659 false /* do not update wallpaper */));
660 return;
661 }
662 LoadWallpaper(email, info, false /* do not update wallpaper */);
663 }
664 }
665
ClearObsoleteWallpaperPrefs()666 void WallpaperManager::ClearObsoleteWallpaperPrefs() {
667 PrefService* prefs = g_browser_process->local_state();
668 DictionaryPrefUpdate wallpaper_properties_pref(prefs,
669 kUserWallpapersProperties);
670 wallpaper_properties_pref->Clear();
671 DictionaryPrefUpdate wallpapers_pref(prefs, kUserWallpapers);
672 wallpapers_pref->Clear();
673 }
674
DeleteAllExcept(const base::FilePath & path)675 void WallpaperManager::DeleteAllExcept(const base::FilePath& path) {
676 base::FilePath dir = path.DirName();
677 if (base::DirectoryExists(dir)) {
678 base::FileEnumerator files(dir, false, base::FileEnumerator::FILES);
679 for (base::FilePath current = files.Next(); !current.empty();
680 current = files.Next()) {
681 if (current != path)
682 base::DeleteFile(current, false);
683 }
684 }
685 }
686
DeleteWallpaperInList(const std::vector<base::FilePath> & file_list)687 void WallpaperManager::DeleteWallpaperInList(
688 const std::vector<base::FilePath>& file_list) {
689 for (std::vector<base::FilePath>::const_iterator it = file_list.begin();
690 it != file_list.end(); ++it) {
691 base::FilePath path = *it;
692 // Some users may still have legacy wallpapers with png extension. We need
693 // to delete these wallpapers too.
694 if (!base::DeleteFile(path, true) &&
695 !base::DeleteFile(path.AddExtension(".png"), false)) {
696 LOG(ERROR) << "Failed to remove user wallpaper at " << path.value();
697 }
698 }
699 }
700
DeleteUserWallpapers(const std::string & email,const std::string & path_to_file)701 void WallpaperManager::DeleteUserWallpapers(const std::string& email,
702 const std::string& path_to_file) {
703 std::vector<base::FilePath> file_to_remove;
704 // Remove small user wallpaper.
705 base::FilePath wallpaper_path =
706 GetCustomWallpaperDir(kSmallWallpaperSubDir);
707 // Remove old directory if exists
708 file_to_remove.push_back(wallpaper_path.Append(email));
709 wallpaper_path = wallpaper_path.Append(path_to_file).DirName();
710 file_to_remove.push_back(wallpaper_path);
711
712 // Remove large user wallpaper.
713 wallpaper_path = GetCustomWallpaperDir(kLargeWallpaperSubDir);
714 file_to_remove.push_back(wallpaper_path.Append(email));
715 wallpaper_path = wallpaper_path.Append(path_to_file);
716 file_to_remove.push_back(wallpaper_path);
717
718 // Remove user wallpaper thumbnail.
719 wallpaper_path = GetCustomWallpaperDir(kThumbnailWallpaperSubDir);
720 file_to_remove.push_back(wallpaper_path.Append(email));
721 wallpaper_path = wallpaper_path.Append(path_to_file);
722 file_to_remove.push_back(wallpaper_path);
723
724 // Remove original user wallpaper.
725 wallpaper_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
726 file_to_remove.push_back(wallpaper_path.Append(email));
727 wallpaper_path = wallpaper_path.Append(path_to_file);
728 file_to_remove.push_back(wallpaper_path);
729
730 base::WorkerPool::PostTask(
731 FROM_HERE,
732 base::Bind(&WallpaperManager::DeleteWallpaperInList,
733 base::Unretained(this),
734 file_to_remove),
735 false);
736 }
737
EnsureCustomWallpaperDirectories(const std::string & user_id_hash)738 void WallpaperManager::EnsureCustomWallpaperDirectories(
739 const std::string& user_id_hash) {
740 base::FilePath dir;
741 dir = GetCustomWallpaperDir(kSmallWallpaperSubDir);
742 dir = dir.Append(user_id_hash);
743 if (!base::PathExists(dir))
744 base::CreateDirectory(dir);
745 dir = GetCustomWallpaperDir(kLargeWallpaperSubDir);
746 dir = dir.Append(user_id_hash);
747 if (!base::PathExists(dir))
748 base::CreateDirectory(dir);
749 dir = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
750 dir = dir.Append(user_id_hash);
751 if (!base::PathExists(dir))
752 base::CreateDirectory(dir);
753 dir = GetCustomWallpaperDir(kThumbnailWallpaperSubDir);
754 dir = dir.Append(user_id_hash);
755 if (!base::PathExists(dir))
756 base::CreateDirectory(dir);
757 }
758
GetComandLine()759 CommandLine* WallpaperManager::GetComandLine() {
760 CommandLine* command_line = command_line_for_testing_ ?
761 command_line_for_testing_ : CommandLine::ForCurrentProcess();
762 return command_line;
763 }
764
InitializeRegisteredDeviceWallpaper()765 void WallpaperManager::InitializeRegisteredDeviceWallpaper() {
766 if (UserManager::Get()->IsUserLoggedIn())
767 return;
768
769 bool disable_boot_animation = GetComandLine()->
770 HasSwitch(switches::kDisableBootAnimation);
771 bool show_users = true;
772 bool result = CrosSettings::Get()->GetBoolean(
773 kAccountsPrefShowUserNamesOnSignIn, &show_users);
774 DCHECK(result) << "Unable to fetch setting "
775 << kAccountsPrefShowUserNamesOnSignIn;
776 const chromeos::UserList& users = UserManager::Get()->GetUsers();
777 if (!show_users || users.empty()) {
778 // Boot into sign in form, preload default wallpaper.
779 SetDefaultWallpaper();
780 return;
781 }
782
783 if (!disable_boot_animation) {
784 // Normal boot, load user wallpaper.
785 // If normal boot animation is disabled wallpaper would be set
786 // asynchronously once user pods are loaded.
787 SetUserWallpaper(users[0]->email());
788 }
789 }
790
LoadWallpaper(const std::string & email,const WallpaperInfo & info,bool update_wallpaper)791 void WallpaperManager::LoadWallpaper(const std::string& email,
792 const WallpaperInfo& info,
793 bool update_wallpaper) {
794 base::FilePath wallpaper_dir;
795 base::FilePath wallpaper_path;
796 if (info.type == User::ONLINE) {
797 std::string file_name = GURL(info.file).ExtractFileName();
798 ash::WallpaperResolution resolution = ash::Shell::GetInstance()->
799 desktop_background_controller()->GetAppropriateResolution();
800 // Only solid color wallpapers have stretch layout and they have only one
801 // resolution.
802 if (info.layout != ash::WALLPAPER_LAYOUT_STRETCH &&
803 resolution == ash::WALLPAPER_RESOLUTION_SMALL) {
804 file_name = base::FilePath(file_name).InsertBeforeExtension(
805 kSmallWallpaperSuffix).value();
806 }
807 CHECK(PathService::Get(chrome::DIR_CHROMEOS_WALLPAPERS, &wallpaper_dir));
808 wallpaper_path = wallpaper_dir.Append(file_name);
809 if (current_wallpaper_path_ == wallpaper_path)
810 return;
811 if (update_wallpaper)
812 current_wallpaper_path_ = wallpaper_path;
813 loaded_wallpapers_++;
814 StartLoad(email, info, update_wallpaper, wallpaper_path);
815 } else if (info.type == User::DEFAULT) {
816 // Default wallpapers are migrated from M21 user profiles. A code refactor
817 // overlooked that case and caused these wallpapers not being loaded at all.
818 // On some slow devices, it caused login webui not visible after upgrade to
819 // M26 from M21. See crosbug.com/38429 for details.
820 base::FilePath user_data_dir;
821 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
822 wallpaper_path = user_data_dir.Append(info.file);
823 StartLoad(email, info, update_wallpaper, wallpaper_path);
824 } else {
825 // In unexpected cases, revert to default wallpaper to fail safely. See
826 // crosbug.com/38429.
827 LOG(ERROR) << "Wallpaper reverts to default unexpected.";
828 SetDefaultWallpaper();
829 }
830 }
831
GetUserWallpaperInfo(const std::string & email,WallpaperInfo * info)832 bool WallpaperManager::GetUserWallpaperInfo(const std::string& email,
833 WallpaperInfo* info){
834 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
835
836 if (UserManager::Get()->IsUserNonCryptohomeDataEphemeral(email)) {
837 // Default to the values cached in memory.
838 *info = current_user_wallpaper_info_;
839
840 // Ephemeral users do not save anything to local state. But we have got
841 // wallpaper info from memory. Returns true.
842 return true;
843 }
844
845 const DictionaryValue* user_wallpapers = g_browser_process->local_state()->
846 GetDictionary(prefs::kUsersWallpaperInfo);
847 const base::DictionaryValue* wallpaper_info_dict;
848 if (user_wallpapers->GetDictionaryWithoutPathExpansion(
849 email, &wallpaper_info_dict)) {
850 info->file = "";
851 info->layout = ash::WALLPAPER_LAYOUT_CENTER_CROPPED;
852 info->type = User::UNKNOWN;
853 info->date = base::Time::Now().LocalMidnight();
854 wallpaper_info_dict->GetString(kNewWallpaperFileNodeName, &(info->file));
855 int temp;
856 wallpaper_info_dict->GetInteger(kNewWallpaperLayoutNodeName, &temp);
857 info->layout = static_cast<ash::WallpaperLayout>(temp);
858 wallpaper_info_dict->GetInteger(kNewWallpaperTypeNodeName, &temp);
859 info->type = static_cast<User::WallpaperType>(temp);
860 std::string date_string;
861 int64 val;
862 if (!(wallpaper_info_dict->GetString(kNewWallpaperDateNodeName,
863 &date_string) &&
864 base::StringToInt64(date_string, &val)))
865 val = 0;
866 info->date = base::Time::FromInternalValue(val);
867 return true;
868 }
869
870 return false;
871 }
872
MoveCustomWallpapersOnWorker(const std::string & email,const std::string & user_id_hash)873 void WallpaperManager::MoveCustomWallpapersOnWorker(
874 const std::string& email,
875 const std::string& user_id_hash) {
876 DCHECK(BrowserThread::GetBlockingPool()->
877 IsRunningSequenceOnCurrentThread(sequence_token_));
878 if (MoveCustomWallpaperDirectory(kOriginalWallpaperSubDir,
879 email,
880 user_id_hash)) {
881 // Consider success if the original wallpaper is moved to the new directory.
882 // Original wallpaper is the fallback if the correct resolution wallpaper
883 // can not be found.
884 BrowserThread::PostTask(
885 BrowserThread::UI, FROM_HERE,
886 base::Bind(&WallpaperManager::MoveCustomWallpapersSuccess,
887 base::Unretained(this),
888 email,
889 user_id_hash));
890 }
891 MoveCustomWallpaperDirectory(kLargeWallpaperSubDir, email, user_id_hash);
892 MoveCustomWallpaperDirectory(kSmallWallpaperSubDir, email, user_id_hash);
893 MoveCustomWallpaperDirectory(kThumbnailWallpaperSubDir, email, user_id_hash);
894 }
895
MoveCustomWallpapersSuccess(const std::string & email,const std::string & user_id_hash)896 void WallpaperManager::MoveCustomWallpapersSuccess(
897 const std::string& email,
898 const std::string& user_id_hash) {
899 WallpaperInfo info;
900 GetUserWallpaperInfo(email, &info);
901 if (info.type == User::CUSTOMIZED) {
902 // New file field should include user id hash in addition to file name.
903 // This is needed because at login screen, user id hash is not available.
904 std::string relative_path =
905 base::FilePath(user_id_hash).Append(info.file).value();
906 info.file = relative_path;
907 bool is_persistent =
908 !UserManager::Get()->IsUserNonCryptohomeDataEphemeral(email);
909 SetUserWallpaperInfo(email, info, is_persistent);
910 }
911 }
912
MoveLoggedInUserCustomWallpaper()913 void WallpaperManager::MoveLoggedInUserCustomWallpaper() {
914 const User* logged_in_user = UserManager::Get()->GetLoggedInUser();
915 task_runner_->PostTask(
916 FROM_HERE,
917 base::Bind(&WallpaperManager::MoveCustomWallpapersOnWorker,
918 base::Unretained(this),
919 logged_in_user->email(),
920 logged_in_user->username_hash()));
921 }
922
GetCustomWallpaperInternal(const std::string & email,const WallpaperInfo & info,const base::FilePath & wallpaper_path,bool update_wallpaper)923 void WallpaperManager::GetCustomWallpaperInternal(
924 const std::string& email,
925 const WallpaperInfo& info,
926 const base::FilePath& wallpaper_path,
927 bool update_wallpaper) {
928 DCHECK(BrowserThread::GetBlockingPool()->
929 IsRunningSequenceOnCurrentThread(sequence_token_));
930
931 base::FilePath valid_path = wallpaper_path;
932 if (!base::PathExists(wallpaper_path)) {
933 // Falls back on original file if the correct resoltuion file does not
934 // exist. This may happen when the original custom wallpaper is small or
935 // browser shutdown before resized wallpaper saved.
936 valid_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir);
937 valid_path = valid_path.Append(info.file);
938 }
939
940 if (!base::PathExists(valid_path)) {
941 // Falls back to custom wallpaper that uses email as part of its file path.
942 // Note that email is used instead of user_id_hash here.
943 valid_path = GetCustomWallpaperPath(kOriginalWallpaperSubDir, email,
944 info.file);
945 }
946
947 if (!base::PathExists(valid_path)) {
948 LOG(ERROR) << "Failed to load previously selected custom wallpaper. " <<
949 "Fallback to default wallpaper";
950 BrowserThread::PostTask(BrowserThread::UI,
951 FROM_HERE,
952 base::Bind(&WallpaperManager::SetDefaultWallpaper,
953 base::Unretained(this)));
954 } else {
955 BrowserThread::PostTask(BrowserThread::UI,
956 FROM_HERE,
957 base::Bind(&WallpaperManager::StartLoad,
958 base::Unretained(this),
959 email,
960 info,
961 update_wallpaper,
962 valid_path));
963 }
964 }
965
OnWallpaperDecoded(const std::string & email,ash::WallpaperLayout layout,bool update_wallpaper,const UserImage & wallpaper)966 void WallpaperManager::OnWallpaperDecoded(const std::string& email,
967 ash::WallpaperLayout layout,
968 bool update_wallpaper,
969 const UserImage& wallpaper) {
970 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
971 TRACE_EVENT_ASYNC_END0("ui", "LoadAndDecodeWallpaper", this);
972
973 // If decoded wallpaper is empty, we are probably failed to decode the file.
974 // Use default wallpaper in this case.
975 if (wallpaper.image().isNull()) {
976 // Updates user pref to default wallpaper.
977 WallpaperInfo info = {
978 "",
979 ash::WALLPAPER_LAYOUT_CENTER_CROPPED,
980 User::DEFAULT,
981 base::Time::Now().LocalMidnight()
982 };
983 SetUserWallpaperInfo(email, info, true);
984
985 if (update_wallpaper)
986 SetDefaultWallpaper();
987 return;
988 }
989
990 // Only cache user wallpaper at login screen.
991 if (!UserManager::Get()->IsUserLoggedIn()) {
992 wallpaper_cache_.insert(std::make_pair(email, wallpaper.image()));
993 }
994 if (update_wallpaper) {
995 ash::Shell::GetInstance()->desktop_background_controller()->
996 SetCustomWallpaper(wallpaper.image(), layout);
997 }
998 }
999
ProcessCustomWallpaper(const std::string & user_id_hash,bool persistent,const WallpaperInfo & info,scoped_ptr<gfx::ImageSkia> image,const UserImage::RawImage & raw_image)1000 void WallpaperManager::ProcessCustomWallpaper(
1001 const std::string& user_id_hash,
1002 bool persistent,
1003 const WallpaperInfo& info,
1004 scoped_ptr<gfx::ImageSkia> image,
1005 const UserImage::RawImage& raw_image) {
1006 DCHECK(BrowserThread::GetBlockingPool()->
1007 IsRunningSequenceOnCurrentThread(sequence_token_));
1008 UserImage wallpaper(*image.get(), raw_image);
1009 if (persistent) {
1010 SaveCustomWallpaper(user_id_hash, base::FilePath(info.file), info.layout,
1011 wallpaper);
1012 }
1013 }
1014
SaveCustomWallpaper(const std::string & user_id_hash,const base::FilePath & original_path,ash::WallpaperLayout layout,const UserImage & wallpaper)1015 void WallpaperManager::SaveCustomWallpaper(const std::string& user_id_hash,
1016 const base::FilePath& original_path,
1017 ash::WallpaperLayout layout,
1018 const UserImage& wallpaper) {
1019 DCHECK(BrowserThread::GetBlockingPool()->
1020 IsRunningSequenceOnCurrentThread(sequence_token_));
1021 EnsureCustomWallpaperDirectories(user_id_hash);
1022 std::string file_name = original_path.BaseName().value();
1023 base::FilePath small_wallpaper_path =
1024 GetCustomWallpaperPath(kSmallWallpaperSubDir, user_id_hash, file_name);
1025 base::FilePath large_wallpaper_path =
1026 GetCustomWallpaperPath(kLargeWallpaperSubDir, user_id_hash, file_name);
1027
1028 // Re-encode orginal file to jpeg format and saves the result in case that
1029 // resized wallpaper is not generated (i.e. chrome shutdown before resized
1030 // wallpaper is saved).
1031 ResizeAndSaveWallpaper(wallpaper, original_path,
1032 ash::WALLPAPER_LAYOUT_STRETCH,
1033 wallpaper.image().width(),
1034 wallpaper.image().height());
1035 DeleteAllExcept(original_path);
1036
1037 ResizeAndSaveWallpaper(wallpaper, small_wallpaper_path, layout,
1038 ash::kSmallWallpaperMaxWidth,
1039 ash::kSmallWallpaperMaxHeight);
1040 DeleteAllExcept(small_wallpaper_path);
1041 ResizeAndSaveWallpaper(wallpaper, large_wallpaper_path, layout,
1042 ash::kLargeWallpaperMaxWidth,
1043 ash::kLargeWallpaperMaxHeight);
1044 DeleteAllExcept(large_wallpaper_path);
1045 }
1046
RecordUma(User::WallpaperType type,int index)1047 void WallpaperManager::RecordUma(User::WallpaperType type, int index) {
1048 UMA_HISTOGRAM_ENUMERATION("Ash.Wallpaper.Type", type,
1049 User::WALLPAPER_TYPE_COUNT);
1050 }
1051
SaveWallpaperInternal(const base::FilePath & path,const char * data,int size)1052 void WallpaperManager::SaveWallpaperInternal(const base::FilePath& path,
1053 const char* data,
1054 int size) {
1055 int written_bytes = file_util::WriteFile(path, data, size);
1056 DCHECK(written_bytes == size);
1057 }
1058
StartLoad(const std::string & email,const WallpaperInfo & info,bool update_wallpaper,const base::FilePath & wallpaper_path)1059 void WallpaperManager::StartLoad(const std::string& email,
1060 const WallpaperInfo& info,
1061 bool update_wallpaper,
1062 const base::FilePath& wallpaper_path) {
1063 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1064 TRACE_EVENT_ASYNC_BEGIN0("ui", "LoadAndDecodeWallpaper", this);
1065
1066 wallpaper_loader_->Start(wallpaper_path.value(), 0,
1067 base::Bind(&WallpaperManager::OnWallpaperDecoded,
1068 base::Unretained(this),
1069 email,
1070 info.layout,
1071 update_wallpaper));
1072 }
1073
1074 } // namespace chromeos
1075