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/files/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/wallpaper/wallpaper_manager.h" 16 #include "chrome/browser/profiles/profile.h" 17 #include "chrome/common/chrome_paths.h" 18 #include "chrome/common/extensions/extension_constants.h" 19 #include "chrome/common/pref_names.h" 20 #include "components/user_manager/user.h" 21 #include "components/user_manager/user_manager.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: 39 WallpaperFetcher() {} 40 41 virtual ~WallpaperFetcher() {} 42 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: 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 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 91 WallpaperSetWallpaperFunction::WallpaperSetWallpaperFunction() { 92 } 93 94 WallpaperSetWallpaperFunction::~WallpaperSetWallpaperFunction() { 95 } 96 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_ = user_manager::UserManager::Get()->GetLoggedInUser()->email(); 104 user_id_hash_ = 105 user_manager::UserManager::Get()->GetLoggedInUser()->username_hash(); 106 107 if (params_->details.data) { 108 StartDecode(*params_->details.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 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.filename); 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_ == user_manager::UserManager::Get()->GetActiveUser()->email(); 142 wallpaper_manager->SetCustomWallpaper(user_id_, 143 user_id_hash_, 144 params_->details.filename, 145 layout, 146 user_manager::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 (extension()->id() == extension_misc::kWallpaperManagerId) { 168 profile->GetPrefs()->SetString(prefs::kCurrentWallpaperAppName, 169 std::string()); 170 } else { 171 profile->GetPrefs()->SetString(prefs::kCurrentWallpaperAppName, 172 extension()->name()); 173 } 174 SendResponse(true); 175 } 176 } 177 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 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 208 void WallpaperSetWallpaperFunction::OnWallpaperFetched( 209 bool success, 210 const std::string& response) { 211 if (success) { 212 params_->details.data.reset(new std::string(response)); 213 StartDecode(*params_->details.data); 214 } else { 215 SetError(response); 216 SendResponse(false); 217 } 218 } 219