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