Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(59)

Side by Side Diff: chrome/browser/download/download_file_manager.cc

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

Powered by Google App Engine
This is Rietveld 408576698