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