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 } | |
39 | 31 |
40 WriteFromUrlOperation::~WriteFromUrlOperation() { | 32 WriteFromUrlOperation::~WriteFromUrlOperation() { |
41 } | 33 } |
42 | 34 |
43 void WriteFromUrlOperation::Start() { | 35 void WriteFromUrlOperation::Start() { |
44 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 36 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
45 | 37 |
46 SetStage(image_writer_api::STAGE_DOWNLOAD); | 38 Operation::Start(); |
47 | 39 |
48 if (saveImageAsDownload_){ | 40 GetDownloadTarget(base::Bind( |
49 BrowserThread::PostTask( | 41 &WriteFromUrlOperation::Download, |
50 BrowserThread::UI, | 42 this, |
51 FROM_HERE, | 43 base::Bind( |
52 base::Bind(&WriteFromUrlOperation::DownloadStart, this)); | 44 &WriteFromUrlOperation::VerifyDownload, |
53 } else { | 45 this, |
54 BrowserThread::PostTask( | 46 base::Bind( |
55 BrowserThread::FILE, | 47 &WriteFromUrlOperation::Unzip, |
56 FROM_HERE, | 48 this, |
57 base::Bind(&WriteFromUrlOperation::CreateTempFile, this)); | 49 base::Bind(&WriteFromUrlOperation::Write, |
58 } | 50 this, |
59 | 51 base::Bind(&WriteFromUrlOperation::VerifyWrite, |
60 AddCleanUpFunction(base::Bind(&WriteFromUrlOperation::DownloadCleanUp, this)); | 52 this, |
| 53 base::Bind(&WriteFromUrlOperation::Finish, |
| 54 this))))))); |
61 } | 55 } |
62 | 56 |
63 void WriteFromUrlOperation::CreateTempFile() { | 57 void WriteFromUrlOperation::GetDownloadTarget( |
| 58 const base::Closure& continuation) { |
| 59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
64 if (IsCancelled()) { | 60 if (IsCancelled()) { |
65 return; | 61 return; |
66 } | 62 } |
67 | 63 |
68 tmp_file_.reset(new base::FilePath()); | 64 if (url_.ExtractFileName() == "") { |
| 65 if (!base::CreateTemporaryFileInDir(temp_dir_.path(), &image_path_)) { |
| 66 Error(error::kTempFileError); |
| 67 return; |
| 68 } |
| 69 } else { |
| 70 #if defined(OS_WIN) |
| 71 base::FilePath file_name = |
| 72 base::FilePath::FromUTF16Unsafe(url_.ExtractFileName()); |
| 73 #else |
| 74 base::FilePath file_name = base::FilePath(url_.ExtractFileName()); |
| 75 #endif |
| 76 image_path_ = temp_dir_.path().Append(file_name); |
| 77 } |
69 | 78 |
70 if (base::CreateTemporaryFile(tmp_file_.get())) { | 79 continuation.Run(); |
71 BrowserThread::PostTask( | 80 } |
72 BrowserThread::UI, | 81 |
73 FROM_HERE, | 82 void WriteFromUrlOperation::Download(const base::Closure& continuation) { |
74 base::Bind(&WriteFromUrlOperation::DownloadStart, this)); | 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 84 |
| 85 if (IsCancelled()) { |
| 86 return; |
| 87 } |
| 88 |
| 89 download_continuation_ = continuation; |
| 90 |
| 91 SetStage(image_writer_api::STAGE_DOWNLOAD); |
| 92 |
| 93 // Store the URL fetcher on this object so that it is destroyed before this |
| 94 // object is. |
| 95 url_fetcher_.reset(net::URLFetcher::Create(url_, net::URLFetcher::GET, this)); |
| 96 |
| 97 url_fetcher_->SetRequestContext(request_context_); |
| 98 url_fetcher_->SaveResponseToFileAtPath( |
| 99 image_path_, |
| 100 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); |
| 101 |
| 102 AddCleanUpFunction( |
| 103 base::Bind(&WriteFromUrlOperation::DestroyUrlFetcher, this)); |
| 104 |
| 105 url_fetcher_->Start(); |
| 106 } |
| 107 |
| 108 void WriteFromUrlOperation::DestroyUrlFetcher() { url_fetcher_.reset(); } |
| 109 |
| 110 void WriteFromUrlOperation::OnURLFetchUploadProgress( |
| 111 const net::URLFetcher* source, |
| 112 int64 current, |
| 113 int64 total) { |
| 114 // No-op |
| 115 } |
| 116 |
| 117 void WriteFromUrlOperation::OnURLFetchDownloadProgress( |
| 118 const net::URLFetcher* source, |
| 119 int64 current, |
| 120 int64 total) { |
| 121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 122 |
| 123 if (IsCancelled()) { |
| 124 url_fetcher_.reset(NULL); |
| 125 } |
| 126 |
| 127 int progress = (kProgressComplete * current) / total; |
| 128 |
| 129 SetProgress(progress); |
| 130 } |
| 131 |
| 132 void WriteFromUrlOperation::OnURLFetchComplete(const net::URLFetcher* source) { |
| 133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 134 |
| 135 if (source->GetStatus().is_success() && source->GetResponseCode() == 200) { |
| 136 SetProgress(kProgressComplete); |
| 137 |
| 138 download_continuation_.Run(); |
| 139 |
| 140 // Remove the reference to ourselves in this closure. |
| 141 download_continuation_ = base::Closure(); |
75 } else { | 142 } else { |
76 Error(error::kTempFileError); | 143 Error(error::kDownloadInterrupted); |
77 } | 144 } |
78 } | 145 } |
79 | 146 |
80 // The downloader runs on the UI thread. | 147 void WriteFromUrlOperation::VerifyDownload(const base::Closure& continuation) { |
81 void 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 | |
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() { | |
196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 148 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
197 | 149 |
198 if (IsCancelled()) { | 150 if (IsCancelled()) { |
199 return; | 151 return; |
200 } | 152 } |
201 | 153 |
202 // Skip verify if no hash. | 154 // Skip verify if no hash. |
203 if (hash_.empty()) { | 155 if (hash_.empty()) { |
204 scoped_ptr<base::FilePath> download_path( | 156 continuation.Run(); |
205 new base::FilePath(download_path_)); | |
206 UnzipStart(download_path.Pass()); | |
207 return; | 157 return; |
208 } | 158 } |
209 | 159 |
210 DVLOG(1) << "Download verification started."; | |
211 | |
212 SetStage(image_writer_api::STAGE_VERIFYDOWNLOAD); | 160 SetStage(image_writer_api::STAGE_VERIFYDOWNLOAD); |
213 | 161 |
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_)); | |
223 GetMD5SumOfFile( | 162 GetMD5SumOfFile( |
224 download_path.Pass(), | 163 image_path_, |
225 0, | 164 0, |
226 0, | 165 0, |
227 kProgressComplete, | 166 kProgressComplete, |
228 base::Bind(&WriteFromUrlOperation::VerifyDownloadCompare, this)); | 167 base::Bind( |
| 168 &WriteFromUrlOperation::VerifyDownloadCompare, this, continuation)); |
229 } | 169 } |
230 | 170 |
231 void WriteFromUrlOperation::VerifyDownloadCompare( | 171 void WriteFromUrlOperation::VerifyDownloadCompare( |
232 scoped_ptr<std::string> download_hash) { | 172 const base::Closure& continuation, |
| 173 const std::string& download_hash) { |
233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
234 if (*download_hash != hash_) { | 175 if (download_hash != hash_) { |
235 Error(error::kDownloadHashError); | 176 Error(error::kDownloadHashError); |
236 return; | 177 return; |
237 } | 178 } |
238 | 179 |
239 BrowserThread::PostTask( | 180 BrowserThread::PostTask( |
240 BrowserThread::FILE, | 181 BrowserThread::FILE, |
241 FROM_HERE, | 182 FROM_HERE, |
242 base::Bind(&WriteFromUrlOperation::VerifyDownloadComplete, this)); | 183 base::Bind( |
| 184 &WriteFromUrlOperation::VerifyDownloadComplete, this, continuation)); |
243 } | 185 } |
244 | 186 |
245 void WriteFromUrlOperation::VerifyDownloadComplete() { | 187 void WriteFromUrlOperation::VerifyDownloadComplete( |
| 188 const base::Closure& continuation) { |
246 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
247 if (IsCancelled()) { | 190 if (IsCancelled()) { |
248 return; | 191 return; |
249 } | 192 } |
250 | 193 |
251 DVLOG(1) << "Download verification complete."; | |
252 | |
253 SetProgress(kProgressComplete); | 194 SetProgress(kProgressComplete); |
254 | 195 |
255 scoped_ptr<base::FilePath> download_path(new base::FilePath(download_path_)); | 196 continuation.Run(); |
256 UnzipStart(download_path.Pass()); | |
257 } | 197 } |
258 | 198 |
259 } // namespace image_writer | 199 } // namespace image_writer |
260 } // namespace extensions | 200 } // namespace extensions |
OLD | NEW |