1 // Copyright 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/extensions/wallpaper_api.h"
6
7 #include "ash/desktop_background/desktop_background_controller.h"
8 #include "base/file_util.h"
9 #include "base/lazy_instance.h"
10 #include "base/path_service.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/threading/worker_pool.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chromeos/login/users/user.h"
16 #include "chrome/browser/chromeos/login/users/user_manager.h"
17 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/chrome_paths.h"
20 #include "chrome/common/extensions/extension_constants.h"
21 #include "chrome/common/pref_names.h"
22 #include "net/base/load_flags.h"
23 #include "net/http/http_status_code.h"
24 #include "net/url_request/url_fetcher.h"
25 #include "net/url_request/url_fetcher_delegate.h"
26 #include "url/gurl.h"
27
28 using base::BinaryValue;
29 using content::BrowserThread;
30
31 typedef base::Callback<void(bool success, const std::string&)> FetchCallback;
32
33 namespace set_wallpaper = extensions::api::wallpaper::SetWallpaper;
34
35 namespace {
36
37 class WallpaperFetcher : public net::URLFetcherDelegate {
38 public:
WallpaperFetcher()39 WallpaperFetcher() {}
40
~WallpaperFetcher()41 virtual ~WallpaperFetcher() {}
42
FetchWallpaper(const GURL & url,FetchCallback callback)43 void FetchWallpaper(const GURL& url, FetchCallback callback) {
44 CancelPreviousFetch();
45 callback_ = callback;
46 url_fetcher_.reset(net::URLFetcher::Create(url,
47 net::URLFetcher::GET,
48 this));
49 url_fetcher_->SetRequestContext(
50 g_browser_process->system_request_context());
51 url_fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
52 url_fetcher_->Start();
53 }
54
55 private:
56 // URLFetcherDelegate overrides:
OnURLFetchComplete(const net::URLFetcher * source)57 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
58 DCHECK(url_fetcher_.get() == source);
59
60 bool success = source->GetStatus().is_success() &&
61 source->GetResponseCode() == net::HTTP_OK;
62 std::string response;
63 if (success) {
64 source->GetResponseAsString(&response);
65 } else {
66 response = base::StringPrintf(
67 "Downloading wallpaper %s failed. The response code is %d.",
68 source->GetOriginalURL().ExtractFileName().c_str(),
69 source->GetResponseCode());
70 }
71 url_fetcher_.reset();
72 callback_.Run(success, response);
73 }
74
CancelPreviousFetch()75 void CancelPreviousFetch() {
76 if (url_fetcher_.get()) {
77 callback_.Run(false, wallpaper_api_util::kCancelWallpaperMessage);
78 url_fetcher_.reset();
79 }
80 }
81
82 scoped_ptr<net::URLFetcher> url_fetcher_;
83 FetchCallback callback_;
84 };
85
86 base::LazyInstance<WallpaperFetcher> g_wallpaper_fetcher =
87 LAZY_INSTANCE_INITIALIZER;
88
89 } // namespace
90
WallpaperSetWallpaperFunction()91 WallpaperSetWallpaperFunction::WallpaperSetWallpaperFunction() {
92 }
93
~WallpaperSetWallpaperFunction()94 WallpaperSetWallpaperFunction::~WallpaperSetWallpaperFunction() {
95 }
96
RunAsync()97 bool WallpaperSetWallpaperFunction::RunAsync() {
98 DCHECK_CURRENTLY_ON(BrowserThread::UI);
99 params_ = set_wallpaper::Params::Create(*args_);
100 EXTENSION_FUNCTION_VALIDATE(params_);
101
102 // Gets email address and username hash while at UI thread.
103 user_id_ = chromeos::UserManager::Get()->GetLoggedInUser()->email();
104 user_id_hash_ =
105 chromeos::UserManager::Get()->GetLoggedInUser()->username_hash();
106
107 if (params_->details.wallpaper_data) {
108 StartDecode(*params_->details.wallpaper_data);
109 } else {
110 GURL wallpaper_url(*params_->details.url);
111 if (wallpaper_url.is_valid()) {
112 g_wallpaper_fetcher.Get().FetchWallpaper(
113 wallpaper_url,
114 base::Bind(&WallpaperSetWallpaperFunction::OnWallpaperFetched, this));
115 } else {
116 SetError("URL is invalid.");
117 SendResponse(false);
118 }
119 }
120 return true;
121 }
122
OnWallpaperDecoded(const gfx::ImageSkia & image)123 void WallpaperSetWallpaperFunction::OnWallpaperDecoded(
124 const gfx::ImageSkia& image) {
125 chromeos::WallpaperManager* wallpaper_manager =
126 chromeos::WallpaperManager::Get();
127 base::FilePath thumbnail_path = wallpaper_manager->GetCustomWallpaperPath(
128 chromeos::kThumbnailWallpaperSubDir,
129 user_id_hash_,
130 params_->details.name);
131
132 sequence_token_ = BrowserThread::GetBlockingPool()->
133 GetNamedSequenceToken(chromeos::kWallpaperSequenceTokenName);
134 scoped_refptr<base::SequencedTaskRunner> task_runner =
135 BrowserThread::GetBlockingPool()->
136 GetSequencedTaskRunnerWithShutdownBehavior(sequence_token_,
137 base::SequencedWorkerPool::BLOCK_SHUTDOWN);
138 ash::WallpaperLayout layout = wallpaper_api_util::GetLayoutEnum(
139 set_wallpaper::Params::Details::ToString(params_->details.layout));
140 bool update_wallpaper =
141 user_id_ == chromeos::UserManager::Get()->GetActiveUser()->email();
142 wallpaper_manager->SetCustomWallpaper(user_id_,
143 user_id_hash_,
144 params_->details.name,
145 layout,
146 chromeos::User::CUSTOMIZED,
147 image,
148 update_wallpaper);
149 unsafe_wallpaper_decoder_ = NULL;
150
151 if (params_->details.thumbnail) {
152 image.EnsureRepsForSupportedScales();
153 scoped_ptr<gfx::ImageSkia> deep_copy(image.DeepCopy());
154 // Generates thumbnail before call api function callback. We can then
155 // request thumbnail in the javascript callback.
156 task_runner->PostTask(
157 FROM_HERE,
158 base::Bind(&WallpaperSetWallpaperFunction::GenerateThumbnail,
159 this,
160 thumbnail_path,
161 base::Passed(deep_copy.Pass())));
162 } else {
163 // Save current extenion name. It will be displayed in the component
164 // wallpaper picker app. If current extension is the component wallpaper
165 // picker, set an empty string.
166 Profile* profile = Profile::FromBrowserContext(browser_context());
167 if (GetExtension()->id() == extension_misc::kWallpaperManagerId) {
168 profile->GetPrefs()->SetString(prefs::kCurrentWallpaperAppName,
169 std::string());
170 } else {
171 profile->GetPrefs()->SetString(prefs::kCurrentWallpaperAppName,
172 GetExtension()->name());
173 }
174 SendResponse(true);
175 }
176 }
177
GenerateThumbnail(const base::FilePath & thumbnail_path,scoped_ptr<gfx::ImageSkia> image)178 void WallpaperSetWallpaperFunction::GenerateThumbnail(
179 const base::FilePath& thumbnail_path, scoped_ptr<gfx::ImageSkia> image) {
180 DCHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread(
181 sequence_token_));
182 if (!base::PathExists(thumbnail_path.DirName()))
183 base::CreateDirectory(thumbnail_path.DirName());
184
185 scoped_refptr<base::RefCountedBytes> data;
186 chromeos::WallpaperManager::Get()->ResizeImage(
187 *image,
188 ash::WALLPAPER_LAYOUT_STRETCH,
189 chromeos::kWallpaperThumbnailWidth,
190 chromeos::kWallpaperThumbnailHeight,
191 &data,
192 NULL);
193 BrowserThread::PostTask(
194 BrowserThread::UI, FROM_HERE,
195 base::Bind(
196 &WallpaperSetWallpaperFunction::ThumbnailGenerated,
197 this, data));
198 }
199
ThumbnailGenerated(base::RefCountedBytes * data)200 void WallpaperSetWallpaperFunction::ThumbnailGenerated(
201 base::RefCountedBytes* data) {
202 BinaryValue* result = BinaryValue::CreateWithCopiedBuffer(
203 reinterpret_cast<const char*>(data->front()), data->size());
204 SetResult(result);
205 SendResponse(true);
206 }
207
OnWallpaperFetched(bool success,const std::string & response)208 void WallpaperSetWallpaperFunction::OnWallpaperFetched(
209 bool success,
210 const std::string& response) {
211 if (success) {
212 params_->details.wallpaper_data.reset(new std::string(response));
213 StartDecode(*params_->details.wallpaper_data);
214 } else {
215 SetError(response);
216 SendResponse(false);
217 }
218 }
219