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