1 // Copyright 2017 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/customization/customization_wallpaper_util.h"
6
7 #include "base/bind.h"
8 #include "base/files/file_util.h"
9 #include "base/location.h"
10 #include "base/sequenced_task_runner.h"
11 #include "base/task/post_task.h"
12 #include "base/task/thread_pool.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/chromeos/customization/customization_document.h"
15 #include "chrome/browser/chromeos/login/users/avatar/user_image_loader.h"
16 #include "chrome/browser/ui/ash/wallpaper_controller_client.h"
17 #include "chrome/common/pref_names.h"
18 #include "components/prefs/pref_service.h"
19 #include "components/user_manager/user_image/user_image.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "ui/gfx/codec/jpeg_codec.h"
22 #include "ui/gfx/image/image_skia_operations.h"
23 #include "url/gurl.h"
24
25 namespace chromeos {
26
27 namespace {
28
29 // File path suffixes of resized wallpapers.
30 constexpr char kSmallWallpaperSuffix[] = "_small";
31 constexpr char kLargeWallpaperSuffix[] = "_large";
32
33 // Returns true if saving the resized |image| to |file_path| succeeded.
SaveResizedWallpaper(const gfx::ImageSkia & image,const gfx::Size & size,const base::FilePath & file_path)34 bool SaveResizedWallpaper(const gfx::ImageSkia& image,
35 const gfx::Size& size,
36 const base::FilePath& file_path) {
37 gfx::ImageSkia resized_image = gfx::ImageSkiaOperations::CreateResizedImage(
38 image, skia::ImageOperations::RESIZE_LANCZOS3, size);
39 scoped_refptr<base::RefCountedBytes> image_data = new base::RefCountedBytes();
40 gfx::JPEGCodec::Encode(*resized_image.bitmap(), 90 /*quality=*/,
41 &image_data->data());
42 size_t written_bytes = base::WriteFile(
43 file_path, image_data->front_as<const char>(), image_data->size());
44 return written_bytes == image_data->size();
45 }
46
47 // Returns true if both file paths exist.
CheckCustomizedWallpaperFilesExist(const base::FilePath & resized_small_path,const base::FilePath & resized_large_path)48 bool CheckCustomizedWallpaperFilesExist(
49 const base::FilePath& resized_small_path,
50 const base::FilePath& resized_large_path) {
51 return base::PathExists(resized_small_path) &&
52 base::PathExists(resized_large_path);
53 }
54
55 // Resizes and saves the customized default wallpapers.
ResizeAndSaveCustomizedDefaultWallpaper(gfx::ImageSkia image,const base::FilePath & resized_small_path,const base::FilePath & resized_large_path)56 bool ResizeAndSaveCustomizedDefaultWallpaper(
57 gfx::ImageSkia image,
58 const base::FilePath& resized_small_path,
59 const base::FilePath& resized_large_path) {
60 return SaveResizedWallpaper(image,
61 gfx::Size(ash::kSmallWallpaperMaxWidth,
62 ash::kSmallWallpaperMaxHeight),
63 resized_small_path) &&
64 SaveResizedWallpaper(image,
65 gfx::Size(ash::kLargeWallpaperMaxWidth,
66 ash::kLargeWallpaperMaxHeight),
67 resized_large_path);
68 }
69
70 // Checks the result of |ResizeAndSaveCustomizedDefaultWallpaper| and sends
71 // the paths to apply the wallpapers.
OnCustomizedDefaultWallpaperResizedAndSaved(const GURL & wallpaper_url,const base::FilePath & resized_small_path,const base::FilePath & resized_large_path,bool success)72 void OnCustomizedDefaultWallpaperResizedAndSaved(
73 const GURL& wallpaper_url,
74 const base::FilePath& resized_small_path,
75 const base::FilePath& resized_large_path,
76 bool success) {
77 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
78 if (!success) {
79 LOG(WARNING) << "Failed to save resized customized default wallpaper";
80 return;
81 }
82
83 g_browser_process->local_state()->SetString(
84 prefs::kCustomizationDefaultWallpaperURL, wallpaper_url.spec());
85 WallpaperControllerClient::Get()->SetCustomizedDefaultWallpaperPaths(
86 resized_small_path, resized_large_path);
87 VLOG(1) << "Customized default wallpaper applied.";
88 }
89
90 // Initiates resizing and saving the customized default wallpapers if decoding
91 // is successful.
OnCustomizedDefaultWallpaperDecoded(const GURL & wallpaper_url,const base::FilePath & resized_small_path,const base::FilePath & resized_large_path,std::unique_ptr<user_manager::UserImage> wallpaper)92 void OnCustomizedDefaultWallpaperDecoded(
93 const GURL& wallpaper_url,
94 const base::FilePath& resized_small_path,
95 const base::FilePath& resized_large_path,
96 std::unique_ptr<user_manager::UserImage> wallpaper) {
97 // Empty image indicates decode failure.
98 if (wallpaper->image().isNull()) {
99 LOG(WARNING) << "Failed to decode customized wallpaper.";
100 return;
101 }
102
103 wallpaper->image().EnsureRepsForSupportedScales();
104
105 scoped_refptr<base::SequencedTaskRunner> task_runner =
106 base::ThreadPool::CreateSequencedTaskRunner(
107 {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
108 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
109 base::PostTaskAndReplyWithResult(
110 task_runner.get(), FROM_HERE,
111 base::BindOnce(&ResizeAndSaveCustomizedDefaultWallpaper,
112 wallpaper->image().DeepCopy(), resized_small_path,
113 resized_large_path),
114 base::BindOnce(&OnCustomizedDefaultWallpaperResizedAndSaved,
115 wallpaper_url, resized_small_path, resized_large_path));
116 }
117
118 // If |both_sizes_exist| is false or the url doesn't match the current value,
119 // initiates image decoding, otherwise directly sends the paths.
SetCustomizedDefaultWallpaperAfterCheck(const GURL & wallpaper_url,const base::FilePath & file_path,const base::FilePath & resized_small_path,const base::FilePath & resized_large_path,bool both_sizes_exist)120 void SetCustomizedDefaultWallpaperAfterCheck(
121 const GURL& wallpaper_url,
122 const base::FilePath& file_path,
123 const base::FilePath& resized_small_path,
124 const base::FilePath& resized_large_path,
125 bool both_sizes_exist) {
126 const std::string current_url = g_browser_process->local_state()->GetString(
127 prefs::kCustomizationDefaultWallpaperURL);
128 if (both_sizes_exist && current_url == wallpaper_url.spec()) {
129 WallpaperControllerClient::Get()->SetCustomizedDefaultWallpaperPaths(
130 resized_small_path, resized_small_path);
131 } else {
132 // Either resized images do not exist or cached version is incorrect.
133 // Need to start decoding again.
134 scoped_refptr<base::SequencedTaskRunner> task_runner =
135 base::ThreadPool::CreateSequencedTaskRunner(
136 {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
137 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
138 user_image_loader::StartWithFilePath(
139 task_runner, file_path, ImageDecoder::DEFAULT_CODEC,
140 0, // Do not crop.
141 base::BindOnce(&OnCustomizedDefaultWallpaperDecoded, wallpaper_url,
142 resized_small_path, resized_large_path));
143 }
144 }
145
146 } // namespace
147
148 namespace customization_wallpaper_util {
149
StartSettingCustomizedDefaultWallpaper(const GURL & wallpaper_url,const base::FilePath & file_path)150 void StartSettingCustomizedDefaultWallpaper(const GURL& wallpaper_url,
151 const base::FilePath& file_path) {
152 // Should fail if this ever happens in tests.
153 DCHECK(wallpaper_url.is_valid());
154 if (!wallpaper_url.is_valid()) {
155 if (!wallpaper_url.is_empty()) {
156 LOG(WARNING) << "Invalid Customized Wallpaper URL '"
157 << wallpaper_url.spec() << "'";
158 }
159 return;
160 }
161
162 std::string downloaded_file_name = file_path.BaseName().value();
163 base::FilePath resized_small_path;
164 base::FilePath resized_large_path;
165 if (!GetCustomizedDefaultWallpaperPaths(&resized_small_path,
166 &resized_large_path)) {
167 return;
168 }
169
170 scoped_refptr<base::SequencedTaskRunner> task_runner =
171 base::ThreadPool::CreateSequencedTaskRunner(
172 {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
173 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
174 base::PostTaskAndReplyWithResult(
175 task_runner.get(), FROM_HERE,
176 base::BindOnce(&CheckCustomizedWallpaperFilesExist, resized_small_path,
177 resized_large_path),
178 base::BindOnce(&SetCustomizedDefaultWallpaperAfterCheck, wallpaper_url,
179 file_path, resized_small_path, resized_large_path));
180 }
181
GetCustomizedDefaultWallpaperPaths(base::FilePath * small_path_out,base::FilePath * large_path_out)182 bool GetCustomizedDefaultWallpaperPaths(base::FilePath* small_path_out,
183 base::FilePath* large_path_out) {
184 const base::FilePath default_downloaded_file_name =
185 ServicesCustomizationDocument::GetCustomizedWallpaperDownloadedFileName();
186 const base::FilePath default_cache_dir =
187 ServicesCustomizationDocument::GetCustomizedWallpaperCacheDir();
188 if (default_downloaded_file_name.empty() || default_cache_dir.empty()) {
189 LOG(ERROR) << "Unable to get customized default wallpaper paths.";
190 return false;
191 }
192 const std::string file_name = default_downloaded_file_name.BaseName().value();
193 *small_path_out = default_cache_dir.Append(file_name + kSmallWallpaperSuffix);
194 *large_path_out = default_cache_dir.Append(file_name + kLargeWallpaperSuffix);
195 return true;
196 }
197
ShouldUseCustomizedDefaultWallpaper()198 bool ShouldUseCustomizedDefaultWallpaper() {
199 PrefService* pref_service = g_browser_process->local_state();
200 return !pref_service->FindPreference(prefs::kCustomizationDefaultWallpaperURL)
201 ->IsDefaultValue();
202 }
203
204 } // namespace customization_wallpaper_util
205 } // namespace chromeos
206