1 // Copyright 2014 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_wallpaper_downloader.h" 6 7 #include <math.h> 8 #include <algorithm> 9 10 #include "base/file_util.h" 11 #include "content/public/browser/browser_thread.h" 12 #include "net/base/load_flags.h" 13 #include "net/http/http_status_code.h" 14 #include "net/url_request/url_fetcher.h" 15 #include "net/url_request/url_request_context_getter.h" 16 #include "url/gurl.h" 17 18 namespace chromeos { 19 namespace { 20 // This is temporary file suffix (for downloading or resizing). 21 const char kTemporarySuffix[] = ".tmp"; 22 23 // Sleep between wallpaper retries (used multiplied by squared retry number). 24 const unsigned kRetrySleepSeconds = 10; 25 26 // Retry is infinite with increasing intervals. When calculated delay becomes 27 // longer than maximum (kMaxRetrySleepSeconds) it is set to the maximum. 28 const double kMaxRetrySleepSeconds = 6 * 3600; // 6 hours 29 30 void CreateWallpaperDirectory(const base::FilePath& wallpaper_dir, 31 bool* success) { 32 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 33 DCHECK(success); 34 35 *success = CreateDirectoryAndGetError(wallpaper_dir, NULL); 36 if (!*success) { 37 NOTREACHED() << "Failed to create directory '" << wallpaper_dir.value() 38 << "'"; 39 } 40 } 41 42 void RenameTemporaryFile(const base::FilePath& from, 43 const base::FilePath& to, 44 bool* success) { 45 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 46 DCHECK(success); 47 48 base::File::Error error; 49 if (base::ReplaceFile(from, to, &error)) { 50 *success = true; 51 } else { 52 LOG(WARNING) 53 << "Failed to rename temporary file of Customized Wallpaper. error=" 54 << error; 55 *success = false; 56 } 57 } 58 59 } // namespace 60 61 CustomizationWallpaperDownloader::CustomizationWallpaperDownloader( 62 net::URLRequestContextGetter* url_context_getter, 63 const GURL& wallpaper_url, 64 const base::FilePath& wallpaper_dir, 65 const base::FilePath& wallpaper_downloaded_file, 66 base::Callback<void(bool success, const GURL&)> 67 on_wallpaper_fetch_completed) 68 : url_context_getter_(url_context_getter), 69 wallpaper_url_(wallpaper_url), 70 wallpaper_dir_(wallpaper_dir), 71 wallpaper_downloaded_file_(wallpaper_downloaded_file), 72 wallpaper_temporary_file_(wallpaper_downloaded_file.value() + 73 kTemporarySuffix), 74 retries_(0), 75 retry_delay_(base::TimeDelta::FromSeconds(kRetrySleepSeconds)), 76 on_wallpaper_fetch_completed_(on_wallpaper_fetch_completed), 77 weak_factory_(this) { 78 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 79 } 80 81 CustomizationWallpaperDownloader::~CustomizationWallpaperDownloader() { 82 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 83 } 84 85 void CustomizationWallpaperDownloader::StartRequest() { 86 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 87 DCHECK(wallpaper_url_.is_valid()); 88 89 url_fetcher_.reset( 90 net::URLFetcher::Create(wallpaper_url_, net::URLFetcher::GET, this)); 91 url_fetcher_->SetRequestContext(url_context_getter_); 92 url_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | 93 net::LOAD_DISABLE_CACHE | 94 net::LOAD_DO_NOT_SAVE_COOKIES | 95 net::LOAD_DO_NOT_SEND_COOKIES | 96 net::LOAD_DO_NOT_SEND_AUTH_DATA); 97 base::SequencedWorkerPool* blocking_pool = 98 content::BrowserThread::GetBlockingPool(); 99 url_fetcher_->SaveResponseToFileAtPath( 100 wallpaper_temporary_file_, 101 blocking_pool->GetSequencedTaskRunner(blocking_pool->GetSequenceToken())); 102 url_fetcher_->Start(); 103 } 104 105 void CustomizationWallpaperDownloader::Retry() { 106 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 107 ++retries_; 108 109 const double delay_seconds = std::min( 110 kMaxRetrySleepSeconds, 111 static_cast<double>(retries_) * retries_ * retry_delay_.InSecondsF()); 112 const base::TimeDelta delay = base::TimeDelta::FromSecondsD(delay_seconds); 113 114 VLOG(1) << "Schedule Customized Wallpaper download in " << delay.InSecondsF() 115 << " seconds (retry = " << retries_ << ")."; 116 retry_current_delay_ = delay; 117 request_scheduled_.Start( 118 FROM_HERE, delay, this, &CustomizationWallpaperDownloader::StartRequest); 119 } 120 121 void CustomizationWallpaperDownloader::Start() { 122 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 123 scoped_ptr<bool> success(new bool(false)); 124 125 base::Closure mkdir_closure = base::Bind(&CreateWallpaperDirectory, 126 wallpaper_dir_, 127 base::Unretained(success.get())); 128 base::Closure on_created_closure = 129 base::Bind(&CustomizationWallpaperDownloader::OnWallpaperDirectoryCreated, 130 weak_factory_.GetWeakPtr(), 131 base::Passed(success.Pass())); 132 if (!content::BrowserThread::PostBlockingPoolTaskAndReply( 133 FROM_HERE, mkdir_closure, on_created_closure)) { 134 LOG(WARNING) << "Failed to start Customized Wallpaper download."; 135 } 136 } 137 138 void CustomizationWallpaperDownloader::OnWallpaperDirectoryCreated( 139 scoped_ptr<bool> success) { 140 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 141 if (*success) 142 StartRequest(); 143 } 144 145 void CustomizationWallpaperDownloader::OnURLFetchComplete( 146 const net::URLFetcher* source) { 147 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 148 DCHECK_EQ(url_fetcher_.get(), source); 149 150 const net::URLRequestStatus status = source->GetStatus(); 151 const int response_code = source->GetResponseCode(); 152 153 const bool server_error = 154 !status.is_success() || 155 (response_code >= net::HTTP_INTERNAL_SERVER_ERROR && 156 response_code < (net::HTTP_INTERNAL_SERVER_ERROR + 100)); 157 158 VLOG(1) << "CustomizationWallpaperDownloader::OnURLFetchComplete(): status=" 159 << status.status(); 160 161 if (server_error) { 162 url_fetcher_.reset(); 163 Retry(); 164 return; 165 } 166 167 base::FilePath response_path; 168 url_fetcher_->GetResponseAsFilePath(true, &response_path); 169 url_fetcher_.reset(); 170 171 scoped_ptr<bool> success(new bool(false)); 172 173 base::Closure rename_closure = base::Bind(&RenameTemporaryFile, 174 response_path, 175 wallpaper_downloaded_file_, 176 base::Unretained(success.get())); 177 base::Closure on_rename_closure = 178 base::Bind(&CustomizationWallpaperDownloader::OnTemporaryFileRenamed, 179 weak_factory_.GetWeakPtr(), 180 base::Passed(success.Pass())); 181 if (!content::BrowserThread::PostBlockingPoolTaskAndReply( 182 FROM_HERE, rename_closure, on_rename_closure)) { 183 LOG(WARNING) 184 << "Failed to start Customized Wallpaper Rename DownloadedFile."; 185 on_wallpaper_fetch_completed_.Run(false, wallpaper_url_); 186 } 187 } 188 189 void CustomizationWallpaperDownloader::OnTemporaryFileRenamed( 190 scoped_ptr<bool> success) { 191 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 192 on_wallpaper_fetch_completed_.Run(*success, wallpaper_url_); 193 } 194 195 } // namespace chromeos 196