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 "base/file_util.h" 6#include "base/threading/worker_pool.h" 7#include "chrome/browser/browser_process.h" 8#include "chrome/browser/extensions/api/image_writer_private/error_messages.h" 9#include "chrome/browser/extensions/api/image_writer_private/operation_manager.h" 10#include "chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h" 11#include "chrome/browser/profiles/profile.h" 12#include "content/public/browser/browser_thread.h" 13#include "content/public/browser/download_manager.h" 14#include "content/public/browser/render_process_host.h" 15#include "content/public/browser/render_view_host.h" 16#include "extensions/common/error_utils.h" 17 18namespace extensions { 19namespace image_writer { 20 21using content::BrowserThread; 22 23WriteFromUrlOperation::WriteFromUrlOperation( 24 base::WeakPtr<OperationManager> manager, 25 const ExtensionId& extension_id, 26 content::RenderViewHost* rvh, 27 GURL url, 28 const std::string& hash, 29 bool saveImageAsDownload, 30 const std::string& storage_unit_id) 31 : Operation(manager, extension_id, storage_unit_id), 32 rvh_(rvh), 33 url_(url), 34 hash_(hash), 35 saveImageAsDownload_(saveImageAsDownload), 36 download_stopped_(false), 37 download_(NULL) { 38} 39 40WriteFromUrlOperation::~WriteFromUrlOperation() { 41} 42 43void WriteFromUrlOperation::Start() { 44 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 45 46 SetStage(image_writer_api::STAGE_DOWNLOAD); 47 48 if (saveImageAsDownload_){ 49 BrowserThread::PostTask( 50 BrowserThread::UI, 51 FROM_HERE, 52 base::Bind(&WriteFromUrlOperation::DownloadStart, this)); 53 } else { 54 BrowserThread::PostTask( 55 BrowserThread::FILE, 56 FROM_HERE, 57 base::Bind(&WriteFromUrlOperation::CreateTempFile, this)); 58 } 59 60 AddCleanUpFunction(base::Bind(&WriteFromUrlOperation::DownloadCleanUp, this)); 61} 62 63void WriteFromUrlOperation::CreateTempFile() { 64 if (IsCancelled()) { 65 return; 66 } 67 68 tmp_file_.reset(new base::FilePath()); 69 70 if (base::CreateTemporaryFile(tmp_file_.get())) { 71 BrowserThread::PostTask( 72 BrowserThread::UI, 73 FROM_HERE, 74 base::Bind(&WriteFromUrlOperation::DownloadStart, this)); 75 } else { 76 Error(error::kTempFileError); 77 } 78} 79 80// The downloader runs on the UI thread. 81void WriteFromUrlOperation::DownloadStart() { 82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 83 84 if (download_stopped_) { 85 return; 86 } 87 88 DVLOG(1) << "Starting download of URL: " << url_; 89 90 Profile* current_profile = manager_->profile(); 91 92 scoped_ptr<content::DownloadUrlParameters> download_params( 93 new content::DownloadUrlParameters( 94 url_, 95 rvh_->GetProcess()->GetID(), 96 rvh_->GetRoutingID(), 97 current_profile->GetResourceContext())); 98 99 if (tmp_file_.get()) { 100 download_params->set_file_path(*tmp_file_); 101 } 102 103 download_params->set_callback( 104 base::Bind(&WriteFromUrlOperation::OnDownloadStarted, this)); 105 106 content::DownloadManager* download_manager = 107 content::BrowserContext::GetDownloadManager(current_profile); 108 download_manager->DownloadUrl(download_params.Pass()); 109} 110 111void WriteFromUrlOperation::OnDownloadStarted(content::DownloadItem* item, 112 net::Error error) { 113 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 114 115 if (download_stopped_) { 116 // At this point DownloadCleanUp was called but the |download_| wasn't 117 // stored yet and still hasn't been cancelled. 118 item->Cancel(true); 119 return; 120 } 121 122 if (item) { 123 DCHECK_EQ(net::OK, error); 124 125 download_ = item; 126 download_->AddObserver(this); 127 128 // Run at least once. 129 OnDownloadUpdated(download_); 130 } else { 131 DCHECK_NE(net::OK, error); 132 std::string error_message = ErrorUtils::FormatErrorMessage( 133 "Download failed: *", 134 net::ErrorToString(error)); 135 Error(error_message); 136 } 137} 138 139// Always called from the UI thread. 140void WriteFromUrlOperation::OnDownloadUpdated(content::DownloadItem* download) { 141 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 142 143 if (download_stopped_) { 144 return; 145 } 146 147 SetProgress(download->PercentComplete()); 148 149 if (download->GetState() == content::DownloadItem::COMPLETE) { 150 download_path_ = download_->GetTargetFilePath(); 151 152 download_->RemoveObserver(this); 153 download_ = NULL; 154 155 BrowserThread::PostTask( 156 BrowserThread::FILE, 157 FROM_HERE, 158 base::Bind(&WriteFromUrlOperation::DownloadComplete, this)); 159 160 } else if (download->GetState() == content::DownloadItem::INTERRUPTED) { 161 Error(error::kDownloadInterrupted); 162 } else if (download->GetState() == content::DownloadItem::CANCELLED) { 163 Error(error::kDownloadCancelled); 164 } 165} 166 167void WriteFromUrlOperation::DownloadComplete() { 168 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 169 DVLOG(1) << "Download complete."; 170 171 SetProgress(kProgressComplete); 172 173 VerifyDownloadStart(); 174} 175 176void WriteFromUrlOperation::DownloadCleanUp() { 177 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 178 BrowserThread::PostTask( 179 BrowserThread::UI, 180 FROM_HERE, 181 base::Bind(&WriteFromUrlOperation::DownloadCleanUp, this)); 182 return; 183 } 184 185 download_stopped_ = true; 186 187 if (download_) { 188 download_->RemoveObserver(this); 189 download_->Cancel(true); 190 download_ = NULL; 191 } 192} 193 194void WriteFromUrlOperation::VerifyDownloadStart() { 195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 196 197 if (IsCancelled()) { 198 return; 199 } 200 201 // Skip verify if no hash. 202 if (hash_.empty()) { 203 scoped_ptr<base::FilePath> download_path( 204 new base::FilePath(download_path_)); 205 UnzipStart(download_path.Pass()); 206 return; 207 } 208 209 DVLOG(1) << "Download verification started."; 210 211 SetStage(image_writer_api::STAGE_VERIFYDOWNLOAD); 212 213 BrowserThread::PostTask( 214 BrowserThread::FILE, 215 FROM_HERE, 216 base::Bind(&WriteFromUrlOperation::VerifyDownloadRun, this)); 217} 218 219void WriteFromUrlOperation::VerifyDownloadRun() { 220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 221 scoped_ptr<base::FilePath> download_path(new base::FilePath(download_path_)); 222 GetMD5SumOfFile( 223 download_path.Pass(), 224 0, 225 0, 226 kProgressComplete, 227 base::Bind(&WriteFromUrlOperation::VerifyDownloadCompare, this)); 228} 229 230void WriteFromUrlOperation::VerifyDownloadCompare( 231 scoped_ptr<std::string> download_hash) { 232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 233 if (*download_hash != hash_) { 234 Error(error::kDownloadHashError); 235 return; 236 } 237 238 BrowserThread::PostTask( 239 BrowserThread::FILE, 240 FROM_HERE, 241 base::Bind(&WriteFromUrlOperation::VerifyDownloadComplete, this)); 242} 243 244void WriteFromUrlOperation::VerifyDownloadComplete() { 245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 246 if (IsCancelled()) { 247 return; 248 } 249 250 DVLOG(1) << "Download verification complete."; 251 252 SetProgress(kProgressComplete); 253 254 scoped_ptr<base::FilePath> download_path(new base::FilePath(download_path_)); 255 UnzipStart(download_path.Pass()); 256} 257 258} // namespace image_writer 259} // namespace extensions 260