1 // Copyright 2014 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/users/wallpaper/wallpaper_manager.h" 6 7 #include <numeric> 8 #include <vector> 9 10 #include "ash/ash_constants.h" 11 #include "ash/ash_switches.h" 12 #include "ash/desktop_background/desktop_background_controller.h" 13 #include "ash/shell.h" 14 #include "base/bind.h" 15 #include "base/command_line.h" 16 #include "base/debug/trace_event.h" 17 #include "base/files/file_enumerator.h" 18 #include "base/files/file_path.h" 19 #include "base/files/file_util.h" 20 #include "base/logging.h" 21 #include "base/metrics/histogram.h" 22 #include "base/path_service.h" 23 #include "base/prefs/pref_registry_simple.h" 24 #include "base/prefs/pref_service.h" 25 #include "base/prefs/scoped_user_pref_update.h" 26 #include "base/strings/string_number_conversions.h" 27 #include "base/strings/string_util.h" 28 #include "base/strings/stringprintf.h" 29 #include "base/sys_info.h" 30 #include "base/threading/worker_pool.h" 31 #include "base/time/time.h" 32 #include "base/values.h" 33 #include "chrome/browser/browser_process.h" 34 #include "chrome/browser/chrome_notification_types.h" 35 #include "chrome/browser/chromeos/customization_document.h" 36 #include "chrome/browser/chromeos/login/startup_utils.h" 37 #include "chrome/browser/chromeos/login/wizard_controller.h" 38 #include "chrome/browser/chromeos/settings/cros_settings.h" 39 #include "chrome/common/chrome_paths.h" 40 #include "chrome/common/chrome_switches.h" 41 #include "chrome/common/pref_names.h" 42 #include "chromeos/chromeos_switches.h" 43 #include "chromeos/cryptohome/async_method_caller.h" 44 #include "chromeos/dbus/dbus_thread_manager.h" 45 #include "chromeos/login/user_names.h" 46 #include "components/user_manager/user.h" 47 #include "components/user_manager/user_image/user_image.h" 48 #include "components/user_manager/user_manager.h" 49 #include "components/user_manager/user_type.h" 50 #include "content/public/browser/browser_thread.h" 51 #include "content/public/browser/notification_service.h" 52 #include "third_party/skia/include/core/SkColor.h" 53 #include "ui/gfx/codec/jpeg_codec.h" 54 #include "ui/gfx/image/image_skia_operations.h" 55 #include "ui/gfx/skia_util.h" 56 57 using content::BrowserThread; 58 59 namespace chromeos { 60 61 namespace { 62 63 // The amount of delay before starts to move custom wallpapers to the new place. 64 const int kMoveCustomWallpaperDelaySeconds = 30; 65 66 // Default quality for encoding wallpaper. 67 const int kDefaultEncodingQuality = 90; 68 69 // A dictionary pref that maps usernames to file paths to their wallpapers. 70 // Deprecated. Will remove this const char after done migration. 71 const char kUserWallpapers[] = "UserWallpapers"; 72 73 const int kCacheWallpaperDelayMs = 500; 74 75 // A dictionary pref that maps usernames to wallpaper properties. 76 const char kUserWallpapersProperties[] = "UserWallpapersProperties"; 77 78 // Names of nodes with info about wallpaper in |kUserWallpapersProperties| 79 // dictionary. 80 const char kNewWallpaperDateNodeName[] = "date"; 81 const char kNewWallpaperLayoutNodeName[] = "layout"; 82 const char kNewWallpaperLocationNodeName[] = "file"; 83 const char kNewWallpaperTypeNodeName[] = "type"; 84 85 // Maximum number of wallpapers cached by CacheUsersWallpapers(). 86 const int kMaxWallpapersToCache = 3; 87 88 // Maximum number of entries in WallpaperManager::last_load_times_ . 89 const size_t kLastLoadsStatsMsMaxSize = 4; 90 91 // Minimum delay between wallpaper loads, milliseconds. 92 const unsigned kLoadMinDelayMs = 50; 93 94 // Default wallpaper load delay, milliseconds. 95 const unsigned kLoadDefaultDelayMs = 200; 96 97 // Maximum wallpaper load delay, milliseconds. 98 const unsigned kLoadMaxDelayMs = 2000; 99 100 // When no wallpaper image is specified, the screen is filled with a solid 101 // color. 102 const SkColor kDefaultWallpaperColor = SK_ColorGRAY; 103 104 // For our scaling ratios we need to round positive numbers. 105 int RoundPositive(double x) { 106 return static_cast<int>(floor(x + 0.5)); 107 } 108 109 // Returns custom wallpaper directory by appending corresponding |sub_dir|. 110 base::FilePath GetCustomWallpaperDir(const char* sub_dir) { 111 base::FilePath custom_wallpaper_dir; 112 CHECK(PathService::Get(chrome::DIR_CHROMEOS_CUSTOM_WALLPAPERS, 113 &custom_wallpaper_dir)); 114 return custom_wallpaper_dir.Append(sub_dir); 115 } 116 117 bool MoveCustomWallpaperDirectory(const char* sub_dir, 118 const std::string& user_id, 119 const std::string& user_id_hash) { 120 base::FilePath base_path = GetCustomWallpaperDir(sub_dir); 121 base::FilePath to_path = base_path.Append(user_id_hash); 122 base::FilePath from_path = base_path.Append(user_id); 123 if (base::PathExists(from_path)) 124 return base::Move(from_path, to_path); 125 return false; 126 } 127 128 // These global default values are used to set customized default 129 // wallpaper path in WallpaperManager::InitializeWallpaper(). 130 base::FilePath GetCustomizedWallpaperDefaultRescaledFileName( 131 const std::string& suffix) { 132 const base::FilePath default_downloaded_file_name = 133 ServicesCustomizationDocument::GetCustomizedWallpaperDownloadedFileName(); 134 const base::FilePath default_cache_dir = 135 ServicesCustomizationDocument::GetCustomizedWallpaperCacheDir(); 136 if (default_downloaded_file_name.empty() || default_cache_dir.empty()) 137 return base::FilePath(); 138 return default_cache_dir.Append( 139 default_downloaded_file_name.BaseName().value() + suffix); 140 } 141 142 // Whether DesktopBackgroundController should start with customized default 143 // wallpaper in WallpaperManager::InitializeWallpaper() or not. 144 bool ShouldUseCustomizedDefaultWallpaper() { 145 PrefService* pref_service = g_browser_process->local_state(); 146 147 return !(pref_service->FindPreference( 148 prefs::kCustomizationDefaultWallpaperURL) 149 ->IsDefaultValue()); 150 } 151 152 // Deletes a list of wallpaper files in |file_list|. 153 void DeleteWallpaperInList(const std::vector<base::FilePath>& file_list) { 154 for (std::vector<base::FilePath>::const_iterator it = file_list.begin(); 155 it != file_list.end(); ++it) { 156 base::FilePath path = *it; 157 // Some users may still have legacy wallpapers with png extension. We need 158 // to delete these wallpapers too. 159 if (!base::DeleteFile(path, true) && 160 !base::DeleteFile(path.AddExtension(".png"), false)) { 161 LOG(ERROR) << "Failed to remove user wallpaper at " << path.value(); 162 } 163 } 164 } 165 166 // Creates all new custom wallpaper directories for |user_id_hash| if not exist. 167 // static 168 void EnsureCustomWallpaperDirectories(const std::string& user_id_hash) { 169 base::FilePath dir; 170 dir = GetCustomWallpaperDir(kSmallWallpaperSubDir); 171 dir = dir.Append(user_id_hash); 172 if (!base::PathExists(dir)) 173 base::CreateDirectory(dir); 174 dir = GetCustomWallpaperDir(kLargeWallpaperSubDir); 175 dir = dir.Append(user_id_hash); 176 if (!base::PathExists(dir)) 177 base::CreateDirectory(dir); 178 dir = GetCustomWallpaperDir(kOriginalWallpaperSubDir); 179 dir = dir.Append(user_id_hash); 180 if (!base::PathExists(dir)) 181 base::CreateDirectory(dir); 182 dir = GetCustomWallpaperDir(kThumbnailWallpaperSubDir); 183 dir = dir.Append(user_id_hash); 184 if (!base::PathExists(dir)) 185 base::CreateDirectory(dir); 186 } 187 188 // Saves wallpaper image raw |data| to |path| (absolute path) in file system. 189 // Returns true on success. 190 bool SaveWallpaperInternal(const base::FilePath& path, 191 const char* data, 192 int size) { 193 int written_bytes = base::WriteFile(path, data, size); 194 return written_bytes == size; 195 } 196 197 // Returns index of the first public session user found in |users| 198 // or -1 otherwise. 199 int FindPublicSession(const user_manager::UserList& users) { 200 int index = -1; 201 int i = 0; 202 for (user_manager::UserList::const_iterator it = users.begin(); 203 it != users.end(); 204 ++it, ++i) { 205 if ((*it)->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT) { 206 index = i; 207 break; 208 } 209 } 210 211 return index; 212 } 213 214 } // namespace 215 216 const char kWallpaperSequenceTokenName[] = "wallpaper-sequence"; 217 218 const char kSmallWallpaperSuffix[] = "_small"; 219 const char kLargeWallpaperSuffix[] = "_large"; 220 221 const char kSmallWallpaperSubDir[] = "small"; 222 const char kLargeWallpaperSubDir[] = "large"; 223 const char kOriginalWallpaperSubDir[] = "original"; 224 const char kThumbnailWallpaperSubDir[] = "thumb"; 225 226 const int kSmallWallpaperMaxWidth = 1366; 227 const int kSmallWallpaperMaxHeight = 800; 228 const int kLargeWallpaperMaxWidth = 2560; 229 const int kLargeWallpaperMaxHeight = 1700; 230 const int kWallpaperThumbnailWidth = 108; 231 const int kWallpaperThumbnailHeight = 68; 232 233 static WallpaperManager* g_wallpaper_manager = NULL; 234 235 class WallpaperManager::CustomizedWallpaperRescaledFiles { 236 public: 237 CustomizedWallpaperRescaledFiles(const base::FilePath& path_downloaded, 238 const base::FilePath& path_rescaled_small, 239 const base::FilePath& path_rescaled_large); 240 241 bool AllSizesExist() const; 242 243 // Closure will hold unretained pointer to this object. So caller must 244 // make sure that the closure will be destoyed before this object. 245 // Closure must be called on BlockingPool. 246 base::Closure CreateCheckerClosure(); 247 248 const base::FilePath& path_downloaded() const { return path_downloaded_; } 249 const base::FilePath& path_rescaled_small() const { 250 return path_rescaled_small_; 251 } 252 const base::FilePath& path_rescaled_large() const { 253 return path_rescaled_large_; 254 } 255 256 const bool downloaded_exists() const { return downloaded_exists_; } 257 const bool rescaled_small_exists() const { return rescaled_small_exists_; } 258 const bool rescaled_large_exists() const { return rescaled_large_exists_; } 259 260 private: 261 // Must be called on BlockingPool. 262 void CheckCustomizedWallpaperFilesExist(); 263 264 const base::FilePath path_downloaded_; 265 const base::FilePath path_rescaled_small_; 266 const base::FilePath path_rescaled_large_; 267 268 bool downloaded_exists_; 269 bool rescaled_small_exists_; 270 bool rescaled_large_exists_; 271 272 DISALLOW_COPY_AND_ASSIGN(CustomizedWallpaperRescaledFiles); 273 }; 274 275 WallpaperManager::CustomizedWallpaperRescaledFiles:: 276 CustomizedWallpaperRescaledFiles(const base::FilePath& path_downloaded, 277 const base::FilePath& path_rescaled_small, 278 const base::FilePath& path_rescaled_large) 279 : path_downloaded_(path_downloaded), 280 path_rescaled_small_(path_rescaled_small), 281 path_rescaled_large_(path_rescaled_large), 282 downloaded_exists_(false), 283 rescaled_small_exists_(false), 284 rescaled_large_exists_(false) { 285 } 286 287 base::Closure 288 WallpaperManager::CustomizedWallpaperRescaledFiles::CreateCheckerClosure() { 289 return base::Bind(&WallpaperManager::CustomizedWallpaperRescaledFiles:: 290 CheckCustomizedWallpaperFilesExist, 291 base::Unretained(this)); 292 } 293 294 void WallpaperManager::CustomizedWallpaperRescaledFiles:: 295 CheckCustomizedWallpaperFilesExist() { 296 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 297 downloaded_exists_ = base::PathExists(path_downloaded_); 298 rescaled_small_exists_ = base::PathExists(path_rescaled_small_); 299 rescaled_large_exists_ = base::PathExists(path_rescaled_large_); 300 } 301 302 bool WallpaperManager::CustomizedWallpaperRescaledFiles::AllSizesExist() const { 303 return rescaled_small_exists_ && rescaled_large_exists_; 304 } 305 306 // This object is passed between several threads while wallpaper is being 307 // loaded. It will notify callback when last reference to it is removed 308 // (thus indicating that the last load action has finished). 309 class MovableOnDestroyCallback { 310 public: 311 explicit MovableOnDestroyCallback(const base::Closure& callback) 312 : callback_(callback) { 313 } 314 315 ~MovableOnDestroyCallback() { 316 if (!callback_.is_null()) 317 callback_.Run(); 318 } 319 320 private: 321 base::Closure callback_; 322 }; 323 324 WallpaperManager::PendingWallpaper::PendingWallpaper( 325 const base::TimeDelta delay, 326 const std::string& user_id) 327 : user_id_(user_id), 328 default_(false), 329 on_finish_(new MovableOnDestroyCallback( 330 base::Bind(&WallpaperManager::PendingWallpaper::OnWallpaperSet, 331 this))) { 332 timer.Start( 333 FROM_HERE, 334 delay, 335 base::Bind(&WallpaperManager::PendingWallpaper::ProcessRequest, this)); 336 } 337 338 WallpaperManager::PendingWallpaper::~PendingWallpaper() {} 339 340 void WallpaperManager::PendingWallpaper::ResetSetWallpaperImage( 341 const gfx::ImageSkia& image, 342 const WallpaperInfo& info) { 343 SetMode(image, info, base::FilePath(), false); 344 } 345 346 void WallpaperManager::PendingWallpaper::ResetLoadWallpaper( 347 const WallpaperInfo& info) { 348 SetMode(gfx::ImageSkia(), info, base::FilePath(), false); 349 } 350 351 void WallpaperManager::PendingWallpaper::ResetSetCustomWallpaper( 352 const WallpaperInfo& info, 353 const base::FilePath& wallpaper_path) { 354 SetMode(gfx::ImageSkia(), info, wallpaper_path, false); 355 } 356 357 void WallpaperManager::PendingWallpaper::ResetSetDefaultWallpaper() { 358 SetMode(gfx::ImageSkia(), WallpaperInfo(), base::FilePath(), true); 359 } 360 361 void WallpaperManager::PendingWallpaper::SetMode( 362 const gfx::ImageSkia& image, 363 const WallpaperInfo& info, 364 const base::FilePath& wallpaper_path, 365 const bool is_default) { 366 user_wallpaper_ = image; 367 info_ = info; 368 wallpaper_path_ = wallpaper_path; 369 default_ = is_default; 370 } 371 372 void WallpaperManager::PendingWallpaper::ProcessRequest() { 373 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 374 375 timer.Stop(); // Erase reference to self. 376 377 WallpaperManager* manager = WallpaperManager::Get(); 378 if (manager->pending_inactive_ == this) 379 manager->pending_inactive_ = NULL; 380 381 started_load_at_ = base::Time::Now(); 382 383 if (default_) { 384 manager->DoSetDefaultWallpaper(user_id_, on_finish_.Pass()); 385 } else if (!user_wallpaper_.isNull()) { 386 ash::Shell::GetInstance() 387 ->desktop_background_controller() 388 ->SetWallpaperImage(user_wallpaper_, info_.layout); 389 } else if (!wallpaper_path_.empty()) { 390 manager->task_runner_->PostTask( 391 FROM_HERE, 392 base::Bind(&WallpaperManager::GetCustomWallpaperInternal, 393 user_id_, 394 info_, 395 wallpaper_path_, 396 true /* update wallpaper */, 397 base::Passed(on_finish_.Pass()), 398 manager->weak_factory_.GetWeakPtr())); 399 } else if (!info_.location.empty()) { 400 manager->LoadWallpaper(user_id_, info_, true, on_finish_.Pass()); 401 } else { 402 // PendingWallpaper was created and never initialized? 403 NOTREACHED(); 404 // Error. Do not record time. 405 started_load_at_ = base::Time(); 406 } 407 on_finish_.reset(); 408 } 409 410 void WallpaperManager::PendingWallpaper::OnWallpaperSet() { 411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 412 413 // The only known case for this check to fail is global destruction during 414 // wallpaper load. It should never happen. 415 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) 416 return; // We are in a process of global destruction. 417 418 timer.Stop(); // Erase reference to self. 419 420 WallpaperManager* manager = WallpaperManager::Get(); 421 if (!started_load_at_.is_null()) { 422 const base::TimeDelta elapsed = base::Time::Now() - started_load_at_; 423 manager->SaveLastLoadTime(elapsed); 424 } 425 if (manager->pending_inactive_ == this) { 426 // ProcessRequest() was never executed. 427 manager->pending_inactive_ = NULL; 428 } 429 430 // Destroy self. 431 manager->RemovePendingWallpaperFromList(this); 432 } 433 434 // WallpaperManager, public: --------------------------------------------------- 435 436 // TestApi. For testing purpose 437 WallpaperManager::TestApi::TestApi(WallpaperManager* wallpaper_manager) 438 : wallpaper_manager_(wallpaper_manager) { 439 } 440 441 WallpaperManager::TestApi::~TestApi() { 442 } 443 444 base::FilePath WallpaperManager::TestApi::current_wallpaper_path() { 445 return wallpaper_manager_->current_wallpaper_path_; 446 } 447 448 bool WallpaperManager::TestApi::GetWallpaperFromCache( 449 const std::string& user_id, gfx::ImageSkia* image) { 450 return wallpaper_manager_->GetWallpaperFromCache(user_id, image); 451 } 452 453 void WallpaperManager::TestApi::SetWallpaperCache(const std::string& user_id, 454 const gfx::ImageSkia& image) { 455 DCHECK(!image.isNull()); 456 wallpaper_manager_->wallpaper_cache_[user_id] = image; 457 } 458 459 void WallpaperManager::TestApi::ClearDisposableWallpaperCache() { 460 wallpaper_manager_->ClearDisposableWallpaperCache(); 461 } 462 463 // static 464 WallpaperManager* WallpaperManager::Get() { 465 if (!g_wallpaper_manager) 466 g_wallpaper_manager = new WallpaperManager(); 467 return g_wallpaper_manager; 468 } 469 470 WallpaperManager::WallpaperManager() 471 : loaded_wallpapers_(0), 472 command_line_for_testing_(NULL), 473 should_cache_wallpaper_(false), 474 pending_inactive_(NULL), 475 weak_factory_(this) { 476 SetDefaultWallpaperPathsFromCommandLine( 477 base::CommandLine::ForCurrentProcess()); 478 registrar_.Add(this, 479 chrome::NOTIFICATION_LOGIN_USER_CHANGED, 480 content::NotificationService::AllSources()); 481 registrar_.Add(this, 482 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, 483 content::NotificationService::AllSources()); 484 registrar_.Add(this, 485 chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED, 486 content::NotificationService::AllSources()); 487 sequence_token_ = BrowserThread::GetBlockingPool()-> 488 GetNamedSequenceToken(kWallpaperSequenceTokenName); 489 task_runner_ = BrowserThread::GetBlockingPool()-> 490 GetSequencedTaskRunnerWithShutdownBehavior( 491 sequence_token_, 492 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); 493 wallpaper_loader_ = new UserImageLoader(ImageDecoder::ROBUST_JPEG_CODEC, 494 task_runner_); 495 } 496 497 WallpaperManager::~WallpaperManager() { 498 // TODO(bshe): Lifetime of WallpaperManager needs more consideration. 499 // http://crbug.com/171694 500 DCHECK(!show_user_name_on_signin_subscription_); 501 502 ClearObsoleteWallpaperPrefs(); 503 weak_factory_.InvalidateWeakPtrs(); 504 } 505 506 void WallpaperManager::Shutdown() { 507 show_user_name_on_signin_subscription_.reset(); 508 } 509 510 // static 511 void WallpaperManager::RegisterPrefs(PrefRegistrySimple* registry) { 512 registry->RegisterDictionaryPref(prefs::kUsersWallpaperInfo); 513 registry->RegisterDictionaryPref(kUserWallpapers); 514 registry->RegisterDictionaryPref(kUserWallpapersProperties); 515 } 516 517 void WallpaperManager::AddObservers() { 518 show_user_name_on_signin_subscription_ = 519 CrosSettings::Get()->AddSettingsObserver( 520 kAccountsPrefShowUserNamesOnSignIn, 521 base::Bind(&WallpaperManager::InitializeRegisteredDeviceWallpaper, 522 weak_factory_.GetWeakPtr())); 523 } 524 525 void WallpaperManager::EnsureLoggedInUserWallpaperLoaded() { 526 // Some browser tests do not have a shell instance. As no wallpaper is needed 527 // in these tests anyway, avoid loading one, preventing crashes and speeding 528 // up the tests. 529 if (!ash::Shell::HasInstance()) 530 return; 531 532 WallpaperInfo info; 533 if (GetLoggedInUserWallpaperInfo(&info)) { 534 UMA_HISTOGRAM_ENUMERATION("Ash.Wallpaper.Type", info.type, 535 user_manager::User::WALLPAPER_TYPE_COUNT); 536 if (info == current_user_wallpaper_info_) 537 return; 538 } 539 SetUserWallpaperNow( 540 user_manager::UserManager::Get()->GetLoggedInUser()->email()); 541 } 542 543 void WallpaperManager::ClearDisposableWallpaperCache() { 544 // Cancel callback for previous cache requests. 545 weak_factory_.InvalidateWeakPtrs(); 546 // Keep the wallpaper of logged in users in cache at multi-profile mode. 547 std::set<std::string> logged_in_users_names; 548 const user_manager::UserList& logged_users = 549 user_manager::UserManager::Get()->GetLoggedInUsers(); 550 for (user_manager::UserList::const_iterator it = logged_users.begin(); 551 it != logged_users.end(); 552 ++it) { 553 logged_in_users_names.insert((*it)->email()); 554 } 555 556 CustomWallpaperMap logged_in_users_cache; 557 for (CustomWallpaperMap::iterator it = wallpaper_cache_.begin(); 558 it != wallpaper_cache_.end(); ++it) { 559 if (logged_in_users_names.find(it->first) != 560 logged_in_users_names.end()) { 561 logged_in_users_cache.insert(*it); 562 } 563 } 564 wallpaper_cache_ = logged_in_users_cache; 565 } 566 567 bool WallpaperManager::GetLoggedInUserWallpaperInfo(WallpaperInfo* info) { 568 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 569 570 if (user_manager::UserManager::Get()->IsLoggedInAsStub()) { 571 info->location = current_user_wallpaper_info_.location = ""; 572 info->layout = current_user_wallpaper_info_.layout = 573 ash::WALLPAPER_LAYOUT_CENTER_CROPPED; 574 info->type = current_user_wallpaper_info_.type = 575 user_manager::User::DEFAULT; 576 info->date = current_user_wallpaper_info_.date = 577 base::Time::Now().LocalMidnight(); 578 return true; 579 } 580 581 return GetUserWallpaperInfo( 582 user_manager::UserManager::Get()->GetLoggedInUser()->email(), info); 583 } 584 585 void WallpaperManager::InitializeWallpaper() { 586 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 587 user_manager::UserManager* user_manager = user_manager::UserManager::Get(); 588 589 // Apply device customization. 590 if (ShouldUseCustomizedDefaultWallpaper()) { 591 SetDefaultWallpaperPath( 592 GetCustomizedWallpaperDefaultRescaledFileName(kSmallWallpaperSuffix), 593 scoped_ptr<gfx::ImageSkia>().Pass(), 594 GetCustomizedWallpaperDefaultRescaledFileName(kLargeWallpaperSuffix), 595 scoped_ptr<gfx::ImageSkia>().Pass()); 596 } 597 598 CommandLine* command_line = GetCommandLine(); 599 if (command_line->HasSwitch(chromeos::switches::kGuestSession)) { 600 // Guest wallpaper should be initialized when guest login. 601 // Note: This maybe called before login. So IsLoggedInAsGuest can not be 602 // used here to determine if current user is guest. 603 return; 604 } 605 606 if (command_line->HasSwitch(::switches::kTestType)) 607 WizardController::SetZeroDelays(); 608 609 // Zero delays is also set in autotests. 610 if (WizardController::IsZeroDelayEnabled()) { 611 // Ensure tests have some sort of wallpaper. 612 ash::Shell::GetInstance()->desktop_background_controller()-> 613 CreateEmptyWallpaper(); 614 return; 615 } 616 617 if (!user_manager->IsUserLoggedIn()) { 618 if (!StartupUtils::IsDeviceRegistered()) 619 SetDefaultWallpaperDelayed(chromeos::login::kSignInUser); 620 else 621 InitializeRegisteredDeviceWallpaper(); 622 return; 623 } 624 SetUserWallpaperDelayed(user_manager->GetLoggedInUser()->email()); 625 } 626 627 void WallpaperManager::Observe(int type, 628 const content::NotificationSource& source, 629 const content::NotificationDetails& details) { 630 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 631 switch (type) { 632 case chrome::NOTIFICATION_LOGIN_USER_CHANGED: { 633 ClearDisposableWallpaperCache(); 634 BrowserThread::PostDelayedTask( 635 BrowserThread::UI, 636 FROM_HERE, 637 base::Bind(&WallpaperManager::MoveLoggedInUserCustomWallpaper, 638 weak_factory_.GetWeakPtr()), 639 base::TimeDelta::FromSeconds(kMoveCustomWallpaperDelaySeconds)); 640 break; 641 } 642 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: { 643 if (!GetCommandLine()->HasSwitch(switches::kDisableBootAnimation)) { 644 BrowserThread::PostDelayedTask( 645 BrowserThread::UI, FROM_HERE, 646 base::Bind(&WallpaperManager::CacheUsersWallpapers, 647 weak_factory_.GetWeakPtr()), 648 base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs)); 649 } else { 650 should_cache_wallpaper_ = true; 651 } 652 break; 653 } 654 case chrome::NOTIFICATION_WALLPAPER_ANIMATION_FINISHED: { 655 NotifyAnimationFinished(); 656 if (should_cache_wallpaper_) { 657 BrowserThread::PostDelayedTask( 658 BrowserThread::UI, FROM_HERE, 659 base::Bind(&WallpaperManager::CacheUsersWallpapers, 660 weak_factory_.GetWeakPtr()), 661 base::TimeDelta::FromMilliseconds(kCacheWallpaperDelayMs)); 662 should_cache_wallpaper_ = false; 663 } 664 break; 665 } 666 default: 667 NOTREACHED() << "Unexpected notification " << type; 668 } 669 } 670 671 void WallpaperManager::RemoveUserWallpaperInfo(const std::string& user_id) { 672 WallpaperInfo info; 673 GetUserWallpaperInfo(user_id, &info); 674 PrefService* prefs = g_browser_process->local_state(); 675 DictionaryPrefUpdate prefs_wallpapers_info_update(prefs, 676 prefs::kUsersWallpaperInfo); 677 prefs_wallpapers_info_update->RemoveWithoutPathExpansion(user_id, NULL); 678 DeleteUserWallpapers(user_id, info.location); 679 } 680 681 // static 682 bool WallpaperManager::ResizeImage(const gfx::ImageSkia& image, 683 ash::WallpaperLayout layout, 684 int preferred_width, 685 int preferred_height, 686 scoped_refptr<base::RefCountedBytes>* output, 687 gfx::ImageSkia* output_skia) { 688 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 689 int width = image.width(); 690 int height = image.height(); 691 int resized_width; 692 int resized_height; 693 *output = new base::RefCountedBytes(); 694 695 if (layout == ash::WALLPAPER_LAYOUT_CENTER_CROPPED) { 696 // Do not resize custom wallpaper if it is smaller than preferred size. 697 if (!(width > preferred_width && height > preferred_height)) 698 return false; 699 700 double horizontal_ratio = static_cast<double>(preferred_width) / width; 701 double vertical_ratio = static_cast<double>(preferred_height) / height; 702 if (vertical_ratio > horizontal_ratio) { 703 resized_width = 704 RoundPositive(static_cast<double>(width) * vertical_ratio); 705 resized_height = preferred_height; 706 } else { 707 resized_width = preferred_width; 708 resized_height = 709 RoundPositive(static_cast<double>(height) * horizontal_ratio); 710 } 711 } else if (layout == ash::WALLPAPER_LAYOUT_STRETCH) { 712 resized_width = preferred_width; 713 resized_height = preferred_height; 714 } else { 715 resized_width = width; 716 resized_height = height; 717 } 718 719 gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage( 720 image, 721 skia::ImageOperations::RESIZE_LANCZOS3, 722 gfx::Size(resized_width, resized_height)); 723 724 SkBitmap bitmap = *(resized_image.bitmap()); 725 SkAutoLockPixels lock_input(bitmap); 726 gfx::JPEGCodec::Encode( 727 reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)), 728 gfx::JPEGCodec::FORMAT_SkBitmap, 729 bitmap.width(), 730 bitmap.height(), 731 bitmap.width() * bitmap.bytesPerPixel(), 732 kDefaultEncodingQuality, 733 &(*output)->data()); 734 735 if (output_skia) { 736 resized_image.MakeThreadSafe(); 737 *output_skia = resized_image; 738 } 739 740 return true; 741 } 742 743 // static 744 bool WallpaperManager::ResizeAndSaveWallpaper(const gfx::ImageSkia& image, 745 const base::FilePath& path, 746 ash::WallpaperLayout layout, 747 int preferred_width, 748 int preferred_height, 749 gfx::ImageSkia* output_skia) { 750 if (layout == ash::WALLPAPER_LAYOUT_CENTER) { 751 // TODO(bshe): Generates cropped custom wallpaper for CENTER layout. 752 if (base::PathExists(path)) 753 base::DeleteFile(path, false); 754 return false; 755 } 756 scoped_refptr<base::RefCountedBytes> data; 757 if (ResizeImage(image, 758 layout, 759 preferred_width, 760 preferred_height, 761 &data, 762 output_skia)) { 763 return SaveWallpaperInternal( 764 path, reinterpret_cast<const char*>(data->front()), data->size()); 765 } 766 return false; 767 } 768 769 bool WallpaperManager::IsPolicyControlled(const std::string& user_id) const { 770 chromeos::WallpaperInfo info; 771 if (!GetUserWallpaperInfo(user_id, &info)) 772 return false; 773 return info.type == user_manager::User::POLICY; 774 } 775 776 void WallpaperManager::OnPolicySet(const std::string& policy, 777 const std::string& user_id) { 778 WallpaperInfo info; 779 GetUserWallpaperInfo(user_id, &info); 780 info.type = user_manager::User::POLICY; 781 SetUserWallpaperInfo(user_id, info, true /* is_persistent */); 782 } 783 784 void WallpaperManager::OnPolicyCleared(const std::string& policy, 785 const std::string& user_id) { 786 WallpaperInfo info; 787 GetUserWallpaperInfo(user_id, &info); 788 info.type = user_manager::User::DEFAULT; 789 SetUserWallpaperInfo(user_id, info, true /* is_persistent */); 790 SetDefaultWallpaperNow(user_id); 791 } 792 793 void WallpaperManager::OnPolicyFetched(const std::string& policy, 794 const std::string& user_id, 795 scoped_ptr<std::string> data) { 796 if (!data) 797 return; 798 799 wallpaper_loader_->Start( 800 data.Pass(), 801 0, // Do not crop. 802 base::Bind(&WallpaperManager::SetPolicyControlledWallpaper, 803 weak_factory_.GetWeakPtr(), 804 user_id)); 805 } 806 807 // static 808 WallpaperManager::WallpaperResolution 809 WallpaperManager::GetAppropriateResolution() { 810 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 811 gfx::Size size = 812 ash::DesktopBackgroundController::GetMaxDisplaySizeInNative(); 813 return (size.width() > kSmallWallpaperMaxWidth || 814 size.height() > kSmallWallpaperMaxHeight) 815 ? WALLPAPER_RESOLUTION_LARGE 816 : WALLPAPER_RESOLUTION_SMALL; 817 } 818 819 // static 820 base::FilePath WallpaperManager::GetCustomWallpaperPath( 821 const char* sub_dir, 822 const std::string& user_id_hash, 823 const std::string& file) { 824 base::FilePath custom_wallpaper_path = GetCustomWallpaperDir(sub_dir); 825 return custom_wallpaper_path.Append(user_id_hash).Append(file); 826 } 827 828 void WallpaperManager::SetPolicyControlledWallpaper( 829 const std::string& user_id, 830 const user_manager::UserImage& user_image) { 831 const user_manager::User* user = 832 user_manager::UserManager::Get()->FindUser(user_id); 833 if (!user) { 834 NOTREACHED() << "Unknown user."; 835 return; 836 } 837 838 if (user->username_hash().empty()) { 839 cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername( 840 user_id, 841 base::Bind(&WallpaperManager::SetCustomWallpaperOnSanitizedUsername, 842 weak_factory_.GetWeakPtr(), 843 user_id, 844 user_image.image(), 845 true /* update wallpaper */)); 846 } else { 847 SetCustomWallpaper(user_id, 848 user->username_hash(), 849 "policy-controlled.jpeg", 850 ash::WALLPAPER_LAYOUT_CENTER_CROPPED, 851 user_manager::User::POLICY, 852 user_image.image(), 853 true /* update wallpaper */); 854 } 855 } 856 857 void WallpaperManager::SetCustomWallpaperOnSanitizedUsername( 858 const std::string& user_id, 859 const gfx::ImageSkia& image, 860 bool update_wallpaper, 861 bool cryptohome_success, 862 const std::string& user_id_hash) { 863 if (!cryptohome_success) 864 return; 865 SetCustomWallpaper(user_id, 866 user_id_hash, 867 "policy-controlled.jpeg", 868 ash::WALLPAPER_LAYOUT_CENTER_CROPPED, 869 user_manager::User::POLICY, 870 image, 871 update_wallpaper); 872 } 873 874 void WallpaperManager::SetCustomWallpaper( 875 const std::string& user_id, 876 const std::string& user_id_hash, 877 const std::string& file, 878 ash::WallpaperLayout layout, 879 user_manager::User::WallpaperType type, 880 const gfx::ImageSkia& image, 881 bool update_wallpaper) { 882 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 883 884 // There is no visible background in kiosk mode. 885 if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp()) 886 return; 887 888 // Don't allow custom wallpapers while policy is in effect. 889 if (type != user_manager::User::POLICY && IsPolicyControlled(user_id)) 890 return; 891 892 base::FilePath wallpaper_path = 893 GetCustomWallpaperPath(kOriginalWallpaperSubDir, user_id_hash, file); 894 895 // If decoded wallpaper is empty, we have probably failed to decode the file. 896 // Use default wallpaper in this case. 897 if (image.isNull()) { 898 SetDefaultWallpaperDelayed(user_id); 899 return; 900 } 901 902 const user_manager::User* user = 903 user_manager::UserManager::Get()->FindUser(user_id); 904 CHECK(user); 905 bool is_persistent = 906 !user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral( 907 user_id) || 908 (type == user_manager::User::POLICY && 909 user->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT); 910 911 WallpaperInfo wallpaper_info = { 912 wallpaper_path.value(), 913 layout, 914 type, 915 // Date field is not used. 916 base::Time::Now().LocalMidnight() 917 }; 918 if (is_persistent) { 919 image.EnsureRepsForSupportedScales(); 920 scoped_ptr<gfx::ImageSkia> deep_copy(image.DeepCopy()); 921 // Block shutdown on this task. Otherwise, we may lose the custom wallpaper 922 // that the user selected. 923 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner = 924 BrowserThread::GetBlockingPool() 925 ->GetSequencedTaskRunnerWithShutdownBehavior( 926 sequence_token_, base::SequencedWorkerPool::BLOCK_SHUTDOWN); 927 // TODO(bshe): This may break if RawImage becomes RefCountedMemory. 928 blocking_task_runner->PostTask( 929 FROM_HERE, 930 base::Bind(&WallpaperManager::SaveCustomWallpaper, 931 user_id_hash, 932 base::FilePath(wallpaper_info.location), 933 wallpaper_info.layout, 934 base::Passed(deep_copy.Pass()))); 935 } 936 937 std::string relative_path = base::FilePath(user_id_hash).Append(file).value(); 938 // User's custom wallpaper path is determined by relative path and the 939 // appropriate wallpaper resolution in GetCustomWallpaperInternal. 940 WallpaperInfo info = { 941 relative_path, 942 layout, 943 type, 944 base::Time::Now().LocalMidnight() 945 }; 946 SetUserWallpaperInfo(user_id, info, is_persistent); 947 if (update_wallpaper) { 948 GetPendingWallpaper(user_id, false)->ResetSetWallpaperImage(image, info); 949 } 950 951 wallpaper_cache_[user_id] = image; 952 } 953 954 void WallpaperManager::SetDefaultWallpaperNow(const std::string& user_id) { 955 GetPendingWallpaper(user_id, false)->ResetSetDefaultWallpaper(); 956 } 957 958 void WallpaperManager::SetDefaultWallpaperDelayed(const std::string& user_id) { 959 GetPendingWallpaper(user_id, true)->ResetSetDefaultWallpaper(); 960 } 961 962 void WallpaperManager::DoSetDefaultWallpaper( 963 const std::string& user_id, 964 MovableOnDestroyCallbackHolder on_finish) { 965 // There is no visible background in kiosk mode. 966 if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp()) 967 return; 968 current_wallpaper_path_.clear(); 969 wallpaper_cache_.erase(user_id); 970 // Some browser tests do not have a shell instance. As no wallpaper is needed 971 // in these tests anyway, avoid loading one, preventing crashes and speeding 972 // up the tests. 973 if (!ash::Shell::HasInstance()) 974 return; 975 976 WallpaperResolution resolution = GetAppropriateResolution(); 977 const bool use_small = (resolution == WALLPAPER_RESOLUTION_SMALL); 978 979 const base::FilePath* file = NULL; 980 981 if (user_manager::UserManager::Get()->IsLoggedInAsGuest()) { 982 file = 983 use_small ? &guest_small_wallpaper_file_ : &guest_large_wallpaper_file_; 984 } else { 985 file = use_small ? &default_small_wallpaper_file_ 986 : &default_large_wallpaper_file_; 987 } 988 ash::WallpaperLayout layout = use_small 989 ? ash::WALLPAPER_LAYOUT_CENTER 990 : ash::WALLPAPER_LAYOUT_CENTER_CROPPED; 991 DCHECK(file); 992 if (!default_wallpaper_image_.get() || 993 default_wallpaper_image_->file_path() != file->value()) { 994 default_wallpaper_image_.reset(); 995 if (!file->empty()) { 996 loaded_wallpapers_++; 997 StartLoadAndSetDefaultWallpaper( 998 *file, layout, on_finish.Pass(), &default_wallpaper_image_); 999 return; 1000 } 1001 1002 CreateSolidDefaultWallpaper(); 1003 } 1004 // 1x1 wallpaper is actually solid color, so it should be stretched. 1005 if (default_wallpaper_image_->image().width() == 1 && 1006 default_wallpaper_image_->image().height() == 1) 1007 layout = ash::WALLPAPER_LAYOUT_STRETCH; 1008 1009 ash::Shell::GetInstance()->desktop_background_controller()->SetWallpaperImage( 1010 default_wallpaper_image_->image(), layout); 1011 } 1012 1013 // static 1014 void WallpaperManager::SaveCustomWallpaper(const std::string& user_id_hash, 1015 const base::FilePath& original_path, 1016 ash::WallpaperLayout layout, 1017 scoped_ptr<gfx::ImageSkia> image) { 1018 base::DeleteFile( 1019 GetCustomWallpaperDir(kOriginalWallpaperSubDir).Append(user_id_hash), 1020 true /* recursive */); 1021 base::DeleteFile( 1022 GetCustomWallpaperDir(kSmallWallpaperSubDir).Append(user_id_hash), 1023 true /* recursive */); 1024 base::DeleteFile( 1025 GetCustomWallpaperDir(kLargeWallpaperSubDir).Append(user_id_hash), 1026 true /* recursive */); 1027 EnsureCustomWallpaperDirectories(user_id_hash); 1028 std::string file_name = original_path.BaseName().value(); 1029 base::FilePath small_wallpaper_path = 1030 GetCustomWallpaperPath(kSmallWallpaperSubDir, user_id_hash, file_name); 1031 base::FilePath large_wallpaper_path = 1032 GetCustomWallpaperPath(kLargeWallpaperSubDir, user_id_hash, file_name); 1033 1034 // Re-encode orginal file to jpeg format and saves the result in case that 1035 // resized wallpaper is not generated (i.e. chrome shutdown before resized 1036 // wallpaper is saved). 1037 ResizeAndSaveWallpaper(*image, 1038 original_path, 1039 ash::WALLPAPER_LAYOUT_STRETCH, 1040 image->width(), 1041 image->height(), 1042 NULL); 1043 ResizeAndSaveWallpaper(*image, 1044 small_wallpaper_path, 1045 layout, 1046 kSmallWallpaperMaxWidth, 1047 kSmallWallpaperMaxHeight, 1048 NULL); 1049 ResizeAndSaveWallpaper(*image, 1050 large_wallpaper_path, 1051 layout, 1052 kLargeWallpaperMaxWidth, 1053 kLargeWallpaperMaxHeight, 1054 NULL); 1055 } 1056 1057 // static 1058 void WallpaperManager::MoveCustomWallpapersOnWorker( 1059 const std::string& user_id, 1060 const std::string& user_id_hash, 1061 base::WeakPtr<WallpaperManager> weak_ptr) { 1062 1063 if (MoveCustomWallpaperDirectory( 1064 kOriginalWallpaperSubDir, user_id, user_id_hash)) { 1065 // Consider success if the original wallpaper is moved to the new directory. 1066 // Original wallpaper is the fallback if the correct resolution wallpaper 1067 // can not be found. 1068 BrowserThread::PostTask( 1069 BrowserThread::UI, 1070 FROM_HERE, 1071 base::Bind(&WallpaperManager::MoveCustomWallpapersSuccess, 1072 weak_ptr, 1073 user_id, 1074 user_id_hash)); 1075 } 1076 MoveCustomWallpaperDirectory(kLargeWallpaperSubDir, user_id, user_id_hash); 1077 MoveCustomWallpaperDirectory(kSmallWallpaperSubDir, user_id, user_id_hash); 1078 MoveCustomWallpaperDirectory( 1079 kThumbnailWallpaperSubDir, user_id, user_id_hash); 1080 } 1081 1082 // static 1083 void WallpaperManager::GetCustomWallpaperInternal( 1084 const std::string& user_id, 1085 const WallpaperInfo& info, 1086 const base::FilePath& wallpaper_path, 1087 bool update_wallpaper, 1088 MovableOnDestroyCallbackHolder on_finish, 1089 base::WeakPtr<WallpaperManager> weak_ptr) { 1090 1091 base::FilePath valid_path = wallpaper_path; 1092 if (!base::PathExists(wallpaper_path)) { 1093 // Falls back on original file if the correct resolution file does not 1094 // exist. This may happen when the original custom wallpaper is small or 1095 // browser shutdown before resized wallpaper saved. 1096 valid_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir); 1097 valid_path = valid_path.Append(info.location); 1098 } 1099 1100 if (!base::PathExists(valid_path)) { 1101 // Falls back to custom wallpaper that uses email as part of its file path. 1102 // Note that email is used instead of user_id_hash here. 1103 valid_path = GetCustomWallpaperPath(kOriginalWallpaperSubDir, 1104 user_id, info.location); 1105 } 1106 1107 if (!base::PathExists(valid_path)) { 1108 LOG(ERROR) << "Failed to load previously selected custom wallpaper. " << 1109 "Fallback to default wallpaper"; 1110 BrowserThread::PostTask(BrowserThread::UI, 1111 FROM_HERE, 1112 base::Bind(&WallpaperManager::DoSetDefaultWallpaper, 1113 weak_ptr, 1114 user_id, 1115 base::Passed(on_finish.Pass()))); 1116 } else { 1117 BrowserThread::PostTask(BrowserThread::UI, 1118 FROM_HERE, 1119 base::Bind(&WallpaperManager::StartLoad, 1120 weak_ptr, 1121 user_id, 1122 info, 1123 update_wallpaper, 1124 valid_path, 1125 base::Passed(on_finish.Pass()))); 1126 } 1127 } 1128 1129 void WallpaperManager::InitInitialUserWallpaper(const std::string& user_id, 1130 bool is_persistent) { 1131 current_user_wallpaper_info_.location = ""; 1132 current_user_wallpaper_info_.layout = ash::WALLPAPER_LAYOUT_CENTER_CROPPED; 1133 current_user_wallpaper_info_.type = user_manager::User::DEFAULT; 1134 current_user_wallpaper_info_.date = base::Time::Now().LocalMidnight(); 1135 1136 WallpaperInfo info = current_user_wallpaper_info_; 1137 SetUserWallpaperInfo(user_id, info, is_persistent); 1138 } 1139 1140 void WallpaperManager::SetUserWallpaperInfo(const std::string& user_id, 1141 const WallpaperInfo& info, 1142 bool is_persistent) { 1143 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1144 current_user_wallpaper_info_ = info; 1145 if (!is_persistent) 1146 return; 1147 1148 PrefService* local_state = g_browser_process->local_state(); 1149 DictionaryPrefUpdate wallpaper_update(local_state, 1150 prefs::kUsersWallpaperInfo); 1151 1152 base::DictionaryValue* wallpaper_info_dict = new base::DictionaryValue(); 1153 wallpaper_info_dict->SetString(kNewWallpaperDateNodeName, 1154 base::Int64ToString(info.date.ToInternalValue())); 1155 wallpaper_info_dict->SetString(kNewWallpaperLocationNodeName, info.location); 1156 wallpaper_info_dict->SetInteger(kNewWallpaperLayoutNodeName, info.layout); 1157 wallpaper_info_dict->SetInteger(kNewWallpaperTypeNodeName, info.type); 1158 wallpaper_update->SetWithoutPathExpansion(user_id, wallpaper_info_dict); 1159 } 1160 1161 void WallpaperManager::SetUserWallpaperDelayed(const std::string& user_id) { 1162 ScheduleSetUserWallpaper(user_id, true); 1163 } 1164 1165 void WallpaperManager::SetUserWallpaperNow(const std::string& user_id) { 1166 ScheduleSetUserWallpaper(user_id, false); 1167 } 1168 1169 void WallpaperManager::ScheduleSetUserWallpaper(const std::string& user_id, 1170 bool delayed) { 1171 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1172 // Some unit tests come here without a UserManager or without a pref system. 1173 if (!user_manager::UserManager::IsInitialized() || 1174 !g_browser_process->local_state()) { 1175 return; 1176 } 1177 1178 // There is no visible background in kiosk mode. 1179 if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp()) 1180 return; 1181 // Guest user, regular user in ephemeral mode, or kiosk app. 1182 const user_manager::User* user = 1183 user_manager::UserManager::Get()->FindUser(user_id); 1184 if (user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral( 1185 user_id) || 1186 (user != NULL && user->GetType() == user_manager::USER_TYPE_KIOSK_APP)) { 1187 InitInitialUserWallpaper(user_id, false); 1188 GetPendingWallpaper(user_id, delayed)->ResetSetDefaultWallpaper(); 1189 return; 1190 } 1191 1192 if (!user_manager::UserManager::Get()->IsKnownUser(user_id)) 1193 return; 1194 1195 last_selected_user_ = user_id; 1196 1197 WallpaperInfo info; 1198 1199 if (!GetUserWallpaperInfo(user_id, &info)) { 1200 InitInitialUserWallpaper(user_id, true); 1201 GetUserWallpaperInfo(user_id, &info); 1202 } 1203 1204 gfx::ImageSkia user_wallpaper; 1205 current_user_wallpaper_info_ = info; 1206 if (GetWallpaperFromCache(user_id, &user_wallpaper)) { 1207 GetPendingWallpaper(user_id, delayed) 1208 ->ResetSetWallpaperImage(user_wallpaper, info); 1209 } else { 1210 if (info.location.empty()) { 1211 // Uses default built-in wallpaper when file is empty. Eventually, we 1212 // will only ship one built-in wallpaper in ChromeOS image. 1213 GetPendingWallpaper(user_id, delayed)->ResetSetDefaultWallpaper(); 1214 return; 1215 } 1216 1217 if (info.type == user_manager::User::CUSTOMIZED || 1218 info.type == user_manager::User::POLICY) { 1219 const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution(); 1220 // Wallpaper is not resized when layout is ash::WALLPAPER_LAYOUT_CENTER. 1221 // Original wallpaper should be used in this case. 1222 // TODO(bshe): Generates cropped custom wallpaper for CENTER layout. 1223 if (info.layout == ash::WALLPAPER_LAYOUT_CENTER) 1224 sub_dir = kOriginalWallpaperSubDir; 1225 base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir); 1226 wallpaper_path = wallpaper_path.Append(info.location); 1227 if (current_wallpaper_path_ == wallpaper_path) 1228 return; 1229 current_wallpaper_path_ = wallpaper_path; 1230 loaded_wallpapers_++; 1231 1232 GetPendingWallpaper(user_id, delayed) 1233 ->ResetSetCustomWallpaper(info, wallpaper_path); 1234 return; 1235 } 1236 1237 // Load downloaded ONLINE or converted DEFAULT wallpapers. 1238 GetPendingWallpaper(user_id, delayed)->ResetLoadWallpaper(info); 1239 } 1240 } 1241 1242 void WallpaperManager::SetWallpaperFromImageSkia(const std::string& user_id, 1243 const gfx::ImageSkia& image, 1244 ash::WallpaperLayout layout, 1245 bool update_wallpaper) { 1246 DCHECK(user_manager::UserManager::Get()->IsUserLoggedIn()); 1247 1248 // There is no visible background in kiosk mode. 1249 if (user_manager::UserManager::Get()->IsLoggedInAsKioskApp()) 1250 return; 1251 WallpaperInfo info; 1252 info.layout = layout; 1253 wallpaper_cache_[user_id] = image; 1254 1255 if (update_wallpaper) { 1256 GetPendingWallpaper(last_selected_user_, false /* Not delayed */) 1257 ->ResetSetWallpaperImage(image, info); 1258 } 1259 } 1260 1261 void WallpaperManager::UpdateWallpaper(bool clear_cache) { 1262 FOR_EACH_OBSERVER(Observer, observers_, OnUpdateWallpaperForTesting()); 1263 if (clear_cache) 1264 wallpaper_cache_.clear(); 1265 current_wallpaper_path_.clear(); 1266 // For GAIA login flow, the last_selected_user_ may not be set before user 1267 // login. If UpdateWallpaper is called at GAIA login screen, no wallpaper will 1268 // be set. It could result a black screen on external monitors. 1269 // See http://crbug.com/265689 for detail. 1270 if (last_selected_user_.empty()) { 1271 SetDefaultWallpaperNow(chromeos::login::kSignInUser); 1272 return; 1273 } 1274 SetUserWallpaperNow(last_selected_user_); 1275 } 1276 1277 void WallpaperManager::AddObserver(WallpaperManager::Observer* observer) { 1278 observers_.AddObserver(observer); 1279 } 1280 1281 void WallpaperManager::RemoveObserver(WallpaperManager::Observer* observer) { 1282 observers_.RemoveObserver(observer); 1283 } 1284 1285 void WallpaperManager::NotifyAnimationFinished() { 1286 FOR_EACH_OBSERVER( 1287 Observer, observers_, OnWallpaperAnimationFinished(last_selected_user_)); 1288 } 1289 1290 // WallpaperManager, private: -------------------------------------------------- 1291 1292 bool WallpaperManager::GetWallpaperFromCache(const std::string& user_id, 1293 gfx::ImageSkia* image) { 1294 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1295 CustomWallpaperMap::const_iterator it = wallpaper_cache_.find(user_id); 1296 if (it != wallpaper_cache_.end()) { 1297 *image = (*it).second; 1298 return true; 1299 } 1300 return false; 1301 } 1302 1303 void WallpaperManager::CacheUsersWallpapers() { 1304 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1305 user_manager::UserList users = user_manager::UserManager::Get()->GetUsers(); 1306 1307 if (!users.empty()) { 1308 user_manager::UserList::const_iterator it = users.begin(); 1309 // Skip the wallpaper of first user in the list. It should have been cached. 1310 it++; 1311 for (int cached = 0; 1312 it != users.end() && cached < kMaxWallpapersToCache; 1313 ++it, ++cached) { 1314 std::string user_id = (*it)->email(); 1315 CacheUserWallpaper(user_id); 1316 } 1317 } 1318 } 1319 1320 void WallpaperManager::CacheUserWallpaper(const std::string& user_id) { 1321 if (wallpaper_cache_.find(user_id) != wallpaper_cache_.end()) 1322 return; 1323 WallpaperInfo info; 1324 if (GetUserWallpaperInfo(user_id, &info)) { 1325 if (info.location.empty()) 1326 return; 1327 1328 base::FilePath wallpaper_dir; 1329 base::FilePath wallpaper_path; 1330 if (info.type == user_manager::User::CUSTOMIZED || 1331 info.type == user_manager::User::POLICY) { 1332 const char* sub_dir = GetCustomWallpaperSubdirForCurrentResolution(); 1333 base::FilePath wallpaper_path = GetCustomWallpaperDir(sub_dir); 1334 wallpaper_path = wallpaper_path.Append(info.location); 1335 task_runner_->PostTask( 1336 FROM_HERE, 1337 base::Bind(&WallpaperManager::GetCustomWallpaperInternal, 1338 user_id, 1339 info, 1340 wallpaper_path, 1341 false /* do not update wallpaper */, 1342 base::Passed(MovableOnDestroyCallbackHolder()), 1343 weak_factory_.GetWeakPtr())); 1344 return; 1345 } 1346 LoadWallpaper(user_id, 1347 info, 1348 false /* do not update wallpaper */, 1349 MovableOnDestroyCallbackHolder().Pass()); 1350 } 1351 } 1352 1353 void WallpaperManager::ClearObsoleteWallpaperPrefs() { 1354 PrefService* prefs = g_browser_process->local_state(); 1355 DictionaryPrefUpdate wallpaper_properties_pref(prefs, 1356 kUserWallpapersProperties); 1357 wallpaper_properties_pref->Clear(); 1358 DictionaryPrefUpdate wallpapers_pref(prefs, kUserWallpapers); 1359 wallpapers_pref->Clear(); 1360 } 1361 1362 void WallpaperManager::DeleteUserWallpapers(const std::string& user_id, 1363 const std::string& path_to_file) { 1364 std::vector<base::FilePath> file_to_remove; 1365 // Remove small user wallpaper. 1366 base::FilePath wallpaper_path = 1367 GetCustomWallpaperDir(kSmallWallpaperSubDir); 1368 // Remove old directory if exists 1369 file_to_remove.push_back(wallpaper_path.Append(user_id)); 1370 wallpaper_path = wallpaper_path.Append(path_to_file).DirName(); 1371 file_to_remove.push_back(wallpaper_path); 1372 1373 // Remove large user wallpaper. 1374 wallpaper_path = GetCustomWallpaperDir(kLargeWallpaperSubDir); 1375 file_to_remove.push_back(wallpaper_path.Append(user_id)); 1376 wallpaper_path = wallpaper_path.Append(path_to_file); 1377 file_to_remove.push_back(wallpaper_path); 1378 1379 // Remove user wallpaper thumbnail. 1380 wallpaper_path = GetCustomWallpaperDir(kThumbnailWallpaperSubDir); 1381 file_to_remove.push_back(wallpaper_path.Append(user_id)); 1382 wallpaper_path = wallpaper_path.Append(path_to_file); 1383 file_to_remove.push_back(wallpaper_path); 1384 1385 // Remove original user wallpaper. 1386 wallpaper_path = GetCustomWallpaperDir(kOriginalWallpaperSubDir); 1387 file_to_remove.push_back(wallpaper_path.Append(user_id)); 1388 wallpaper_path = wallpaper_path.Append(path_to_file); 1389 file_to_remove.push_back(wallpaper_path); 1390 1391 base::WorkerPool::PostTask( 1392 FROM_HERE, 1393 base::Bind(&DeleteWallpaperInList, file_to_remove), 1394 false); 1395 } 1396 1397 void WallpaperManager::SetCommandLineForTesting( 1398 base::CommandLine* command_line) { 1399 command_line_for_testing_ = command_line; 1400 SetDefaultWallpaperPathsFromCommandLine(command_line); 1401 } 1402 1403 CommandLine* WallpaperManager::GetCommandLine() { 1404 CommandLine* command_line = command_line_for_testing_ ? 1405 command_line_for_testing_ : CommandLine::ForCurrentProcess(); 1406 return command_line; 1407 } 1408 1409 void WallpaperManager::InitializeRegisteredDeviceWallpaper() { 1410 if (user_manager::UserManager::Get()->IsUserLoggedIn()) 1411 return; 1412 1413 bool disable_boot_animation = 1414 GetCommandLine()->HasSwitch(switches::kDisableBootAnimation); 1415 bool show_users = true; 1416 bool result = CrosSettings::Get()->GetBoolean( 1417 kAccountsPrefShowUserNamesOnSignIn, &show_users); 1418 DCHECK(result) << "Unable to fetch setting " 1419 << kAccountsPrefShowUserNamesOnSignIn; 1420 const user_manager::UserList& users = 1421 user_manager::UserManager::Get()->GetUsers(); 1422 int public_session_user_index = FindPublicSession(users); 1423 if ((!show_users && public_session_user_index == -1) || users.empty()) { 1424 // Boot into sign in form, preload default wallpaper. 1425 SetDefaultWallpaperDelayed(chromeos::login::kSignInUser); 1426 return; 1427 } 1428 1429 if (!disable_boot_animation) { 1430 int index = public_session_user_index != -1 ? public_session_user_index : 0; 1431 // Normal boot, load user wallpaper. 1432 // If normal boot animation is disabled wallpaper would be set 1433 // asynchronously once user pods are loaded. 1434 SetUserWallpaperDelayed(users[index]->email()); 1435 } 1436 } 1437 1438 void WallpaperManager::LoadWallpaper(const std::string& user_id, 1439 const WallpaperInfo& info, 1440 bool update_wallpaper, 1441 MovableOnDestroyCallbackHolder on_finish) { 1442 base::FilePath wallpaper_dir; 1443 base::FilePath wallpaper_path; 1444 1445 // Do a sanity check that file path information is not empty. 1446 if (info.type == user_manager::User::ONLINE || 1447 info.type == user_manager::User::DEFAULT) { 1448 if (info.location.empty()) { 1449 if (base::SysInfo::IsRunningOnChromeOS()) { 1450 NOTREACHED() << "User wallpaper info appears to be broken: " << user_id; 1451 } else { 1452 // Filename might be empty on debug configurations when stub users 1453 // were created directly in Local State (for testing). Ignore such 1454 // errors i.e. allowsuch type of debug configurations on the desktop. 1455 LOG(WARNING) << "User wallpaper info is empty: " << user_id; 1456 1457 // |on_finish| callback will get called on destruction. 1458 return; 1459 } 1460 } 1461 } 1462 1463 if (info.type == user_manager::User::ONLINE) { 1464 std::string file_name = GURL(info.location).ExtractFileName(); 1465 WallpaperResolution resolution = GetAppropriateResolution(); 1466 // Only solid color wallpapers have stretch layout and they have only one 1467 // resolution. 1468 if (info.layout != ash::WALLPAPER_LAYOUT_STRETCH && 1469 resolution == WALLPAPER_RESOLUTION_SMALL) { 1470 file_name = base::FilePath(file_name).InsertBeforeExtension( 1471 kSmallWallpaperSuffix).value(); 1472 } 1473 CHECK(PathService::Get(chrome::DIR_CHROMEOS_WALLPAPERS, &wallpaper_dir)); 1474 wallpaper_path = wallpaper_dir.Append(file_name); 1475 if (current_wallpaper_path_ == wallpaper_path) 1476 return; 1477 1478 if (update_wallpaper) 1479 current_wallpaper_path_ = wallpaper_path; 1480 1481 loaded_wallpapers_++; 1482 StartLoad( 1483 user_id, info, update_wallpaper, wallpaper_path, on_finish.Pass()); 1484 } else if (info.type == user_manager::User::DEFAULT) { 1485 // Default wallpapers are migrated from M21 user profiles. A code refactor 1486 // overlooked that case and caused these wallpapers not being loaded at all. 1487 // On some slow devices, it caused login webui not visible after upgrade to 1488 // M26 from M21. See crosbug.com/38429 for details. 1489 base::FilePath user_data_dir; 1490 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); 1491 wallpaper_path = user_data_dir.Append(info.location); 1492 StartLoad( 1493 user_id, info, update_wallpaper, wallpaper_path, on_finish.Pass()); 1494 } else { 1495 // In unexpected cases, revert to default wallpaper to fail safely. See 1496 // crosbug.com/38429. 1497 LOG(ERROR) << "Wallpaper reverts to default unexpected."; 1498 DoSetDefaultWallpaper(user_id, on_finish.Pass()); 1499 } 1500 } 1501 1502 bool WallpaperManager::GetUserWallpaperInfo(const std::string& user_id, 1503 WallpaperInfo* info) const { 1504 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1505 1506 if (user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral( 1507 user_id)) { 1508 // Default to the values cached in memory. 1509 *info = current_user_wallpaper_info_; 1510 1511 // Ephemeral users do not save anything to local state. But we have got 1512 // wallpaper info from memory. Returns true. 1513 return true; 1514 } 1515 1516 const base::DictionaryValue* info_dict; 1517 if (!g_browser_process->local_state()-> 1518 GetDictionary(prefs::kUsersWallpaperInfo)-> 1519 GetDictionaryWithoutPathExpansion(user_id, &info_dict)) { 1520 return false; 1521 } 1522 1523 // Use temporary variables to keep |info| untouched in the error case. 1524 std::string location; 1525 if (!info_dict->GetString(kNewWallpaperLocationNodeName, &location)) 1526 return false; 1527 int layout; 1528 if (!info_dict->GetInteger(kNewWallpaperLayoutNodeName, &layout)) 1529 return false; 1530 int type; 1531 if (!info_dict->GetInteger(kNewWallpaperTypeNodeName, &type)) 1532 return false; 1533 std::string date_string; 1534 if (!info_dict->GetString(kNewWallpaperDateNodeName, &date_string)) 1535 return false; 1536 int64 date_val; 1537 if (!base::StringToInt64(date_string, &date_val)) 1538 return false; 1539 1540 info->location = location; 1541 info->layout = static_cast<ash::WallpaperLayout>(layout); 1542 info->type = static_cast<user_manager::User::WallpaperType>(type); 1543 info->date = base::Time::FromInternalValue(date_val); 1544 return true; 1545 } 1546 1547 void WallpaperManager::MoveCustomWallpapersSuccess( 1548 const std::string& user_id, 1549 const std::string& user_id_hash) { 1550 WallpaperInfo info; 1551 GetUserWallpaperInfo(user_id, &info); 1552 if (info.type == user_manager::User::CUSTOMIZED) { 1553 // New file field should include user id hash in addition to file name. 1554 // This is needed because at login screen, user id hash is not available. 1555 info.location = base::FilePath(user_id_hash).Append(info.location).value(); 1556 bool is_persistent = 1557 !user_manager::UserManager::Get()->IsUserNonCryptohomeDataEphemeral( 1558 user_id); 1559 SetUserWallpaperInfo(user_id, info, is_persistent); 1560 } 1561 } 1562 1563 void WallpaperManager::MoveLoggedInUserCustomWallpaper() { 1564 const user_manager::User* logged_in_user = 1565 user_manager::UserManager::Get()->GetLoggedInUser(); 1566 if (logged_in_user) { 1567 task_runner_->PostTask( 1568 FROM_HERE, 1569 base::Bind(&WallpaperManager::MoveCustomWallpapersOnWorker, 1570 logged_in_user->email(), 1571 logged_in_user->username_hash(), 1572 weak_factory_.GetWeakPtr())); 1573 } 1574 } 1575 1576 void WallpaperManager::OnWallpaperDecoded( 1577 const std::string& user_id, 1578 ash::WallpaperLayout layout, 1579 bool update_wallpaper, 1580 MovableOnDestroyCallbackHolder on_finish, 1581 const user_manager::UserImage& user_image) { 1582 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1583 TRACE_EVENT_ASYNC_END0("ui", "LoadAndDecodeWallpaper", this); 1584 1585 // If decoded wallpaper is empty, we have probably failed to decode the file. 1586 // Use default wallpaper in this case. 1587 if (user_image.image().isNull()) { 1588 // Updates user pref to default wallpaper. 1589 WallpaperInfo info = {"", ash::WALLPAPER_LAYOUT_CENTER_CROPPED, 1590 user_manager::User::DEFAULT, 1591 base::Time::Now().LocalMidnight()}; 1592 SetUserWallpaperInfo(user_id, info, true); 1593 1594 if (update_wallpaper) 1595 DoSetDefaultWallpaper(user_id, on_finish.Pass()); 1596 return; 1597 } 1598 1599 wallpaper_cache_[user_id] = user_image.image(); 1600 1601 if (update_wallpaper) { 1602 ash::Shell::GetInstance() 1603 ->desktop_background_controller() 1604 ->SetWallpaperImage(user_image.image(), layout); 1605 } 1606 } 1607 1608 void WallpaperManager::StartLoad(const std::string& user_id, 1609 const WallpaperInfo& info, 1610 bool update_wallpaper, 1611 const base::FilePath& wallpaper_path, 1612 MovableOnDestroyCallbackHolder on_finish) { 1613 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1614 TRACE_EVENT_ASYNC_BEGIN0("ui", "LoadAndDecodeWallpaper", this); 1615 1616 wallpaper_loader_->Start(wallpaper_path.value(), 1617 0, // Do not crop. 1618 base::Bind(&WallpaperManager::OnWallpaperDecoded, 1619 weak_factory_.GetWeakPtr(), 1620 user_id, 1621 info.layout, 1622 update_wallpaper, 1623 base::Passed(on_finish.Pass()))); 1624 } 1625 1626 void WallpaperManager::SaveLastLoadTime(const base::TimeDelta elapsed) { 1627 while (last_load_times_.size() >= kLastLoadsStatsMsMaxSize) 1628 last_load_times_.pop_front(); 1629 1630 if (elapsed > base::TimeDelta::FromMicroseconds(0)) { 1631 last_load_times_.push_back(elapsed); 1632 last_load_finished_at_ = base::Time::Now(); 1633 } 1634 } 1635 1636 base::TimeDelta WallpaperManager::GetWallpaperLoadDelay() const { 1637 base::TimeDelta delay; 1638 1639 if (last_load_times_.size() == 0) { 1640 delay = base::TimeDelta::FromMilliseconds(kLoadDefaultDelayMs); 1641 } else { 1642 delay = std::accumulate(last_load_times_.begin(), 1643 last_load_times_.end(), 1644 base::TimeDelta(), 1645 std::plus<base::TimeDelta>()) / 1646 last_load_times_.size(); 1647 } 1648 1649 if (delay < base::TimeDelta::FromMilliseconds(kLoadMinDelayMs)) 1650 delay = base::TimeDelta::FromMilliseconds(kLoadMinDelayMs); 1651 else if (delay > base::TimeDelta::FromMilliseconds(kLoadMaxDelayMs)) 1652 delay = base::TimeDelta::FromMilliseconds(kLoadMaxDelayMs); 1653 1654 // If we had ever loaded wallpaper, adjust wait delay by time since last load. 1655 if (!last_load_finished_at_.is_null()) { 1656 const base::TimeDelta interval = base::Time::Now() - last_load_finished_at_; 1657 if (interval > delay) 1658 delay = base::TimeDelta::FromMilliseconds(0); 1659 else if (interval > base::TimeDelta::FromMilliseconds(0)) 1660 delay -= interval; 1661 } 1662 return delay; 1663 } 1664 1665 void WallpaperManager::SetCustomizedDefaultWallpaperAfterCheck( 1666 const GURL& wallpaper_url, 1667 const base::FilePath& downloaded_file, 1668 scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files) { 1669 PrefService* pref_service = g_browser_process->local_state(); 1670 1671 std::string current_url = 1672 pref_service->GetString(prefs::kCustomizationDefaultWallpaperURL); 1673 if (current_url != wallpaper_url.spec() || !rescaled_files->AllSizesExist()) { 1674 DCHECK(rescaled_files->downloaded_exists()); 1675 1676 // Either resized images do not exist or cached version is incorrect. 1677 // Need to start resize again. 1678 wallpaper_loader_->Start( 1679 downloaded_file.value(), 1680 0, // Do not crop. 1681 base::Bind(&WallpaperManager::OnCustomizedDefaultWallpaperDecoded, 1682 weak_factory_.GetWeakPtr(), 1683 wallpaper_url, 1684 base::Passed(rescaled_files.Pass()))); 1685 } else { 1686 SetDefaultWallpaperPath(rescaled_files->path_rescaled_small(), 1687 scoped_ptr<gfx::ImageSkia>().Pass(), 1688 rescaled_files->path_rescaled_large(), 1689 scoped_ptr<gfx::ImageSkia>().Pass()); 1690 } 1691 } 1692 1693 void WallpaperManager::OnCustomizedDefaultWallpaperDecoded( 1694 const GURL& wallpaper_url, 1695 scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files, 1696 const user_manager::UserImage& wallpaper) { 1697 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1698 1699 // If decoded wallpaper is empty, we have probably failed to decode the file. 1700 if (wallpaper.image().isNull()) { 1701 LOG(WARNING) << "Failed to decode customized wallpaper."; 1702 return; 1703 } 1704 1705 wallpaper.image().EnsureRepsForSupportedScales(); 1706 scoped_ptr<gfx::ImageSkia> deep_copy(wallpaper.image().DeepCopy()); 1707 1708 scoped_ptr<bool> success(new bool(false)); 1709 scoped_ptr<gfx::ImageSkia> small_wallpaper_image(new gfx::ImageSkia); 1710 scoped_ptr<gfx::ImageSkia> large_wallpaper_image(new gfx::ImageSkia); 1711 1712 // TODO(bshe): This may break if RawImage becomes RefCountedMemory. 1713 base::Closure resize_closure = 1714 base::Bind(&WallpaperManager::ResizeCustomizedDefaultWallpaper, 1715 base::Passed(&deep_copy), 1716 wallpaper.raw_image(), 1717 base::Unretained(rescaled_files.get()), 1718 base::Unretained(success.get()), 1719 base::Unretained(small_wallpaper_image.get()), 1720 base::Unretained(large_wallpaper_image.get())); 1721 base::Closure on_resized_closure = 1722 base::Bind(&WallpaperManager::OnCustomizedDefaultWallpaperResized, 1723 weak_factory_.GetWeakPtr(), 1724 wallpaper_url, 1725 base::Passed(rescaled_files.Pass()), 1726 base::Passed(success.Pass()), 1727 base::Passed(small_wallpaper_image.Pass()), 1728 base::Passed(large_wallpaper_image.Pass())); 1729 1730 if (!task_runner_->PostTaskAndReply( 1731 FROM_HERE, resize_closure, on_resized_closure)) { 1732 LOG(WARNING) << "Failed to start Customized Wallpaper resize."; 1733 } 1734 } 1735 1736 void WallpaperManager::ResizeCustomizedDefaultWallpaper( 1737 scoped_ptr<gfx::ImageSkia> image, 1738 const user_manager::UserImage::RawImage& raw_image, 1739 const CustomizedWallpaperRescaledFiles* rescaled_files, 1740 bool* success, 1741 gfx::ImageSkia* small_wallpaper_image, 1742 gfx::ImageSkia* large_wallpaper_image) { 1743 *success = true; 1744 1745 *success &= ResizeAndSaveWallpaper(*image, 1746 rescaled_files->path_rescaled_small(), 1747 ash::WALLPAPER_LAYOUT_STRETCH, 1748 kSmallWallpaperMaxWidth, 1749 kSmallWallpaperMaxHeight, 1750 small_wallpaper_image); 1751 1752 *success &= ResizeAndSaveWallpaper(*image, 1753 rescaled_files->path_rescaled_large(), 1754 ash::WALLPAPER_LAYOUT_STRETCH, 1755 kLargeWallpaperMaxWidth, 1756 kLargeWallpaperMaxHeight, 1757 large_wallpaper_image); 1758 } 1759 1760 void WallpaperManager::OnCustomizedDefaultWallpaperResized( 1761 const GURL& wallpaper_url, 1762 scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files, 1763 scoped_ptr<bool> success, 1764 scoped_ptr<gfx::ImageSkia> small_wallpaper_image, 1765 scoped_ptr<gfx::ImageSkia> large_wallpaper_image) { 1766 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1767 DCHECK(rescaled_files); 1768 DCHECK(success.get()); 1769 if (!*success) { 1770 LOG(WARNING) << "Failed to save resized customized default wallpaper"; 1771 return; 1772 } 1773 PrefService* pref_service = g_browser_process->local_state(); 1774 pref_service->SetString(prefs::kCustomizationDefaultWallpaperURL, 1775 wallpaper_url.spec()); 1776 SetDefaultWallpaperPath(rescaled_files->path_rescaled_small(), 1777 small_wallpaper_image.Pass(), 1778 rescaled_files->path_rescaled_large(), 1779 large_wallpaper_image.Pass()); 1780 VLOG(1) << "Customized default wallpaper applied."; 1781 } 1782 1783 WallpaperManager::PendingWallpaper* WallpaperManager::GetPendingWallpaper( 1784 const std::string& user_id, 1785 bool delayed) { 1786 if (!pending_inactive_) { 1787 loading_.push_back(new WallpaperManager::PendingWallpaper( 1788 (delayed ? GetWallpaperLoadDelay() 1789 : base::TimeDelta::FromMilliseconds(0)), 1790 user_id)); 1791 pending_inactive_ = loading_.back().get(); 1792 } 1793 return pending_inactive_; 1794 } 1795 1796 void WallpaperManager::RemovePendingWallpaperFromList( 1797 PendingWallpaper* pending) { 1798 DCHECK(loading_.size() > 0); 1799 for (WallpaperManager::PendingList::iterator i = loading_.begin(); 1800 i != loading_.end(); 1801 ++i) { 1802 if (i->get() == pending) { 1803 loading_.erase(i); 1804 break; 1805 } 1806 } 1807 1808 if (loading_.empty()) 1809 FOR_EACH_OBSERVER(Observer, observers_, OnPendingListEmptyForTesting()); 1810 } 1811 1812 void WallpaperManager::SetCustomizedDefaultWallpaper( 1813 const GURL& wallpaper_url, 1814 const base::FilePath& downloaded_file, 1815 const base::FilePath& resized_directory) { 1816 // Should fail if this ever happens in tests. 1817 DCHECK(wallpaper_url.is_valid()); 1818 if (!wallpaper_url.is_valid()) { 1819 if (!wallpaper_url.is_empty()) { 1820 LOG(WARNING) << "Invalid Customized Wallpaper URL '" 1821 << wallpaper_url.spec() << "'"; 1822 } 1823 return; 1824 } 1825 std::string downloaded_file_name = downloaded_file.BaseName().value(); 1826 scoped_ptr<CustomizedWallpaperRescaledFiles> rescaled_files( 1827 new CustomizedWallpaperRescaledFiles( 1828 downloaded_file, 1829 resized_directory.Append(downloaded_file_name + 1830 kSmallWallpaperSuffix), 1831 resized_directory.Append(downloaded_file_name + 1832 kLargeWallpaperSuffix))); 1833 1834 base::Closure check_file_exists = rescaled_files->CreateCheckerClosure(); 1835 base::Closure on_checked_closure = 1836 base::Bind(&WallpaperManager::SetCustomizedDefaultWallpaperAfterCheck, 1837 weak_factory_.GetWeakPtr(), 1838 wallpaper_url, 1839 downloaded_file, 1840 base::Passed(rescaled_files.Pass())); 1841 if (!BrowserThread::PostBlockingPoolTaskAndReply( 1842 FROM_HERE, check_file_exists, on_checked_closure)) { 1843 LOG(WARNING) << "Failed to start check CheckCustomizedWallpaperFilesExist."; 1844 } 1845 } 1846 1847 size_t WallpaperManager::GetPendingListSizeForTesting() const { 1848 return loading_.size(); 1849 } 1850 1851 void WallpaperManager::SetDefaultWallpaperPathsFromCommandLine( 1852 base::CommandLine* command_line) { 1853 default_small_wallpaper_file_ = command_line->GetSwitchValuePath( 1854 ash::switches::kAshDefaultWallpaperSmall); 1855 default_large_wallpaper_file_ = command_line->GetSwitchValuePath( 1856 ash::switches::kAshDefaultWallpaperLarge); 1857 guest_small_wallpaper_file_ = 1858 command_line->GetSwitchValuePath(ash::switches::kAshGuestWallpaperSmall); 1859 guest_large_wallpaper_file_ = 1860 command_line->GetSwitchValuePath(ash::switches::kAshGuestWallpaperLarge); 1861 default_wallpaper_image_.reset(); 1862 } 1863 1864 void WallpaperManager::OnDefaultWallpaperDecoded( 1865 const base::FilePath& path, 1866 const ash::WallpaperLayout layout, 1867 scoped_ptr<user_manager::UserImage>* result_out, 1868 MovableOnDestroyCallbackHolder on_finish, 1869 const user_manager::UserImage& user_image) { 1870 result_out->reset(new user_manager::UserImage(user_image)); 1871 ash::Shell::GetInstance()->desktop_background_controller()->SetWallpaperImage( 1872 user_image.image(), layout); 1873 } 1874 1875 void WallpaperManager::StartLoadAndSetDefaultWallpaper( 1876 const base::FilePath& path, 1877 const ash::WallpaperLayout layout, 1878 MovableOnDestroyCallbackHolder on_finish, 1879 scoped_ptr<user_manager::UserImage>* result_out) { 1880 wallpaper_loader_->Start( 1881 path.value(), 1882 0, // Do not crop. 1883 base::Bind(&WallpaperManager::OnDefaultWallpaperDecoded, 1884 weak_factory_.GetWeakPtr(), 1885 path, 1886 layout, 1887 base::Unretained(result_out), 1888 base::Passed(on_finish.Pass()))); 1889 } 1890 1891 const char* WallpaperManager::GetCustomWallpaperSubdirForCurrentResolution() { 1892 WallpaperResolution resolution = GetAppropriateResolution(); 1893 return resolution == WALLPAPER_RESOLUTION_SMALL ? kSmallWallpaperSubDir 1894 : kLargeWallpaperSubDir; 1895 } 1896 1897 void WallpaperManager::SetDefaultWallpaperPath( 1898 const base::FilePath& default_small_wallpaper_file, 1899 scoped_ptr<gfx::ImageSkia> small_wallpaper_image, 1900 const base::FilePath& default_large_wallpaper_file, 1901 scoped_ptr<gfx::ImageSkia> large_wallpaper_image) { 1902 default_small_wallpaper_file_ = default_small_wallpaper_file; 1903 default_large_wallpaper_file_ = default_large_wallpaper_file; 1904 1905 ash::DesktopBackgroundController* dbc = 1906 ash::Shell::GetInstance()->desktop_background_controller(); 1907 1908 // |need_update_screen| is true if the previous default wallpaper is visible 1909 // now, so we need to update wallpaper on the screen. 1910 // 1911 // Layout is ignored here, so ash::WALLPAPER_LAYOUT_CENTER is used 1912 // as a placeholder only. 1913 const bool need_update_screen = 1914 default_wallpaper_image_.get() && 1915 dbc->WallpaperIsAlreadyLoaded(default_wallpaper_image_->image(), 1916 false /* compare_layouts */, 1917 ash::WALLPAPER_LAYOUT_CENTER); 1918 1919 default_wallpaper_image_.reset(); 1920 if (GetAppropriateResolution() == WALLPAPER_RESOLUTION_SMALL) { 1921 if (small_wallpaper_image) { 1922 default_wallpaper_image_.reset( 1923 new user_manager::UserImage(*small_wallpaper_image)); 1924 default_wallpaper_image_->set_file_path( 1925 default_small_wallpaper_file.value()); 1926 } 1927 } else { 1928 if (large_wallpaper_image) { 1929 default_wallpaper_image_.reset( 1930 new user_manager::UserImage(*large_wallpaper_image)); 1931 default_wallpaper_image_->set_file_path( 1932 default_large_wallpaper_file.value()); 1933 } 1934 } 1935 1936 if (need_update_screen) { 1937 DoSetDefaultWallpaper(std::string(), 1938 MovableOnDestroyCallbackHolder().Pass()); 1939 } 1940 } 1941 1942 void WallpaperManager::CreateSolidDefaultWallpaper() { 1943 loaded_wallpapers_++; 1944 SkBitmap bitmap; 1945 bitmap.allocN32Pixels(1, 1); 1946 bitmap.eraseColor(kDefaultWallpaperColor); 1947 const gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(bitmap); 1948 default_wallpaper_image_.reset(new user_manager::UserImage(image)); 1949 } 1950 1951 } // namespace chromeos 1952