1 // Copyright (c) 2011 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/download/download_file_manager.h"
6
7 #include "base/file_util.h"
8 #include "base/logging.h"
9 #include "base/stl_util-inl.h"
10 #include "base/task.h"
11 #include "base/utf_string_conversions.h"
12 #include "build/build_config.h"
13 #include "chrome/browser/download/download_manager.h"
14 #include "chrome/browser/download/download_util.h"
15 #include "chrome/browser/history/download_create_info.h"
16 #include "chrome/browser/net/chrome_url_request_context.h"
17 #include "chrome/browser/platform_util.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
20 #include "chrome/browser/tab_contents/tab_util.h"
21 #include "content/browser/browser_thread.h"
22 #include "content/browser/renderer_host/resource_dispatcher_host.h"
23 #include "content/browser/tab_contents/tab_contents.h"
24 #include "googleurl/src/gurl.h"
25 #include "net/base/io_buffer.h"
26
27 namespace {
28
29 // Throttle updates to the UI thread so that a fast moving download doesn't
30 // cause it to become unresponsive (in milliseconds).
31 const int kUpdatePeriodMs = 500;
32
DownloadManagerForRenderViewHost(int render_process_id,int render_view_id)33 DownloadManager* DownloadManagerForRenderViewHost(int render_process_id,
34 int render_view_id) {
35 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
36
37 TabContents* contents = tab_util::GetTabContentsByID(render_process_id,
38 render_view_id);
39 if (contents) {
40 Profile* profile = contents->profile();
41 if (profile)
42 return profile->GetDownloadManager();
43 }
44
45 return NULL;
46 }
47
48 } // namespace
49
DownloadFileManager(ResourceDispatcherHost * rdh)50 DownloadFileManager::DownloadFileManager(ResourceDispatcherHost* rdh)
51 : next_id_(0),
52 resource_dispatcher_host_(rdh) {
53 }
54
~DownloadFileManager()55 DownloadFileManager::~DownloadFileManager() {
56 DCHECK(downloads_.empty());
57 }
58
Shutdown()59 void DownloadFileManager::Shutdown() {
60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
61 BrowserThread::PostTask(
62 BrowserThread::FILE, FROM_HERE,
63 NewRunnableMethod(this, &DownloadFileManager::OnShutdown));
64 }
65
OnShutdown()66 void DownloadFileManager::OnShutdown() {
67 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
68 StopUpdateTimer();
69 STLDeleteValues(&downloads_);
70 }
71
CreateDownloadFile(DownloadCreateInfo * info,DownloadManager * download_manager,bool get_hash)72 void DownloadFileManager::CreateDownloadFile(DownloadCreateInfo* info,
73 DownloadManager* download_manager,
74 bool get_hash) {
75 VLOG(20) << __FUNCTION__ << "()" << " info = " << info->DebugString();
76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
77
78 scoped_ptr<DownloadFile>
79 download_file(new DownloadFile(info, download_manager));
80 if (!download_file->Initialize(get_hash)) {
81 BrowserThread::PostTask(
82 BrowserThread::IO, FROM_HERE,
83 NewRunnableFunction(&download_util::CancelDownloadRequest,
84 resource_dispatcher_host_,
85 info->child_id,
86 info->request_id));
87 delete info;
88 return;
89 }
90
91 DCHECK(GetDownloadFile(info->download_id) == NULL);
92 downloads_[info->download_id] = download_file.release();
93 // TODO(phajdan.jr): fix the duplication of path info below.
94 info->path = info->save_info.file_path;
95
96 // The file is now ready, we can un-pause the request and start saving data.
97 BrowserThread::PostTask(
98 BrowserThread::IO, FROM_HERE,
99 NewRunnableMethod(this, &DownloadFileManager::ResumeDownloadRequest,
100 info->child_id, info->request_id));
101
102 StartUpdateTimer();
103
104 BrowserThread::PostTask(
105 BrowserThread::UI, FROM_HERE,
106 NewRunnableMethod(download_manager,
107 &DownloadManager::StartDownload, info));
108 }
109
ResumeDownloadRequest(int child_id,int request_id)110 void DownloadFileManager::ResumeDownloadRequest(int child_id, int request_id) {
111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
112
113 // This balances the pause in DownloadResourceHandler::OnResponseStarted.
114 resource_dispatcher_host_->PauseRequest(child_id, request_id, false);
115 }
116
GetDownloadFile(int id)117 DownloadFile* DownloadFileManager::GetDownloadFile(int id) {
118 DownloadFileMap::iterator it = downloads_.find(id);
119 return it == downloads_.end() ? NULL : it->second;
120 }
121
StartUpdateTimer()122 void DownloadFileManager::StartUpdateTimer() {
123 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
124 if (!update_timer_.IsRunning()) {
125 update_timer_.Start(base::TimeDelta::FromMilliseconds(kUpdatePeriodMs),
126 this, &DownloadFileManager::UpdateInProgressDownloads);
127 }
128 }
129
StopUpdateTimer()130 void DownloadFileManager::StopUpdateTimer() {
131 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
132 update_timer_.Stop();
133 }
134
UpdateInProgressDownloads()135 void DownloadFileManager::UpdateInProgressDownloads() {
136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
137 for (DownloadFileMap::iterator i = downloads_.begin();
138 i != downloads_.end(); ++i) {
139 int id = i->first;
140 DownloadFile* download_file = i->second;
141 DownloadManager* manager = download_file->GetDownloadManager();
142 if (manager) {
143 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
144 NewRunnableMethod(manager, &DownloadManager::UpdateDownload,
145 id, download_file->bytes_so_far()));
146 }
147 }
148 }
149
150 // Called on the IO thread once the ResourceDispatcherHost has decided that a
151 // request is a download.
GetNextId()152 int DownloadFileManager::GetNextId() {
153 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
154 return next_id_++;
155 }
156
StartDownload(DownloadCreateInfo * info)157 void DownloadFileManager::StartDownload(DownloadCreateInfo* info) {
158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
159 DCHECK(info);
160
161 DownloadManager* manager = DownloadManagerForRenderViewHost(
162 info->child_id, info->render_view_id);
163 if (!manager) {
164 BrowserThread::PostTask(
165 BrowserThread::IO, FROM_HERE,
166 NewRunnableFunction(&download_util::CancelDownloadRequest,
167 resource_dispatcher_host_,
168 info->child_id,
169 info->request_id));
170 delete info;
171 return;
172 }
173
174 manager->CreateDownloadItem(info);
175
176 bool hash_needed = resource_dispatcher_host_->safe_browsing_service()->
177 DownloadBinHashNeeded();
178
179 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
180 NewRunnableMethod(this, &DownloadFileManager::CreateDownloadFile,
181 info, make_scoped_refptr(manager), hash_needed));
182 }
183
184 // We don't forward an update to the UI thread here, since we want to throttle
185 // the UI update rate via a periodic timer. If the user has cancelled the
186 // download (in the UI thread), we may receive a few more updates before the IO
187 // thread gets the cancel message: we just delete the data since the
188 // DownloadFile has been deleted.
UpdateDownload(int id,DownloadBuffer * buffer)189 void DownloadFileManager::UpdateDownload(int id, DownloadBuffer* buffer) {
190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
191 std::vector<DownloadBuffer::Contents> contents;
192 {
193 base::AutoLock auto_lock(buffer->lock);
194 contents.swap(buffer->contents);
195 }
196
197 DownloadFile* download = GetDownloadFile(id);
198 for (size_t i = 0; i < contents.size(); ++i) {
199 net::IOBuffer* data = contents[i].first;
200 const int data_len = contents[i].second;
201 if (download)
202 download->AppendDataToFile(data->data(), data_len);
203 data->Release();
204 }
205 }
206
OnResponseCompleted(int id,DownloadBuffer * buffer,int os_error,const std::string & security_info)207 void DownloadFileManager::OnResponseCompleted(
208 int id,
209 DownloadBuffer* buffer,
210 int os_error,
211 const std::string& security_info) {
212 VLOG(20) << __FUNCTION__ << "()" << " id = " << id
213 << " os_error = " << os_error
214 << " security_info = \"" << security_info << "\"";
215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
216 delete buffer;
217 DownloadFile* download = GetDownloadFile(id);
218 if (!download)
219 return;
220
221 download->Finish();
222
223 DownloadManager* download_manager = download->GetDownloadManager();
224 if (!download_manager) {
225 CancelDownload(id);
226 return;
227 }
228
229 std::string hash;
230 if (!download->GetSha256Hash(&hash))
231 hash.clear();
232
233 BrowserThread::PostTask(
234 BrowserThread::UI, FROM_HERE,
235 NewRunnableMethod(
236 download_manager, &DownloadManager::OnResponseCompleted,
237 id, download->bytes_so_far(), os_error, hash));
238 // We need to keep the download around until the UI thread has finalized
239 // the name.
240 }
241
242 // This method will be sent via a user action, or shutdown on the UI thread, and
243 // run on the download thread. Since this message has been sent from the UI
244 // thread, the download may have already completed and won't exist in our map.
CancelDownload(int id)245 void DownloadFileManager::CancelDownload(int id) {
246 VLOG(20) << __FUNCTION__ << "()" << " id = " << id;
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
248 DownloadFileMap::iterator it = downloads_.find(id);
249 if (it == downloads_.end())
250 return;
251
252 DownloadFile* download = it->second;
253 VLOG(20) << __FUNCTION__ << "()"
254 << " download = " << download->DebugString();
255 download->Cancel();
256
257 EraseDownload(id);
258 }
259
CompleteDownload(int id)260 void DownloadFileManager::CompleteDownload(int id) {
261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
262
263 if (!ContainsKey(downloads_, id))
264 return;
265
266 DownloadFile* download_file = downloads_[id];
267
268 VLOG(20) << " " << __FUNCTION__ << "()"
269 << " id = " << id
270 << " download_file = " << download_file->DebugString();
271
272 download_file->Detach();
273
274 EraseDownload(id);
275 }
276
OnDownloadManagerShutdown(DownloadManager * manager)277 void DownloadFileManager::OnDownloadManagerShutdown(DownloadManager* manager) {
278 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
279 DCHECK(manager);
280
281 std::set<DownloadFile*> to_remove;
282
283 for (DownloadFileMap::iterator i = downloads_.begin();
284 i != downloads_.end(); ++i) {
285 DownloadFile* download_file = i->second;
286 if (download_file->GetDownloadManager() == manager) {
287 download_file->CancelDownloadRequest(resource_dispatcher_host_);
288 to_remove.insert(download_file);
289 }
290 }
291
292 for (std::set<DownloadFile*>::iterator i = to_remove.begin();
293 i != to_remove.end(); ++i) {
294 downloads_.erase((*i)->id());
295 delete *i;
296 }
297 }
298
299 // Actions from the UI thread and run on the download thread
300
301 // The DownloadManager in the UI thread has provided an intermediate .crdownload
302 // name for the download specified by 'id'. Rename the in progress download.
303 //
304 // There are 2 possible rename cases where this method can be called:
305 // 1. tmp -> foo.crdownload (not final, safe)
306 // 2. tmp-> Unconfirmed.xxx.crdownload (not final, dangerous)
RenameInProgressDownloadFile(int id,const FilePath & full_path)307 void DownloadFileManager::RenameInProgressDownloadFile(
308 int id, const FilePath& full_path) {
309 VLOG(20) << __FUNCTION__ << "()" << " id = " << id
310 << " full_path = \"" << full_path.value() << "\"";
311 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
312
313 DownloadFile* download = GetDownloadFile(id);
314 if (!download)
315 return;
316
317 VLOG(20) << __FUNCTION__ << "()" << " download = " << download->DebugString();
318
319 if (!download->Rename(full_path)) {
320 // Error. Between the time the UI thread generated 'full_path' to the time
321 // this code runs, something happened that prevents us from renaming.
322 CancelDownloadOnRename(id);
323 }
324 }
325
326 // The DownloadManager in the UI thread has provided a final name for the
327 // download specified by 'id'. Rename the download that's in the process
328 // of completing.
329 //
330 // There are 2 possible rename cases where this method can be called:
331 // 1. foo.crdownload -> foo (final, safe)
332 // 2. Unconfirmed.xxx.crdownload -> xxx (final, validated)
RenameCompletingDownloadFile(int id,const FilePath & full_path,bool overwrite_existing_file)333 void DownloadFileManager::RenameCompletingDownloadFile(
334 int id, const FilePath& full_path, bool overwrite_existing_file) {
335 VLOG(20) << __FUNCTION__ << "()" << " id = " << id
336 << " overwrite_existing_file = " << overwrite_existing_file
337 << " full_path = \"" << full_path.value() << "\"";
338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
339
340 DownloadFile* download = GetDownloadFile(id);
341 if (!download)
342 return;
343
344 DCHECK(download->GetDownloadManager());
345 DownloadManager* download_manager = download->GetDownloadManager();
346
347 VLOG(20) << __FUNCTION__ << "()" << " download = " << download->DebugString();
348
349 int uniquifier = 0;
350 FilePath new_path = full_path;
351 if (!overwrite_existing_file) {
352 // Make our name unique at this point, as if a dangerous file is
353 // downloading and a 2nd download is started for a file with the same
354 // name, they would have the same path. This is because we uniquify
355 // the name on download start, and at that time the first file does
356 // not exists yet, so the second file gets the same name.
357 // This should not happen in the SAFE case, and we check for that in the UI
358 // thread.
359 uniquifier = download_util::GetUniquePathNumber(new_path);
360 if (uniquifier > 0) {
361 download_util::AppendNumberToPath(&new_path, uniquifier);
362 }
363 }
364
365 // Rename the file, overwriting if necessary.
366 if (!download->Rename(new_path)) {
367 // Error. Between the time the UI thread generated 'full_path' to the time
368 // this code runs, something happened that prevents us from renaming.
369 CancelDownloadOnRename(id);
370 return;
371 }
372
373 #if defined(OS_MACOSX)
374 // Done here because we only want to do this once; see
375 // http://crbug.com/13120 for details.
376 download->AnnotateWithSourceInformation();
377 #endif
378
379 BrowserThread::PostTask(
380 BrowserThread::UI, FROM_HERE,
381 NewRunnableMethod(
382 download_manager, &DownloadManager::OnDownloadRenamedToFinalName, id,
383 new_path, uniquifier));
384 }
385
386 // Called only from RenameInProgressDownloadFile and
387 // RenameCompletingDownloadFile on the FILE thread.
CancelDownloadOnRename(int id)388 void DownloadFileManager::CancelDownloadOnRename(int id) {
389 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
390
391 DownloadFile* download = GetDownloadFile(id);
392 if (!download)
393 return;
394
395 DownloadManager* download_manager = download->GetDownloadManager();
396 if (!download_manager) {
397 // Without a download manager, we can't cancel the request normally, so we
398 // need to do it here. The normal path will also update the download
399 // history before cancelling the request.
400 download->CancelDownloadRequest(resource_dispatcher_host_);
401 return;
402 }
403
404 BrowserThread::PostTask(
405 BrowserThread::UI, FROM_HERE,
406 NewRunnableMethod(download_manager,
407 &DownloadManager::DownloadCancelled, id));
408 }
409
EraseDownload(int id)410 void DownloadFileManager::EraseDownload(int id) {
411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
412
413 if (!ContainsKey(downloads_, id))
414 return;
415
416 DownloadFile* download_file = downloads_[id];
417
418 VLOG(20) << " " << __FUNCTION__ << "()"
419 << " id = " << id
420 << " download_file = " << download_file->DebugString();
421
422 downloads_.erase(id);
423
424 delete download_file;
425
426 if (downloads_.empty())
427 StopUpdateTimer();
428 }
429