• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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