Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/file_util.h" | 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" | 6 #include "chrome/browser/extensions/api/image_writer_private/error_messages.h" |
| 9 #include "chrome/browser/extensions/api/image_writer_private/operation_manager.h " | 7 #include "chrome/browser/extensions/api/image_writer_private/operation_manager.h " |
| 10 #include "chrome/browser/extensions/api/image_writer_private/write_from_url_oper ation.h" | 8 #include "chrome/browser/extensions/api/image_writer_private/write_from_url_oper ation.h" |
| 11 #include "chrome/browser/profiles/profile.h" | |
| 12 #include "content/public/browser/browser_thread.h" | 9 #include "content/public/browser/browser_thread.h" |
| 13 #include "content/public/browser/download_manager.h" | 10 #include "net/url_request/url_fetcher.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 | 11 |
| 18 namespace extensions { | 12 namespace extensions { |
| 19 namespace image_writer { | 13 namespace image_writer { |
| 20 | 14 |
| 21 using content::BrowserThread; | 15 using content::BrowserThread; |
| 22 | 16 |
| 23 WriteFromUrlOperation::WriteFromUrlOperation( | 17 WriteFromUrlOperation::WriteFromUrlOperation( |
| 24 base::WeakPtr<OperationManager> manager, | 18 base::WeakPtr<OperationManager> manager, |
| 25 const ExtensionId& extension_id, | 19 const ExtensionId& extension_id, |
| 26 content::RenderViewHost* rvh, | 20 net::URLRequestContextGetter* request_context, |
| 27 GURL url, | 21 GURL url, |
| 28 const std::string& hash, | 22 const std::string& hash, |
| 29 bool saveImageAsDownload, | 23 bool saveImageAsDownload, |
| 30 const std::string& storage_unit_id) | 24 const std::string& device_path) |
| 31 : Operation(manager, extension_id, storage_unit_id), | 25 : Operation(manager, extension_id, device_path), |
| 32 rvh_(rvh), | 26 request_context_(request_context), |
| 33 url_(url), | 27 url_(url), |
| 34 hash_(hash), | 28 hash_(hash), |
| 35 saveImageAsDownload_(saveImageAsDownload), | 29 saveImageAsDownload_(saveImageAsDownload), |
| 36 download_stopped_(false), | 30 download_continuation_() { |
| 37 download_(NULL) { | |
| 38 } | 31 } |
| 39 | 32 |
| 40 WriteFromUrlOperation::~WriteFromUrlOperation() { | 33 WriteFromUrlOperation::~WriteFromUrlOperation() { |
| 41 } | 34 } |
| 42 | 35 |
| 43 void WriteFromUrlOperation::Start() { | 36 void WriteFromUrlOperation::Start() { |
| 44 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 37 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 45 | 38 |
| 46 SetStage(image_writer_api::STAGE_DOWNLOAD); | 39 GetDownloadTarget( |
|
tbarzic
2014/02/07 22:06:42
I like this cascading :)
Drew Haven
2014/02/11 00:50:15
I like that it makes the stages of an operation ve
| |
| 47 | 40 base::Bind( |
| 48 if (saveImageAsDownload_){ | 41 &WriteFromUrlOperation::Download, |
| 49 BrowserThread::PostTask( | 42 this, |
| 50 BrowserThread::UI, | 43 base::Bind( |
| 51 FROM_HERE, | 44 &WriteFromUrlOperation::VerifyDownload, |
| 52 base::Bind(&WriteFromUrlOperation::DownloadStart, this)); | 45 this, |
| 53 } else { | 46 base::Bind( |
| 54 BrowserThread::PostTask( | 47 &WriteFromUrlOperation::Unzip, |
| 55 BrowserThread::FILE, | 48 this, |
| 56 FROM_HERE, | 49 base::Bind( |
| 57 base::Bind(&WriteFromUrlOperation::CreateTempFile, this)); | 50 &WriteFromUrlOperation::Write, |
| 58 } | 51 this, |
| 59 | 52 base::Bind( |
| 60 AddCleanUpFunction(base::Bind(&WriteFromUrlOperation::DownloadCleanUp, this)); | 53 &WriteFromUrlOperation::VerifyWrite, |
| 54 this, | |
| 55 base::Bind( | |
| 56 &WriteFromUrlOperation::Finish, this))))))); | |
| 61 } | 57 } |
| 62 | 58 |
| 63 void WriteFromUrlOperation::CreateTempFile() { | 59 void WriteFromUrlOperation::GetDownloadTarget( |
| 60 const base::Closure& continuation) { | |
| 61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 64 if (IsCancelled()) { | 62 if (IsCancelled()) { |
| 65 return; | 63 return; |
| 66 } | 64 } |
| 67 | 65 |
| 68 tmp_file_.reset(new base::FilePath()); | 66 if (saveImageAsDownload_) { |
| 69 | 67 Error("Not supported yet."); |
| 70 if (base::CreateTemporaryFile(tmp_file_.get())) { | 68 return; |
| 71 BrowserThread::PostTask( | |
| 72 BrowserThread::UI, | |
| 73 FROM_HERE, | |
| 74 base::Bind(&WriteFromUrlOperation::DownloadStart, this)); | |
| 75 } else { | 69 } else { |
| 76 Error(error::kTempFileError); | 70 if (url_.ExtractFileName() == "") { |
| 71 if (base::CreateTemporaryFileInDir(temp_dir_.path(), &image_path_)) { | |
| 72 continuation.Run(); | |
| 73 } else { | |
| 74 Error(error::kTempFileError); | |
| 75 } | |
| 76 } else { | |
| 77 #if defined(OS_WIN) | |
| 78 base::FilePath file_name = | |
| 79 base::FilePath::FromUTF16Unsafe(url_.ExtractFileName()); | |
| 80 #else | |
| 81 base::FilePath file_name = base::FilePath(url_.ExtractFileName()); | |
| 82 #endif | |
| 83 image_path_ = temp_dir_.path().Append(file_name); | |
| 84 continuation.Run(); | |
| 85 } | |
| 77 } | 86 } |
| 78 } | 87 } |
| 79 | 88 |
| 80 // The downloader runs on the UI thread. | 89 // The downloader runs on the UI thread. |
| 81 void WriteFromUrlOperation::DownloadStart() { | 90 void WriteFromUrlOperation::Download(const base::Closure& continuation) { |
| 82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 83 | 92 |
| 84 if (download_stopped_) { | 93 if (IsCancelled()) { |
| 85 return; | 94 return; |
| 86 } | 95 } |
| 87 | 96 |
| 88 DVLOG(1) << "Starting download of URL: " << url_; | 97 download_continuation_ = continuation; |
| 89 | 98 |
| 90 Profile* current_profile = manager_->profile(); | 99 SetStage(image_writer_api::STAGE_DOWNLOAD); |
| 91 | 100 |
| 92 scoped_ptr<content::DownloadUrlParameters> download_params( | 101 // Store the URL fetcher on this object so that it is destroyed before this |
| 93 new content::DownloadUrlParameters( | 102 // object is. |
| 94 url_, | 103 url_fetcher_.reset(net::URLFetcher::Create(url_, net::URLFetcher::GET, this)); |
| 95 rvh_->GetProcess()->GetID(), | |
| 96 rvh_->GetRoutingID(), | |
| 97 current_profile->GetResourceContext())); | |
| 98 | 104 |
| 99 if (tmp_file_.get()) { | 105 url_fetcher_->SetRequestContext(request_context_); |
| 100 download_params->set_file_path(*tmp_file_); | 106 url_fetcher_->SaveResponseToFileAtPath( |
| 101 } | 107 image_path_, |
| 108 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); | |
| 102 | 109 |
| 103 download_params->set_callback( | 110 url_fetcher_->Start(); |
| 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 } | 111 } |
| 110 | 112 |
| 111 void WriteFromUrlOperation::OnDownloadStarted( | 113 void WriteFromUrlOperation::OnURLFetchUploadProgress( |
| 112 content::DownloadItem* item, | 114 const net::URLFetcher* source, |
| 113 content::DownloadInterruptReason interrupt_reason) { | 115 int64 current, |
| 114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 116 int64 total) { |
| 117 // No-op | |
| 118 } | |
| 115 | 119 |
| 116 if (download_stopped_) { | 120 void WriteFromUrlOperation::OnURLFetchDownloadProgress( |
| 117 // At this point DownloadCleanUp was called but the |download_| wasn't | 121 const net::URLFetcher* source, |
| 118 // stored yet and still hasn't been cancelled. | 122 int64 current, |
| 119 item->Cancel(true); | 123 int64 total) { |
| 120 return; | 124 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 125 | |
| 126 if (IsCancelled()) { | |
| 127 url_fetcher_.reset(NULL); | |
| 121 } | 128 } |
| 129 SetProgress(kProgressComplete * current / total); | |
| 130 } | |
| 122 | 131 |
| 123 if (item) { | 132 void WriteFromUrlOperation::OnURLFetchComplete(const net::URLFetcher* source) { |
| 124 DCHECK_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); | 133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 125 | 134 |
| 126 download_ = item; | 135 if (source->GetStatus().is_success() && source->GetResponseCode() == 200) { |
| 127 download_->AddObserver(this); | 136 SetProgress(kProgressComplete); |
| 128 | 137 |
| 129 // Run at least once. | 138 download_continuation_.Run(); |
| 130 OnDownloadUpdated(download_); | |
| 131 } else { | 139 } else { |
| 132 DCHECK_NE(content::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); | 140 Error(error::kDownloadInterrupted); |
| 133 std::string error_message = ErrorUtils::FormatErrorMessage( | |
| 134 "Download failed: *", | |
| 135 content::DownloadInterruptReasonToString(interrupt_reason)); | |
| 136 Error(error_message); | |
| 137 } | 141 } |
| 138 } | 142 } |
| 139 | 143 |
| 140 // Always called from the UI thread. | 144 void WriteFromUrlOperation::VerifyDownload( |
| 141 void WriteFromUrlOperation::OnDownloadUpdated(content::DownloadItem* download) { | 145 const base::Closure& continuation) { |
| 142 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 143 | |
| 144 if (download_stopped_) { | |
| 145 return; | |
| 146 } | |
| 147 | |
| 148 SetProgress(download->PercentComplete()); | |
| 149 | |
| 150 if (download->GetState() == content::DownloadItem::COMPLETE) { | |
| 151 download_path_ = download_->GetTargetFilePath(); | |
| 152 | |
| 153 download_->RemoveObserver(this); | |
| 154 download_ = NULL; | |
| 155 | |
| 156 BrowserThread::PostTask( | |
| 157 BrowserThread::FILE, | |
| 158 FROM_HERE, | |
| 159 base::Bind(&WriteFromUrlOperation::DownloadComplete, this)); | |
| 160 | |
| 161 } else if (download->GetState() == content::DownloadItem::INTERRUPTED) { | |
| 162 Error(error::kDownloadInterrupted); | |
| 163 } else if (download->GetState() == content::DownloadItem::CANCELLED) { | |
| 164 Error(error::kDownloadCancelled); | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 void WriteFromUrlOperation::DownloadComplete() { | |
| 169 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 170 DVLOG(1) << "Download complete."; | |
| 171 | |
| 172 SetProgress(kProgressComplete); | |
| 173 | |
| 174 VerifyDownloadStart(); | |
| 175 } | |
| 176 | |
| 177 void WriteFromUrlOperation::DownloadCleanUp() { | |
| 178 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
| 179 BrowserThread::PostTask( | |
| 180 BrowserThread::UI, | |
| 181 FROM_HERE, | |
| 182 base::Bind(&WriteFromUrlOperation::DownloadCleanUp, this)); | |
| 183 return; | |
| 184 } | |
| 185 | |
| 186 download_stopped_ = true; | |
| 187 | |
| 188 if (download_) { | |
| 189 download_->RemoveObserver(this); | |
| 190 download_->Cancel(true); | |
| 191 download_ = NULL; | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 void WriteFromUrlOperation::VerifyDownloadStart() { | |
| 196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 197 | 147 |
| 198 if (IsCancelled()) { | 148 if (IsCancelled()) { |
| 199 return; | 149 return; |
| 200 } | 150 } |
| 201 | 151 |
| 202 // Skip verify if no hash. | 152 // Skip verify if no hash. |
| 203 if (hash_.empty()) { | 153 if (hash_.empty()) { |
| 204 scoped_ptr<base::FilePath> download_path( | 154 continuation.Run(); |
| 205 new base::FilePath(download_path_)); | |
| 206 UnzipStart(download_path.Pass()); | |
| 207 return; | 155 return; |
| 208 } | 156 } |
| 209 | 157 |
| 210 DVLOG(1) << "Download verification started."; | |
| 211 | |
| 212 SetStage(image_writer_api::STAGE_VERIFYDOWNLOAD); | 158 SetStage(image_writer_api::STAGE_VERIFYDOWNLOAD); |
| 213 | 159 |
| 214 BrowserThread::PostTask( | 160 BrowserThread::PostTask( |
| 215 BrowserThread::FILE, | 161 BrowserThread::FILE, |
| 216 FROM_HERE, | 162 FROM_HERE, |
| 217 base::Bind(&WriteFromUrlOperation::VerifyDownloadRun, this)); | 163 base::Bind(&WriteFromUrlOperation::VerifyDownloadRun, |
| 164 this, | |
| 165 continuation)); | |
| 218 } | 166 } |
| 219 | 167 |
| 220 void WriteFromUrlOperation::VerifyDownloadRun() { | 168 void WriteFromUrlOperation::VerifyDownloadRun( |
| 169 const base::Closure& continuation) { | |
| 221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 222 scoped_ptr<base::FilePath> download_path(new base::FilePath(download_path_)); | |
| 223 GetMD5SumOfFile( | 171 GetMD5SumOfFile( |
| 224 download_path.Pass(), | 172 image_path_, |
| 225 0, | 173 0, |
| 226 0, | 174 100, |
| 227 kProgressComplete, | 175 kProgressComplete, |
| 228 base::Bind(&WriteFromUrlOperation::VerifyDownloadCompare, this)); | 176 base::Bind(&WriteFromUrlOperation::VerifyDownloadCompare, |
| 177 this, | |
| 178 continuation)); | |
| 229 } | 179 } |
| 230 | 180 |
| 231 void WriteFromUrlOperation::VerifyDownloadCompare( | 181 void WriteFromUrlOperation::VerifyDownloadCompare( |
| 232 scoped_ptr<std::string> download_hash) { | 182 const base::Closure& continuation, |
| 183 const std::string& download_hash) { | |
| 233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 184 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 234 if (*download_hash != hash_) { | 185 if (download_hash != hash_) { |
| 235 Error(error::kDownloadHashError); | 186 Error(error::kDownloadHashError); |
| 236 return; | 187 return; |
| 237 } | 188 } |
| 238 | 189 |
| 239 BrowserThread::PostTask( | 190 BrowserThread::PostTask( |
| 240 BrowserThread::FILE, | 191 BrowserThread::FILE, |
| 241 FROM_HERE, | 192 FROM_HERE, |
| 242 base::Bind(&WriteFromUrlOperation::VerifyDownloadComplete, this)); | 193 base::Bind(&WriteFromUrlOperation::VerifyDownloadComplete, |
| 194 this, | |
| 195 continuation)); | |
| 243 } | 196 } |
| 244 | 197 |
| 245 void WriteFromUrlOperation::VerifyDownloadComplete() { | 198 void WriteFromUrlOperation::VerifyDownloadComplete( |
| 199 const base::Closure& continuation) { | |
| 246 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 247 if (IsCancelled()) { | 201 if (IsCancelled()) { |
| 248 return; | 202 return; |
| 249 } | 203 } |
| 250 | 204 |
| 251 DVLOG(1) << "Download verification complete."; | |
| 252 | |
| 253 SetProgress(kProgressComplete); | 205 SetProgress(kProgressComplete); |
| 254 | 206 |
| 255 scoped_ptr<base::FilePath> download_path(new base::FilePath(download_path_)); | 207 continuation.Run(); |
| 256 UnzipStart(download_path.Pass()); | |
| 257 } | 208 } |
| 258 | 209 |
| 259 } // namespace image_writer | 210 } // namespace image_writer |
| 260 } // namespace extensions | 211 } // namespace extensions |
| OLD | NEW |