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

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

Issue 7192016: chrome.experimental.downloads (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: merged db_handle, id; onCreated, onErased Created 9 years, 5 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
« no previous file with comments | « chrome/browser/download/download_manager.h ('k') | chrome/browser/download/download_prefs.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 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 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 "chrome/browser/download/download_manager.h" 5 #include "chrome/browser/download/download_manager.h"
6 6
7 #include "base/bind.h"
7 #include "base/callback.h" 8 #include "base/callback.h"
8 #include "base/file_util.h" 9 #include "base/file_util.h"
9 #include "base/i18n/case_conversion.h" 10 #include "base/i18n/case_conversion.h"
10 #include "base/logging.h" 11 #include "base/logging.h"
11 #include "base/path_service.h" 12 #include "base/path_service.h"
12 #include "base/rand_util.h" 13 #include "base/rand_util.h"
13 #include "base/stl_util-inl.h" 14 #include "base/stl_util-inl.h"
14 #include "base/stringprintf.h" 15 #include "base/stringprintf.h"
15 #include "base/sys_string_conversions.h" 16 #include "base/sys_string_conversions.h"
16 #include "base/task.h" 17 #include "base/task.h"
17 #include "base/utf_string_conversions.h" 18 #include "base/utf_string_conversions.h"
18 #include "build/build_config.h" 19 #include "build/build_config.h"
19 #include "chrome/browser/browser_process.h" 20 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/download/download_create_info.h" 21 #include "chrome/browser/download/download_create_info.h"
21 #include "chrome/browser/download/download_extensions.h" 22 #include "chrome/browser/download/download_extensions.h"
22 #include "chrome/browser/download/download_file_manager.h" 23 #include "chrome/browser/download/download_file_manager.h"
23 #include "chrome/browser/download/download_history.h" 24 #include "chrome/browser/download/download_history.h"
24 #include "chrome/browser/download/download_item.h" 25 #include "chrome/browser/download/download_item.h"
25 #include "chrome/browser/download/download_prefs.h" 26 #include "chrome/browser/download/download_prefs.h"
27 #include "chrome/browser/download/download_query.h"
26 #include "chrome/browser/download/download_request_handle.h" 28 #include "chrome/browser/download/download_request_handle.h"
27 #include "chrome/browser/download/download_safe_browsing_client.h" 29 #include "chrome/browser/download/download_safe_browsing_client.h"
28 #include "chrome/browser/download/download_status_updater.h" 30 #include "chrome/browser/download/download_status_updater.h"
29 #include "chrome/browser/download/download_util.h" 31 #include "chrome/browser/download/download_util.h"
30 #include "chrome/browser/extensions/extension_service.h" 32 #include "chrome/browser/extensions/extension_service.h"
31 #include "chrome/browser/history/download_history_info.h" 33 #include "chrome/browser/history/download_history_info.h"
32 #include "chrome/browser/platform_util.h" 34 #include "chrome/browser/platform_util.h"
33 #include "chrome/browser/profiles/profile.h" 35 #include "chrome/browser/profiles/profile.h"
34 #include "chrome/browser/tab_contents/tab_util.h" 36 #include "chrome/browser/tab_contents/tab_util.h"
35 #include "chrome/browser/ui/browser.h" 37 #include "chrome/browser/ui/browser.h"
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
77 79
78 if (file_manager_) { 80 if (file_manager_) {
79 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 81 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
80 NewRunnableMethod(file_manager_, 82 NewRunnableMethod(file_manager_,
81 &DownloadFileManager::OnDownloadManagerShutdown, 83 &DownloadFileManager::OnDownloadManagerShutdown,
82 make_scoped_refptr(this))); 84 make_scoped_refptr(this)));
83 } 85 }
84 86
85 AssertContainersConsistent(); 87 AssertContainersConsistent();
86 88
87 // Go through all downloads in downloads_. Dangerous ones we need to 89 for (DownloadMap::iterator it = downloads_.begin(); it != downloads_.end();) {
88 // remove on disk, and in progress ones we need to cancel. 90 DownloadItem* download = it->second;
89 for (DownloadSet::iterator it = downloads_.begin(); it != downloads_.end();) { 91 ++it;
90 DownloadItem* download = *it;
91
92 // Save iterator from potential erases in this set done by called code.
93 // Iterators after an erasure point are still valid for lists and
94 // associative containers such as sets.
95 it++;
96
97 if (download->safety_state() == DownloadItem::DANGEROUS && 92 if (download->safety_state() == DownloadItem::DANGEROUS &&
98 download->IsPartialDownload()) { 93 download->IsPartialDownload()) {
99 // The user hasn't accepted it, so we need to remove it
100 // from the disk. This may or may not result in it being
101 // removed from the DownloadManager queues and deleted
102 // (specifically, DownloadManager::RemoveDownload only
103 // removes and deletes it if it's known to the history service)
104 // so the only thing we know after calling this function is that
105 // the download was deleted if-and-only-if it was removed
106 // from all queues.
107 download->Delete(DownloadItem::DELETE_DUE_TO_BROWSER_SHUTDOWN); 94 download->Delete(DownloadItem::DELETE_DUE_TO_BROWSER_SHUTDOWN);
108 } else if (download->IsPartialDownload()) { 95 } else if (download->IsPartialDownload()) {
109 download->Cancel(false); 96 download->Cancel(false);
110 download_history_->UpdateEntry(download); 97 download_history_->UpdateEntry(download);
111 } 98 }
112 } 99 }
113 100 STLValueDeleter<DownloadMap> delete_items(&downloads_);
114 // At this point, all dangerous downloads have had their files removed
115 // and all in progress downloads have been cancelled. We can now delete
116 // anything left.
117
118 // Copy downloads_ to separate container so as not to set off checks
119 // in DownloadItem destruction.
120 DownloadSet downloads_to_delete;
121 downloads_to_delete.swap(downloads_);
122
123 in_progress_.clear();
124 active_downloads_.clear();
125 history_downloads_.clear();
126 #if !defined(NDEBUG)
127 save_page_as_downloads_.clear();
128 #endif
129 STLDeleteElements(&downloads_to_delete);
130 101
131 file_manager_ = NULL; 102 file_manager_ = NULL;
132 103
133 // Make sure the save as dialog doesn't notify us back if we're gone before 104 // Make sure the save as dialog doesn't notify us back if we're gone before
134 // it returns. 105 // it returns.
135 if (select_file_dialog_.get()) 106 if (select_file_dialog_.get())
136 select_file_dialog_->ListenerDestroyed(); 107 select_file_dialog_->ListenerDestroyed();
137 108
138 download_history_.reset(); 109 download_history_.reset();
139 download_prefs_.reset(); 110 download_prefs_.reset();
140
141 shutdown_needed_ = false;
142 }
143
144 void DownloadManager::GetTemporaryDownloads(
145 const FilePath& dir_path, std::vector<DownloadItem*>* result) {
146 DCHECK(result);
147
148 for (DownloadMap::iterator it = history_downloads_.begin();
149 it != history_downloads_.end(); ++it) {
150 if (it->second->is_temporary() &&
151 it->second->full_path().DirName() == dir_path)
152 result->push_back(it->second);
153 }
154 }
155
156 void DownloadManager::GetAllDownloads(
157 const FilePath& dir_path, std::vector<DownloadItem*>* result) {
158 DCHECK(result);
159
160 for (DownloadMap::iterator it = history_downloads_.begin();
161 it != history_downloads_.end(); ++it) {
162 if (!it->second->is_temporary() &&
163 (dir_path.empty() || it->second->full_path().DirName() == dir_path))
164 result->push_back(it->second);
165 }
166 }
167
168 void DownloadManager::GetCurrentDownloads(
169 const FilePath& dir_path, std::vector<DownloadItem*>* result) {
170 DCHECK(result);
171
172 for (DownloadMap::iterator it = history_downloads_.begin();
173 it != history_downloads_.end(); ++it) {
174 DownloadItem* item =it->second;
175 // Skip temporary items.
176 if (item->is_temporary())
177 continue;
178 // Skip items that have all their data, and are OK to save.
179 if (!item->IsPartialDownload() &&
180 (item->safety_state() != DownloadItem::DANGEROUS))
181 continue;
182 // Skip items that don't match |dir_path|.
183 // If |dir_path| is empty, all remaining items match.
184 if (!dir_path.empty() && (it->second->full_path().DirName() != dir_path))
185 continue;
186
187 result->push_back(item);
188 }
189
190 // If we have a parent profile, let it add its downloads to the results.
191 Profile* original_profile = profile_->GetOriginalProfile();
192 if (original_profile != profile_)
193 original_profile->GetDownloadManager()->GetCurrentDownloads(dir_path,
194 result);
195 }
196
197 void DownloadManager::SearchDownloads(const string16& query,
198 std::vector<DownloadItem*>* result) {
199 DCHECK(result);
200
201 string16 query_lower(base::i18n::ToLower(query));
202
203 for (DownloadMap::iterator it = history_downloads_.begin();
204 it != history_downloads_.end(); ++it) {
205 DownloadItem* download_item = it->second;
206
207 if (download_item->is_temporary() || download_item->is_extension_install())
208 continue;
209
210 // Display Incognito downloads only in Incognito window, and vice versa.
211 // The Incognito Downloads page will get the list of non-Incognito downloads
212 // from its parent profile.
213 if (profile_->IsOffTheRecord() != download_item->is_otr())
214 continue;
215
216 if (download_item->MatchesQuery(query_lower))
217 result->push_back(download_item);
218 }
219
220 // If we have a parent profile, let it add its downloads to the results.
221 Profile* original_profile = profile_->GetOriginalProfile();
222 if (original_profile != profile_)
223 original_profile->GetDownloadManager()->SearchDownloads(query, result);
224 } 111 }
225 112
226 // Query the history service for information about all persisted downloads. 113 // Query the history service for information about all persisted downloads.
227 bool DownloadManager::Init(Profile* profile) { 114 bool DownloadManager::Init(Profile* profile) {
228 DCHECK(profile); 115 DCHECK(profile);
229 DCHECK(!shutdown_needed_) << "DownloadManager already initialized."; 116 DCHECK(!shutdown_needed_) << "DownloadManager already initialized.";
230 shutdown_needed_ = true; 117 shutdown_needed_ = true;
231 118
232 profile_ = profile; 119 profile_ = profile;
233 download_history_.reset(new DownloadHistory(profile)); 120 download_history_.reset(new DownloadHistory(profile));
(...skipping 22 matching lines...) Expand all
256 // history system of a new download. Since this method can be called while the 143 // history system of a new download. Since this method can be called while the
257 // history service thread is still reading the persistent state, we do not 144 // history service thread is still reading the persistent state, we do not
258 // insert the new DownloadItem into 'history_downloads_' or inform our 145 // insert the new DownloadItem into 'history_downloads_' or inform our
259 // observers at this point. OnCreateDownloadEntryComplete() handles that 146 // observers at this point. OnCreateDownloadEntryComplete() handles that
260 // finalization of the the download creation as a callback from the 147 // finalization of the the download creation as a callback from the
261 // history thread. 148 // history thread.
262 void DownloadManager::StartDownload(int32 download_id) { 149 void DownloadManager::StartDownload(int32 download_id) {
263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
264 151
265 DownloadItem* download = GetActiveDownloadItem(download_id); 152 DownloadItem* download = GetActiveDownloadItem(download_id);
153 DVLOG(1) << __PRETTY_FUNCTION__ << " " << download_id << " " << download;
266 if (!download) 154 if (!download)
267 return; 155 return;
268 156
269 #if defined(ENABLE_SAFE_BROWSING) 157 #if defined(ENABLE_SAFE_BROWSING)
270 // Create a client to verify download URL with safebrowsing. 158 // Create a client to verify download URL with safebrowsing.
271 // It deletes itself after the callback. 159 // It deletes itself after the callback.
272 scoped_refptr<DownloadSBClient> sb_client = new DownloadSBClient( 160 scoped_refptr<DownloadSBClient> sb_client = new DownloadSBClient(
273 download_id, download->url_chain(), download->referrer_url()); 161 download_id, download->url_chain(), download->referrer_url());
274 sb_client->CheckDownloadUrl( 162 sb_client->CheckDownloadUrl(
275 NewCallback(this, &DownloadManager::CheckDownloadUrlDone)); 163 NewCallback(this, &DownloadManager::CheckDownloadUrlDone));
276 #else 164 #else
277 CheckDownloadUrlDone(download_id, false); 165 CheckDownloadUrlDone(download_id, false);
278 #endif 166 #endif
279 } 167 }
280 168
281 void DownloadManager::CheckForHistoryFilesRemoval() { 169 void DownloadManager::CheckForHistoryFilesRemoval() {
282 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
283 for (DownloadMap::iterator it = history_downloads_.begin(); 171 DVLOG(1) << __PRETTY_FUNCTION__;
284 it != history_downloads_.end(); ++it) { 172 for (DownloadMap::iterator it = downloads_.begin();
173 it != downloads_.end(); ++it) {
285 CheckForFileRemoval(it->second); 174 CheckForFileRemoval(it->second);
286 } 175 }
287 } 176 }
288 177
289 void DownloadManager::CheckForFileRemoval(DownloadItem* download_item) { 178 void DownloadManager::CheckForFileRemoval(DownloadItem* download_item) {
290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
291 if (download_item->IsComplete() && 180 if (download_item->IsComplete() &&
292 !download_item->file_externally_removed()) { 181 !download_item->file_externally_removed()) {
293 BrowserThread::PostTask( 182 BrowserThread::PostTask(
294 BrowserThread::FILE, FROM_HERE, 183 BrowserThread::FILE, FROM_HERE,
295 NewRunnableMethod(this, 184 NewRunnableMethod(this,
296 &DownloadManager::CheckForFileRemovalOnFileThread, 185 &DownloadManager::CheckForFileRemovalOnFileThread,
297 download_item->db_handle(), 186 download_item->id(),
298 download_item->GetTargetFilePath())); 187 download_item->GetTargetFilePath()));
299 } 188 }
300 } 189 }
301 190
302 void DownloadManager::CheckForFileRemovalOnFileThread( 191 void DownloadManager::CheckForFileRemovalOnFileThread(
303 int64 db_handle, const FilePath& path) { 192 int64 id, const FilePath& path) {
304 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
305 if (!file_util::PathExists(path)) { 194 if (!file_util::PathExists(path)) {
195 DVLOG(1) << __PRETTY_FUNCTION__ << " " << id;
306 BrowserThread::PostTask( 196 BrowserThread::PostTask(
307 BrowserThread::UI, FROM_HERE, 197 BrowserThread::UI, FROM_HERE,
308 NewRunnableMethod(this, 198 NewRunnableMethod(this,
309 &DownloadManager::OnFileRemovalDetected, 199 &DownloadManager::OnFileRemovalDetected,
310 db_handle)); 200 id));
311 } 201 }
312 } 202 }
313 203
314 void DownloadManager::OnFileRemovalDetected(int64 db_handle) { 204 void DownloadManager::OnFileRemovalDetected(int64 id) {
315 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
316 DownloadMap::iterator it = history_downloads_.find(db_handle); 206 DVLOG(1) << __PRETTY_FUNCTION__ << " " << id;
317 if (it != history_downloads_.end()) { 207 DownloadMap::iterator it = downloads_.find(id);
208 if (it != downloads_.end()) {
209 DVLOG(1) << __PRETTY_FUNCTION__ << " " << id;
318 DownloadItem* download_item = it->second; 210 DownloadItem* download_item = it->second;
319 download_item->OnDownloadedFileRemoved(); 211 download_item->OnDownloadedFileRemoved();
320 } 212 }
321 } 213 }
322 214
323 void DownloadManager::CheckDownloadUrlDone(int32 download_id, 215 void DownloadManager::CheckDownloadUrlDone(int32 download_id,
324 bool is_dangerous_url) { 216 bool is_dangerous_url) {
325 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
326 218
327 DownloadItem* download = GetActiveDownloadItem(download_id); 219 DownloadItem* download = GetActiveDownloadItem(download_id);
220 DVLOG(1) << __PRETTY_FUNCTION__ << " " << download_id << " "
221 << is_dangerous_url << " " << download;
328 if (!download) 222 if (!download)
329 return; 223 return;
330 224
331 if (is_dangerous_url) 225 if (is_dangerous_url)
332 download->MarkUrlDangerous(); 226 download->MarkUrlDangerous();
333 227
334 download_history_->CheckVisitedReferrerBefore(download_id, 228 download_history_->CheckVisitedReferrerBefore(download_id,
335 download->referrer_url(), 229 download->referrer_url(),
336 NewCallback(this, &DownloadManager::CheckVisitedReferrerBeforeDone)); 230 NewCallback(this, &DownloadManager::CheckVisitedReferrerBeforeDone));
337 } 231 }
338 232
339 void DownloadManager::CheckVisitedReferrerBeforeDone( 233 void DownloadManager::CheckVisitedReferrerBeforeDone(
340 int32 download_id, 234 int32 download_id,
341 bool visited_referrer_before) { 235 bool visited_referrer_before) {
342 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
343 237
344 DownloadItem* download = GetActiveDownloadItem(download_id); 238 DownloadItem* download = GetActiveDownloadItem(download_id);
239 DVLOG(1) << __PRETTY_FUNCTION__ << " " << download_id << " " << download;
345 if (!download) 240 if (!download)
346 return; 241 return;
347 242
348 // Check whether this download is for an extension install or not. 243 // Check whether this download is for an extension install or not.
349 // Allow extensions to be explicitly saved. 244 // Allow extensions to be explicitly saved.
350 DownloadStateInfo state = download->state_info(); 245 DownloadStateInfo state = download->state_info();
351 if (!state.prompt_user_for_save_location) { 246 if (!state.prompt_user_for_save_location) {
352 if (UserScript::IsURLUserScript(download->GetURL(), 247 if (UserScript::IsURLUserScript(download->GetURL(),
353 download->mime_type()) || 248 download->mime_type()) ||
354 (download->mime_type() == Extension::kMimeType)) { 249 (download->mime_type() == Extension::kMimeType)) {
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
411 &DownloadManager::CheckIfSuggestedPathExists, 306 &DownloadManager::CheckIfSuggestedPathExists,
412 download->id(), 307 download->id(),
413 state, 308 state,
414 download_prefs()->download_path())); 309 download_prefs()->download_path()));
415 } 310 }
416 311
417 void DownloadManager::CheckIfSuggestedPathExists(int32 download_id, 312 void DownloadManager::CheckIfSuggestedPathExists(int32 download_id,
418 DownloadStateInfo state, 313 DownloadStateInfo state,
419 const FilePath& default_path) { 314 const FilePath& default_path) {
420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 315 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
316 DVLOG(1) << __PRETTY_FUNCTION__ << " " << download_id;
421 317
422 // Make sure the default download directory exists. 318 // Make sure the default download directory exists.
423 // TODO(phajdan.jr): only create the directory when we're sure the user 319 // TODO(phajdan.jr): only create the directory when we're sure the user
424 // is going to save there and not to another directory of his choice. 320 // is going to save there and not to another directory of his choice.
425 file_util::CreateDirectory(default_path); 321 file_util::CreateDirectory(default_path);
426 322
427 // Check writability of the suggested path. If we can't write to it, default 323 // Check writability of the suggested path. If we can't write to it, default
428 // to the user's "My Documents" directory. We'll prompt them in this case. 324 // to the user's "My Documents" directory. We'll prompt them in this case.
429 FilePath dir = state.suggested_path.DirName(); 325 FilePath dir = state.suggested_path.DirName();
430 FilePath filename = state.suggested_path.BaseName(); 326 FilePath filename = state.suggested_path.BaseName();
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
501 &DownloadManager::OnPathExistenceAvailable, 397 &DownloadManager::OnPathExistenceAvailable,
502 download_id, 398 download_id,
503 state)); 399 state));
504 } 400 }
505 401
506 void DownloadManager::OnPathExistenceAvailable( 402 void DownloadManager::OnPathExistenceAvailable(
507 int32 download_id, const DownloadStateInfo& new_state) { 403 int32 download_id, const DownloadStateInfo& new_state) {
508 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 404 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
509 405
510 DownloadItem* download = GetActiveDownloadItem(download_id); 406 DownloadItem* download = GetActiveDownloadItem(download_id);
407 DVLOG(1) << __PRETTY_FUNCTION__ << " " << download << " " << download_id;
511 if (!download) 408 if (!download)
512 return; 409 return;
513 410
514 VLOG(20) << __FUNCTION__ << "()" 411 VLOG(20) << __FUNCTION__ << "()"
515 << " download = " << download->DebugString(true); 412 << " download = " << download->DebugString(true);
516 413
517 download->SetFileCheckResults(new_state); 414 download->SetFileCheckResults(new_state);
518 415
519 FilePath suggested_path = download->suggested_path(); 416 FilePath suggested_path = download->suggested_path();
520 417
(...skipping 30 matching lines...) Expand all
551 // No prompting for download, just continue with the suggested name. 448 // No prompting for download, just continue with the suggested name.
552 ContinueDownloadWithPath(download, suggested_path); 449 ContinueDownloadWithPath(download, suggested_path);
553 } 450 }
554 } 451 }
555 452
556 void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info) { 453 void DownloadManager::CreateDownloadItem(DownloadCreateInfo* info) {
557 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 454 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
558 455
559 DownloadItem* download = new DownloadItem(this, *info, 456 DownloadItem* download = new DownloadItem(this, *info,
560 profile_->IsOffTheRecord()); 457 profile_->IsOffTheRecord());
458 DVLOG(1) << __PRETTY_FUNCTION__ << " " << info->download_id << " "
459 << download;
561 int32 download_id = info->download_id; 460 int32 download_id = info->download_id;
562 DCHECK(!ContainsKey(in_progress_, download_id)); 461 DCHECK(!ContainsKey(downloads_, download_id));
563 DCHECK(!ContainsKey(active_downloads_, download_id)); 462 downloads_[download_id] = download;
564 downloads_.insert(download);
565 active_downloads_[download_id] = download;
566 } 463 }
567 464
568 void DownloadManager::ContinueDownloadWithPath(DownloadItem* download, 465 void DownloadManager::ContinueDownloadWithPath(DownloadItem* download,
569 const FilePath& chosen_file) { 466 const FilePath& chosen_file) {
570 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 467 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
571 DCHECK(download); 468 DCHECK(download);
572 469
573 int32 download_id = download->id(); 470 int32 download_id = download->id();
574 471 DVLOG(1) << __PRETTY_FUNCTION__ << " " << download_id << " " << download
575 // NOTE(ahendrickson) Eventually |active_downloads_| will replace 472 << " " << chosen_file.value();
576 // |in_progress_|, but we don't want to change the semantics yet.
577 DCHECK(!ContainsKey(in_progress_, download_id));
578 DCHECK(ContainsKey(downloads_, download));
579 DCHECK(ContainsKey(active_downloads_, download_id));
580 473
581 // Make sure the initial file name is set only once. 474 // Make sure the initial file name is set only once.
582 DCHECK(download->full_path().empty()); 475 DCHECK(download->full_path().empty());
583 download->OnPathDetermined(chosen_file); 476 download->OnPathDetermined(chosen_file);
584 download->UpdateTarget(); 477 download->UpdateTarget();
585 478
586 VLOG(20) << __FUNCTION__ << "()" 479 VLOG(20) << __FUNCTION__ << "()"
587 << " download = " << download->DebugString(true); 480 << " download = " << download->DebugString(true);
588 481
589 in_progress_[download_id] = download; 482 UpdateAppIcon(); // Reflect entry into .
590 UpdateAppIcon(); // Reflect entry into in_progress_.
591 483
592 // Rename to intermediate name. 484 // Rename to intermediate name.
593 FilePath download_path; 485 FilePath download_path;
594 if (download->IsDangerous()) { 486 if (download->IsDangerous()) {
595 // The download is not safe. We can now rename the file to its 487 // The download is not safe. We can now rename the file to its
596 // tentative name using RenameInProgressDownloadFile. 488 // tentative name using RenameInProgressDownloadFile.
597 // NOTE: The |Rename| below will be a no-op for dangerous files, as we're 489 // NOTE: The |Rename| below will be a no-op for dangerous files, as we're
598 // renaming it to the same name. 490 // renaming it to the same name.
599 download_path = download->full_path(); 491 download_path = download->full_path();
600 } else { 492 } else {
(...skipping 12 matching lines...) Expand all
613 download->id(), download_path)); 505 download->id(), download_path));
614 506
615 download->Rename(download_path); 507 download->Rename(download_path);
616 508
617 download_history_->AddEntry(download, 509 download_history_->AddEntry(download,
618 NewCallback(this, &DownloadManager::OnCreateDownloadEntryComplete)); 510 NewCallback(this, &DownloadManager::OnCreateDownloadEntryComplete));
619 } 511 }
620 512
621 void DownloadManager::UpdateDownload(int32 download_id, int64 size) { 513 void DownloadManager::UpdateDownload(int32 download_id, int64 size) {
622 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 514 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
623 DownloadMap::iterator it = active_downloads_.find(download_id); 515 DownloadMap::iterator it = downloads_.find(download_id);
624 if (it != active_downloads_.end()) { 516 if (it != downloads_.end()) {
625 DownloadItem* download = it->second; 517 DownloadItem* download = it->second;
626 if (download->IsInProgress()) { 518 if (download->IsInProgress()) {
627 download->Update(size); 519 download->Update(size);
628 UpdateAppIcon(); // Reflect size updates. 520 UpdateAppIcon(); // Reflect size updates.
629 download_history_->UpdateEntry(download); 521 download_history_->UpdateEntry(download);
630 } 522 }
631 } 523 }
632 } 524 }
633 525
634 void DownloadManager::OnResponseCompleted(int32 download_id, 526 void DownloadManager::OnResponseCompleted(int32 download_id,
(...skipping 11 matching lines...) Expand all
646 if (os_error == 0) { 538 if (os_error == 0) {
647 OnAllDataSaved(download_id, size, hash); 539 OnAllDataSaved(download_id, size, hash);
648 } else { 540 } else {
649 OnDownloadError(download_id, size, os_error); 541 OnDownloadError(download_id, size, os_error);
650 } 542 }
651 } 543 }
652 544
653 void DownloadManager::OnAllDataSaved(int32 download_id, 545 void DownloadManager::OnAllDataSaved(int32 download_id,
654 int64 size, 546 int64 size,
655 const std::string& hash) { 547 const std::string& hash) {
656 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id 548 DVLOG(1) << __FUNCTION__ << "()" << " download_id = " << download_id
657 << " size = " << size; 549 << " size = " << size;
658 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 550 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
659 551
660 // If it's not in active_downloads_, that means it was cancelled; just 552 DownloadItem* download = downloads_[download_id];
661 // ignore the notification. 553 DVLOG(1) << __PRETTY_FUNCTION__ << " " << download_id << " " << size << " "
662 if (active_downloads_.count(download_id) == 0) 554 << hash << " " << download;
555 if (!download) {
663 return; 556 return;
664 557 }
665 DownloadItem* download = active_downloads_[download_id];
666 download->OnAllDataSaved(size); 558 download->OnAllDataSaved(size);
667 559
668 // When hash is not available, it means either it is not calculated 560 // When hash is not available, it means either it is not calculated
669 // or there is error while it is calculated. We will skip the download hash 561 // or there is error while it is calculated. We will skip the download hash
670 // check in that case. 562 // check in that case.
671 if (!hash.empty()) { 563 if (!hash.empty()) {
672 #if defined(ENABLE_SAFE_BROWSING) 564 #if defined(ENABLE_SAFE_BROWSING)
673 scoped_refptr<DownloadSBClient> sb_client = 565 scoped_refptr<DownloadSBClient> sb_client =
674 new DownloadSBClient(download_id, 566 new DownloadSBClient(download_id,
675 download->url_chain(), 567 download->url_chain(),
676 download->referrer_url()); 568 download->referrer_url());
677 sb_client->CheckDownloadHash( 569 sb_client->CheckDownloadHash(
678 hash, NewCallback(this, &DownloadManager::CheckDownloadHashDone)); 570 hash, NewCallback(this, &DownloadManager::CheckDownloadHashDone));
679 #else 571 #else
680 CheckDownloadHashDone(download_id, false); 572 CheckDownloadHashDone(download_id, false);
681 #endif 573 #endif
682 } 574 }
683 MaybeCompleteDownload(download); 575 MaybeCompleteDownload(download);
684 } 576 }
685 577
686 // TODO(lzheng): This function currently works as a callback place holder. 578 // TODO(lzheng): This function currently works as a callback place holder.
687 // Once we decide the hash check is reliable, we could move the 579 // Once we decide the hash check is reliable, we could move the
688 // MaybeCompleteDownload in OnAllDataSaved to this function. 580 // MaybeCompleteDownload in OnAllDataSaved to this function.
689 void DownloadManager::CheckDownloadHashDone(int32 download_id, 581 void DownloadManager::CheckDownloadHashDone(int32 download_id,
690 bool is_dangerous_hash) { 582 bool is_dangerous_hash) {
691 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 583 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
692 DVLOG(1) << "CheckDownloadHashDone, download_id: " << download_id 584 DVLOG(1) << "CheckDownloadHashDone, download_id: " << download_id
693 << " is dangerous_hash: " << is_dangerous_hash; 585 << " is dangerous_hash: " << is_dangerous_hash;
694
695 // If it's not in active_downloads_, that means it was cancelled or
696 // the download already finished.
697 if (active_downloads_.count(download_id) == 0)
698 return;
699
700 DVLOG(1) << "CheckDownloadHashDone, url: "
701 << active_downloads_[download_id]->GetURL().spec();
702 } 586 }
703 587
704 void DownloadManager::AssertQueueStateConsistent(DownloadItem* download) { 588 void DownloadManager::AssertQueueStateConsistent(DownloadItem* download) {
705 // TODO(rdsmith): Change to DCHECK after http://crbug.com/85408 resolved. 589 DVLOG(1) << __PRETTY_FUNCTION__ << " " << download;
706 if (download->state() == DownloadItem::REMOVING) {
707 CHECK(!ContainsKey(downloads_, download));
708 CHECK(!ContainsKey(active_downloads_, download->id()));
709 CHECK(!ContainsKey(in_progress_, download->id()));
710 CHECK(!ContainsKey(history_downloads_, download->db_handle()));
711 return;
712 }
713
714 // Should be in downloads_ if we're not REMOVING.
715 CHECK(ContainsKey(downloads_, download));
716
717 // Check history_downloads_ consistency.
718 if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
719 CHECK(ContainsKey(history_downloads_, download->db_handle()));
720 } else {
721 // TODO(rdsmith): Somewhat painful; make sure to disable in
722 // release builds after resolution of http://crbug.com/85408.
723 for (DownloadMap::iterator it = history_downloads_.begin();
724 it != history_downloads_.end(); ++it) {
725 CHECK(it->second != download);
726 }
727 }
728
729 CHECK(ContainsKey(active_downloads_, download->id()) ==
730 (download->state() == DownloadItem::IN_PROGRESS));
731 CHECK(ContainsKey(in_progress_, download->id()) ==
732 (download->state() == DownloadItem::IN_PROGRESS));
733 } 590 }
734 591
735 bool DownloadManager::IsDownloadReadyForCompletion(DownloadItem* download) { 592 bool DownloadManager::IsDownloadReadyForCompletion(DownloadItem* download) {
593 DVLOG(1) << __PRETTY_FUNCTION__ << " p=" << download << " id="
594 << download->id() << " data=" << download->all_data_saved()
595 << " safety=" << download->safety_state() << " path="
596 << download->full_path().value();
597 return download->all_data_saved() &&
598 download->safety_state() != DownloadItem::DANGEROUS &&
599 !download->full_path().value().empty();
736 // If we don't have all the data, the download is not ready for 600 // If we don't have all the data, the download is not ready for
737 // completion. 601 // completion.
738 if (!download->all_data_saved()) 602 if (!download->all_data_saved())
739 return false; 603 return false;
740 604
741 // If the download is dangerous, but not yet validated, it's not ready for 605 // If the download is dangerous, but not yet validated, it's not ready for
742 // completion. 606 // completion.
743 if (download->safety_state() == DownloadItem::DANGEROUS) 607 if (download->safety_state() == DownloadItem::DANGEROUS)
744 return false; 608 return false;
745 609
746 // If the download isn't active (e.g. has been cancelled) it's not
747 // ready for completion.
748 if (active_downloads_.count(download->id()) == 0)
749 return false;
750
751 // If the download hasn't been inserted into the history system
752 // (which occurs strictly after file name determination, intermediate
753 // file rename, and UI display) then it's not ready for completion.
754 if (download->db_handle() == DownloadHistory::kUninitializedHandle)
755 return false;
756
757 return true; 610 return true;
758 } 611 }
759 612
760 void DownloadManager::MaybeCompleteDownload(DownloadItem* download) { 613 void DownloadManager::MaybeCompleteDownload(DownloadItem* download) {
761 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 614 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
762 VLOG(20) << __FUNCTION__ << "()" << " download = " 615 DVLOG(1) << __FUNCTION__ << " " << download->DebugString(true);
763 << download->DebugString(false);
764 616
765 if (!IsDownloadReadyForCompletion(download)) 617 if (!IsDownloadReadyForCompletion(download))
766 return; 618 return;
767 619
768 // TODO(rdsmith): DCHECK that we only pass through this point 620 // TODO(rdsmith): DCHECK that we only pass through this point
769 // once per download. The natural way to do this is by a state 621 // once per download. The natural way to do this is by a state
770 // transition on the DownloadItem. 622 // transition on the DownloadItem.
771 623
772 // Confirm we're in the proper set of states to be here; 624 // Confirm we're in the proper set of states to be here;
773 // in in_progress_, have all data, have a history handle, (validated or safe). 625 // in , have all data, have a history handle, (validated or safe).
774 DCHECK_NE(DownloadItem::DANGEROUS, download->safety_state()); 626 DCHECK_NE(DownloadItem::DANGEROUS, download->safety_state());
775 DCHECK_EQ(1u, in_progress_.count(download->id()));
776 DCHECK(download->all_data_saved()); 627 DCHECK(download->all_data_saved());
777 DCHECK(download->db_handle() != DownloadHistory::kUninitializedHandle); 628 DCHECK(!download->full_path().value().empty());
778 DCHECK_EQ(1u, history_downloads_.count(download->db_handle()));
779 629
780 VLOG(20) << __FUNCTION__ << "()" << " executing: download = " 630 DVLOG(1) << __FUNCTION__ << " " << download->DebugString(true);
781 << download->DebugString(false);
782 631
783 // Remove the id from in_progress 632 UpdateAppIcon(); // Reflect removal from .
784 in_progress_.erase(download->id());
785 UpdateAppIcon(); // Reflect removal from in_progress_.
786 633
787 download_history_->UpdateEntry(download); 634 download_history_->UpdateEntry(download);
788 635
789 // Finish the download. 636 // Finish the download.
790 download->OnDownloadCompleting(file_manager_); 637 download->OnDownloadCompleting(file_manager_);
638 DVLOG(1) << __FUNCTION__ << " " << download->DebugString(true);
791 } 639 }
792 640
793 void DownloadManager::DownloadCompleted(int32 download_id) { 641 void DownloadManager::DownloadCompleted(int32 download_id) {
794 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 642 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
795 DownloadItem* download = GetDownloadItem(download_id); 643 DownloadItem* download = GetDownloadItem(download_id);
796 DCHECK(download); 644 DCHECK(download);
797 download_history_->UpdateEntry(download); 645 download_history_->UpdateEntry(download);
798 active_downloads_.erase(download_id);
799 } 646 }
800 647
801 void DownloadManager::OnDownloadRenamedToFinalName(int download_id, 648 void DownloadManager::OnDownloadRenamedToFinalName(int download_id,
802 const FilePath& full_path, 649 const FilePath& full_path,
803 int uniquifier) { 650 int uniquifier) {
804 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id 651 DVLOG(1) << __FUNCTION__ << "()" << " download_id = " << download_id
805 << " full_path = \"" << full_path.value() << "\"" 652 << " full_path = \"" << full_path.value() << "\""
806 << " uniquifier = " << uniquifier; 653 << " uniquifier = " << uniquifier;
807 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 654 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
808 655
809 DownloadItem* item = GetDownloadItem(download_id); 656 DownloadItem* item = GetDownloadItem(download_id);
810 if (!item) 657 if (!item)
811 return; 658 return;
812 659
813 if (item->safety_state() == DownloadItem::SAFE) { 660 if (item->safety_state() == DownloadItem::SAFE) {
814 DCHECK_EQ(0, uniquifier) << "We should not uniquify SAFE downloads twice"; 661 DCHECK_EQ(0, uniquifier) << "We should not uniquify SAFE downloads twice";
815 } 662 }
816 663
817 BrowserThread::PostTask( 664 BrowserThread::PostTask(
818 BrowserThread::FILE, FROM_HERE, 665 BrowserThread::FILE, FROM_HERE,
819 NewRunnableMethod( 666 NewRunnableMethod(
820 file_manager_, &DownloadFileManager::CompleteDownload, download_id)); 667 file_manager_, &DownloadFileManager::CompleteDownload, download_id));
821 668
822 if (uniquifier) 669 if (uniquifier)
823 item->set_path_uniquifier(uniquifier); 670 item->set_path_uniquifier(uniquifier);
824 671
825 item->OnDownloadRenamedToFinalName(full_path); 672 item->OnDownloadRenamedToFinalName(full_path);
826 download_history_->UpdateDownloadPath(item, full_path); 673 download_history_->UpdateDownloadPath(item, full_path);
674 UpdateAppIcon();
827 } 675 }
828 676
829 void DownloadManager::DownloadCancelled(int32 download_id) { 677 void DownloadManager::DownloadCancelled(int32 download_id) {
830 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 678 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
831 DownloadMap::iterator it = in_progress_.find(download_id); 679 DownloadItem* download = downloads_[download_id];
832 if (it == in_progress_.end()) 680 DVLOG(1) << __PRETTY_FUNCTION__ << " " << download_id << " " << download
833 return; 681 << " " << (download ? download->DebugString(true) : "");
834 DownloadItem* download = it->second; 682 DCHECK(download);
835 683 UpdateAppIcon(); // Reflect removal from .
836 VLOG(20) << __FUNCTION__ << "()" << " download_id = " << download_id 684 download_history_->UpdateEntry(download);
837 << " download = " << download->DebugString(true);
838
839 // Clean up will happen when the history system create callback runs if we
840 // don't have a valid db_handle yet.
841 if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
842 in_progress_.erase(it);
843 active_downloads_.erase(download_id);
844 UpdateAppIcon(); // Reflect removal from in_progress_.
845 download_history_->UpdateEntry(download);
846 }
847
848 DownloadCancelledInternal(download_id, download->request_handle()); 685 DownloadCancelledInternal(download_id, download->request_handle());
849 } 686 }
850 687
851 void DownloadManager::DownloadCancelledInternal( 688 void DownloadManager::DownloadCancelledInternal(
852 int download_id, const DownloadRequestHandle& request_handle) { 689 int download_id, const DownloadRequestHandle& request_handle) {
853 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 690 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
854 request_handle.CancelRequest(); 691 request_handle.CancelRequest();
855 692
856 BrowserThread::PostTask( 693 BrowserThread::PostTask(
857 BrowserThread::FILE, FROM_HERE, 694 BrowserThread::FILE, FROM_HERE,
858 NewRunnableMethod( 695 NewRunnableMethod(
859 file_manager_, &DownloadFileManager::CancelDownload, download_id)); 696 file_manager_, &DownloadFileManager::CancelDownload, download_id));
860 } 697 }
861 698
862 void DownloadManager::OnDownloadError(int32 download_id, 699 void DownloadManager::OnDownloadError(int32 download_id,
863 int64 size, 700 int64 size,
864 int os_error) { 701 int os_error) {
865 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 702 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
866 DownloadMap::iterator it = active_downloads_.find(download_id); 703 DownloadItem* download = downloads_[download_id];
867 // A cancel at the right time could remove the download from the 704 DVLOG(1) << __PRETTY_FUNCTION__ << " " << download_id << " " << download
868 // |active_downloads_| map before we get here. 705 << " " << (download ? download->DebugString(true) : "");
869 if (it == active_downloads_.end()) 706 DCHECK(download);
870 return;
871
872 DownloadItem* download = it->second;
873
874 VLOG(20) << __FUNCTION__ << "()" << " Error " << os_error
875 << " at offset " << download->received_bytes()
876 << " for download = " << download->DebugString(true);
877
878 download->Interrupted(size, os_error); 707 download->Interrupted(size, os_error);
879 708 UpdateAppIcon(); // Reflect removal from .
880 // TODO(ahendrickson) - Remove this when we add resuming of interrupted 709 download_history_->UpdateEntry(download);
881 // downloads, as we will keep the download item around in that case.
882 //
883 // Clean up will happen when the history system create callback runs if we
884 // don't have a valid db_handle yet.
885 if (download->db_handle() != DownloadHistory::kUninitializedHandle) {
886 in_progress_.erase(download_id);
887 active_downloads_.erase(download_id);
888 UpdateAppIcon(); // Reflect removal from in_progress_.
889 download_history_->UpdateEntry(download);
890 }
891
892 BrowserThread::PostTask( 710 BrowserThread::PostTask(
893 BrowserThread::FILE, FROM_HERE, 711 BrowserThread::FILE, FROM_HERE,
894 NewRunnableMethod( 712 NewRunnableMethod(
895 file_manager_, &DownloadFileManager::CancelDownload, download_id)); 713 file_manager_, &DownloadFileManager::CancelDownload, download_id));
896 } 714 }
897 715
898 void DownloadManager::UpdateAppIcon() { 716 void DownloadManager::UpdateAppIcon() {
899 if (status_updater_) 717 if (status_updater_)
900 status_updater_->Update(); 718 status_updater_->Update();
901 } 719 }
902 720
903 void DownloadManager::RemoveDownload(int64 download_handle) { 721 void DownloadManager::RemoveDownload(int64 id) {
904 DownloadMap::iterator it = history_downloads_.find(download_handle); 722 DownloadMap::iterator it = downloads_.find(id);
905 if (it == history_downloads_.end()) 723 scoped_ptr<DownloadItem> download(it->second);
724 DVLOG(1) << __PRETTY_FUNCTION__ << " " << id << " " << download.get();
725 if (download.get() == NULL) {
906 return; 726 return;
727 }
728 download_history_->RemoveEntry(download.get());
729 downloads_.erase(it);
730 NotifyModelChanged();
731 }
907 732
908 // Make history update. 733 void DownloadManager::GetTemporaryDownloads(
909 DownloadItem* download = it->second; 734 const FilePath& dir_path, ItemVector* result) {
910 download_history_->RemoveEntry(download); 735 DCHECK(result);
736 Search(DOWNLOAD_QUERY_FILTER(item.is_temporary()).
737 filenameRegex("^" + dir_path.value() + "/.*"),
738 result);
739 DVLOG(1) << __PRETTY_FUNCTION__ << " " << result->size();
740 }
911 741
912 // Remove from our tables and delete. 742 void DownloadManager::GetAllDownloads(
913 history_downloads_.erase(it); 743 const FilePath& dir_path, ItemVector* result) {
914 int downloads_count = downloads_.erase(download); 744 DCHECK(result);
915 DCHECK_EQ(1, downloads_count); 745 Search(DOWNLOAD_QUERY_FILTER(!item.is_temporary()).
746 filenameRegex("^" + dir_path.value() + "/.*"),
747 result);
748 }
916 749
917 // Tell observers to refresh their views. 750 void DownloadManager::GetCurrentDownloads(
918 NotifyModelChanged(); 751 const FilePath& dir_path, ItemVector* result) {
752 DCHECK(result);
753 Search(DOWNLOAD_QUERY_FILTER(
754 !item.is_temporary() &&
755 (item.IsPartialDownload() ||
756 (item.safety_state() == DownloadItem::DANGEROUS))).
757 filenameRegex("^" + dir_path.value() + "/.*"),
758 result);
759 }
919 760
920 delete download; 761 bool DownloadManager::Search(const download_util::DownloadQuery& query,
762 ItemVector* items,
763 bool merge_parent_manager,
764 std::string* error_msg,
765 ListValue* json_results) const {
766 ItemVector default_items;
767 if (items == NULL) {
768 items = &default_items;
769 }
770 MergeItems(merge_parent_manager, items);
771 return query.Search(items, error_msg, json_results);
772 }
773
774 void DownloadManager::MergeItems(
775 bool merge_parent_manager, ItemVector* items) const {
776 for (DownloadMap::const_iterator it = downloads_.begin();
777 it != downloads_.end(); ++it) items->push_back(it->second);
778 if (!merge_parent_manager ||
779 (profile_ == NULL)) return;
780 Profile* original_profile = profile_->GetOriginalProfile();
781 if ((profile_ != NULL) &&
782 (original_profile != NULL) &&
783 (original_profile != profile_) &&
784 (original_profile->GetDownloadManager() != NULL)) {
785 original_profile->GetDownloadManager()->MergeItems(
786 merge_parent_manager, items);
787 }
788 }
789
790 void DownloadManager::SearchDownloads(const string16& query,
791 ItemVector* result) {
792 DCHECK(result);
793 bool otr = false;
794 if (profile_ != NULL) otr = profile_->IsOffTheRecord();
795 Search(DOWNLOAD_QUERY_FILTER1(otr,
796 !item.is_temporary() &&
797 !item.is_extension_install() &&
798 (item.is_otr() == otr)).
799 query(UTF16ToASCII(query)),
800 result);
921 } 801 }
922 802
923 int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin, 803 int DownloadManager::RemoveDownloadsBetween(const base::Time remove_begin,
924 const base::Time remove_end) { 804 const base::Time remove_end) {
925 download_history_->RemoveEntriesBetween(remove_begin, remove_end); 805 download_history_->RemoveEntriesBetween(remove_begin, remove_end);
926 806 ItemVector pending_deletes;
927 // All downloads visible to the user will be in the history, 807 STLElementDeleter<ItemVector> delete_pending_deletes(&pending_deletes);
928 // so scan that map. 808 const base::Time unix_epoch = base::Time::UnixEpoch();
929 DownloadMap::iterator it = history_downloads_.begin(); 809 Search(DOWNLOAD_QUERY_FILTER(
930 std::vector<DownloadItem*> pending_deletes; 810 item.IsComplete() ||
931 while (it != history_downloads_.end()) { 811 item.IsCancelled() ||
932 DownloadItem* download = it->second; 812 item.IsInterrupted()).
933 if (download->start_time() >= remove_begin && 813 startedAfter((remove_begin - unix_epoch).InMilliseconds()).
934 (remove_end.is_null() || download->start_time() < remove_end) && 814 startedBefore((remove_end - unix_epoch).InMilliseconds()),
935 (download->IsComplete() || 815 &pending_deletes);
936 download->IsCancelled() || 816 if (pending_deletes.empty()) return 0;
937 download->IsInterrupted())) { 817 for (ItemVector::const_iterator iter = pending_deletes.begin();
938 AssertQueueStateConsistent(download); 818 iter != pending_deletes.end(); ++iter) {
939 819 downloads_.erase(downloads_.find((*iter)->id()));
940 // Remove from the map and move to the next in the list.
941 history_downloads_.erase(it++);
942
943 // Also remove it from any completed dangerous downloads.
944 pending_deletes.push_back(download);
945
946 continue;
947 }
948
949 ++it;
950 } 820 }
951
952 // If we aren't deleting anything, we're done.
953 if (pending_deletes.empty())
954 return 0;
955
956 // Remove the chosen downloads from the main owning container.
957 for (std::vector<DownloadItem*>::iterator it = pending_deletes.begin();
958 it != pending_deletes.end(); it++) {
959 downloads_.erase(*it);
960 }
961
962 // Tell observers to refresh their views.
963 NotifyModelChanged(); 821 NotifyModelChanged();
964
965 // Delete the download items themselves.
966 int num_deleted = static_cast<int>(pending_deletes.size()); 822 int num_deleted = static_cast<int>(pending_deletes.size());
967
968 STLDeleteContainerPointers(pending_deletes.begin(), pending_deletes.end());
969 pending_deletes.clear();
970
971 return num_deleted; 823 return num_deleted;
972 } 824 }
973 825
974 int DownloadManager::RemoveDownloads(const base::Time remove_begin) { 826 int DownloadManager::RemoveDownloads(const base::Time remove_begin) {
975 return RemoveDownloadsBetween(remove_begin, base::Time()); 827 return RemoveDownloadsBetween(remove_begin, base::Time());
976 } 828 }
977 829
978 int DownloadManager::RemoveAllDownloads() { 830 int DownloadManager::RemoveAllDownloads() {
979 if (this != profile_->GetOriginalProfile()->GetDownloadManager()) { 831 if (this != profile_->GetOriginalProfile()->GetDownloadManager()) {
980 // This is an incognito downloader. Clear All should clear main download 832 // This is an incognito downloader. Clear All should clear main download
981 // manager as well. 833 // manager as well.
982 profile_->GetOriginalProfile()->GetDownloadManager()->RemoveAllDownloads(); 834 profile_->GetOriginalProfile()->GetDownloadManager()->RemoveAllDownloads();
983 } 835 }
984 // The null times make the date range unbounded. 836 // The null times make the date range unbounded.
985 return RemoveDownloadsBetween(base::Time(), base::Time()); 837 return RemoveDownloadsBetween(base::Time(), base::Time());
986 } 838 }
987 839
988 void DownloadManager::SavePageAsDownloadStarted(DownloadItem* download) { 840 void DownloadManager::SavePageAsDownloadStarted(DownloadItem* download) {
989 #if !defined(NDEBUG) 841 #if !defined(NDEBUG)
990 save_page_as_downloads_.insert(download); 842 save_page_as_downloads_.insert(download);
991 #endif 843 #endif
992 downloads_.insert(download); 844 downloads_[download->id()] = download;
993 // Add to history and notify observers. 845 // Add to history and notify observers.
994 AddDownloadItemToHistory(download, DownloadHistory::kUninitializedHandle); 846 AddDownloadItemToHistory(download);
995 NotifyModelChanged(); 847 NotifyModelChanged();
996 } 848 }
997 849
998 // Initiate a download of a specific URL. We send the request to the 850 // Initiate a download of a specific URL. We send the request to the
999 // ResourceDispatcherHost, and let it send us responses like a regular 851 // ResourceDispatcherHost, and let it send us responses like a regular
1000 // download. 852 // download.
1001 void DownloadManager::DownloadUrl(const GURL& url, 853 void DownloadManager::DownloadUrl(const GURL& url,
1002 const GURL& referrer, 854 const GURL& referrer,
1003 const std::string& referrer_charset, 855 const std::string& referrer_charset,
1004 TabContents* tab_contents) { 856 TabContents* tab_contents) {
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
1041 if (extension.empty()) 893 if (extension.empty())
1042 return false; 894 return false;
1043 if (Extension::IsExtension(path)) 895 if (Extension::IsExtension(path))
1044 return false; 896 return false;
1045 DCHECK(extension[0] == FilePath::kExtensionSeparator); 897 DCHECK(extension[0] == FilePath::kExtensionSeparator);
1046 extension.erase(0, 1); 898 extension.erase(0, 1);
1047 return download_prefs_->IsAutoOpenEnabledForExtension(extension); 899 return download_prefs_->IsAutoOpenEnabledForExtension(extension);
1048 } 900 }
1049 901
1050 bool DownloadManager::IsDownloadProgressKnown() { 902 bool DownloadManager::IsDownloadProgressKnown() {
1051 for (DownloadMap::iterator i = in_progress_.begin(); 903 for (DownloadMap::iterator i = downloads_.begin();
1052 i != in_progress_.end(); ++i) { 904 i != downloads_.end(); ++i) {
1053 if (i->second->total_bytes() <= 0) 905 if (i->second->total_bytes() <= 0) {
1054 return false; 906 return false;
907 }
1055 } 908 }
1056
1057 return true; 909 return true;
1058 } 910 }
1059 911
1060 int64 DownloadManager::GetInProgressDownloadCount() { 912 int64 DownloadManager::GetInProgressDownloadCount() const {
1061 return in_progress_.size(); 913 ItemVector in_progress;
914 Search(download_util::DownloadQuery().state_enum(DownloadItem::IN_PROGRESS),
915 &in_progress);
916 DVLOG(1) << __PRETTY_FUNCTION__ << " " << in_progress.size();
917 return in_progress.size();
1062 } 918 }
1063 919
1064 int64 DownloadManager::GetReceivedDownloadBytes() { 920 int64 DownloadManager::GetReceivedDownloadBytes() {
1065 DCHECK(IsDownloadProgressKnown()); 921 DCHECK(IsDownloadProgressKnown());
1066 int64 received_bytes = 0; 922 int64 received_bytes = 0;
1067 for (DownloadMap::iterator i = in_progress_.begin(); 923 for (DownloadMap::iterator i = downloads_.begin();
1068 i != in_progress_.end(); ++i) { 924 i != downloads_.end(); ++i) {
1069 received_bytes += i->second->received_bytes(); 925 received_bytes += i->second->received_bytes();
1070 } 926 }
927 DVLOG(1) << __PRETTY_FUNCTION__ << " " << received_bytes;
1071 return received_bytes; 928 return received_bytes;
1072 } 929 }
1073 930
1074 int64 DownloadManager::GetTotalDownloadBytes() { 931 int64 DownloadManager::GetTotalDownloadBytes() {
1075 DCHECK(IsDownloadProgressKnown()); 932 DCHECK(IsDownloadProgressKnown());
1076 int64 total_bytes = 0; 933 int64 total_bytes = 0;
1077 for (DownloadMap::iterator i = in_progress_.begin(); 934 for (DownloadMap::iterator i = downloads_.begin();
1078 i != in_progress_.end(); ++i) { 935 i != downloads_.end(); ++i) {
1079 total_bytes += i->second->total_bytes(); 936 total_bytes += i->second->total_bytes();
1080 } 937 }
938 DVLOG(1) << __PRETTY_FUNCTION__ << " " << total_bytes;
1081 return total_bytes; 939 return total_bytes;
1082 } 940 }
1083 941
1084 void DownloadManager::FileSelected(const FilePath& path, 942 void DownloadManager::FileSelected(const FilePath& path,
1085 int index, void* params) { 943 int index, void* params) {
1086 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 944 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1087 945
1088 int32* id_ptr = reinterpret_cast<int32*>(params); 946 int32* id_ptr = reinterpret_cast<int32*>(params);
1089 DCHECK(id_ptr != NULL); 947 DCHECK(id_ptr != NULL);
1090 int32 download_id = *id_ptr; 948 int32 download_id = *id_ptr;
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
1157 1015
1158 MaybeCompleteDownload(download); 1016 MaybeCompleteDownload(download);
1159 } 1017 }
1160 1018
1161 // Operations posted to us from the history service ---------------------------- 1019 // Operations posted to us from the history service ----------------------------
1162 1020
1163 // The history service has retrieved all download entries. 'entries' contains 1021 // The history service has retrieved all download entries. 'entries' contains
1164 // 'DownloadHistoryInfo's in sorted order (by ascending start_time). 1022 // 'DownloadHistoryInfo's in sorted order (by ascending start_time).
1165 void DownloadManager::OnQueryDownloadEntriesComplete( 1023 void DownloadManager::OnQueryDownloadEntriesComplete(
1166 std::vector<DownloadHistoryInfo>* entries) { 1024 std::vector<DownloadHistoryInfo>* entries) {
1167 for (size_t i = 0; i < entries->size(); ++i) { 1025 for (std::vector<DownloadHistoryInfo>::iterator it = entries->begin();
1168 DownloadItem* download = new DownloadItem(this, entries->at(i)); 1026 it != entries->end(); ++it) {
1169 DCHECK(!ContainsKey(history_downloads_, download->db_handle())); 1027 DownloadItem* download = new DownloadItem(this, *it);
1170 downloads_.insert(download); 1028 DVLOG(2) << __PRETTY_FUNCTION__ << " " << download->id() << " " << download;
1171 history_downloads_[download->db_handle()] = download; 1029 DCHECK(!ContainsKey(downloads_, download->id()));
1172 VLOG(20) << __FUNCTION__ << "()" << i << ">" 1030 downloads_[download->id()] = download;
1173 << " download = " << download->DebugString(true);
1174 } 1031 }
1175 NotifyModelChanged(); 1032 NotifyModelChanged();
1176 CheckForHistoryFilesRemoval(); 1033 CheckForHistoryFilesRemoval();
1177 } 1034 }
1178 1035
1179 void DownloadManager::AddDownloadItemToHistory(DownloadItem* download, 1036 void DownloadManager::AddDownloadItemToHistory(DownloadItem* download) {
1180 int64 db_handle) {
1181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1037 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1182
1183 // It's not immediately obvious, but HistoryBackend::CreateDownload() can
1184 // call this function with an invalid |db_handle|. For instance, this can
1185 // happen when the history database is offline. We cannot have multiple
1186 // DownloadItems with the same invalid db_handle, so we need to assign a
1187 // unique |db_handle| here.
1188 if (db_handle == DownloadHistory::kUninitializedHandle)
1189 db_handle = download_history_->GetNextFakeDbHandle();
1190
1191 // TODO(rdsmith): Convert to DCHECK() when http://crbug.com/84508
1192 // is fixed.
1193 CHECK_NE(DownloadHistory::kUninitializedHandle, db_handle);
1194
1195 DCHECK(download->db_handle() == DownloadHistory::kUninitializedHandle);
1196 download->set_db_handle(db_handle);
1197
1198 DCHECK(!ContainsKey(history_downloads_, download->db_handle()));
1199 history_downloads_[download->db_handle()] = download;
1200 } 1038 }
1201 1039
1202 // Once the new DownloadItem's creation info has been committed to the history 1040 // Once the new DownloadItem's creation info has been committed to the history
1203 // service, we associate the DownloadItem with the db handle, update our 1041 // service, we associate the DownloadItem with the db handle, update our
1204 // 'history_downloads_' map and inform observers. 1042 // 'history_downloads_' map and inform observers.
1205 void DownloadManager::OnCreateDownloadEntryComplete(int32 download_id, 1043 void DownloadManager::OnCreateDownloadEntryComplete(
1206 int64 db_handle) { 1044 int32 download_id) {
1207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1045 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1208 DownloadItem* download = GetActiveDownloadItem(download_id); 1046 DownloadItem* download = GetActiveDownloadItem(download_id);
1209 if (!download) 1047 DVLOG(1) << __PRETTY_FUNCTION__ << " " << download_id << " " << download;
1210 return; 1048 if (download == NULL) return;
1049 download->SetIsInHistory(true);
1050 VLOG(20) << __FUNCTION__ << download->DebugString(true);
1211 1051
1212 VLOG(20) << __FUNCTION__ << "()" << " db_handle = " << db_handle 1052 AddDownloadItemToHistory(download);
1213 << " download_id = " << download_id
1214 << " download = " << download->DebugString(true);
1215 1053
1216 AddDownloadItemToHistory(download, db_handle); 1054 DVLOG(1) << __PRETTY_FUNCTION__ << " " << download_id << " " << download;
1217 1055
1218 // Show in the appropriate browser UI. 1056 // Show in the appropriate browser UI.
1219 // This includes buttons to save or cancel, for a dangerous download. 1057 // This includes buttons to save or cancel, for a dangerous download.
1220 ShowDownloadInBrowser(download); 1058 ShowDownloadInBrowser(download);
1221 1059
1222 // Inform interested objects about the new download. 1060 // Inform interested objects about the new download.
1223 NotifyModelChanged(); 1061 NotifyModelChanged();
1224 1062
1225 // If the download is still in progress, try to complete it. 1063 // If the download is still in progress, try to complete it.
1226 // 1064 //
1227 // Otherwise, download has been cancelled or interrupted before we've 1065 // Otherwise, download has been cancelled or interrupted before we've
1228 // received the DB handle. We post one final message to the history 1066 // received the DB handle. We post one final message to the history
1229 // service so that it can be properly in sync with the DownloadItem's 1067 // service so that it can be properly in sync with the DownloadItem's
1230 // completion status, and also inform any observers so that they get 1068 // completion status, and also inform any observers so that they get
1231 // more than just the start notification. 1069 // more than just the start notification.
1232 if (download->IsInProgress()) { 1070 if (download->IsInProgress()) {
1233 MaybeCompleteDownload(download); 1071 MaybeCompleteDownload(download);
1234 } else { 1072 } else {
1235 DCHECK(download->IsCancelled())
1236 << " download = " << download->DebugString(true);
1237 in_progress_.erase(download_id);
1238 active_downloads_.erase(download_id);
1239 download_history_->UpdateEntry(download); 1073 download_history_->UpdateEntry(download);
1240 download->UpdateObservers(); 1074 download->UpdateObservers();
1241 } 1075 }
1242 } 1076 }
1243 1077
1244 void DownloadManager::ShowDownloadInBrowser(DownloadItem* download) { 1078 void DownloadManager::ShowDownloadInBrowser(DownloadItem* download) {
1079 DVLOG(1) << __PRETTY_FUNCTION__ << " " << download << " " << download->id();
1080
1245 // The 'contents' may no longer exist if the user closed the tab before we 1081 // The 'contents' may no longer exist if the user closed the tab before we
1246 // get this start completion event. If it does, tell the origin TabContents 1082 // get this start completion event. If it does, tell the origin TabContents
1247 // to display its download shelf. 1083 // to display its download shelf.
1248 DownloadRequestHandle request_handle = download->request_handle(); 1084 DownloadRequestHandle request_handle = download->request_handle();
1249 TabContents* contents = request_handle.GetTabContents(); 1085 TabContents* contents = request_handle.GetTabContents();
1250 TabContentsWrapper* wrapper = NULL; 1086 TabContentsWrapper* wrapper = NULL;
1251 if (contents) 1087 if (contents)
1252 wrapper = TabContentsWrapper::GetCurrentWrapperForContents(contents); 1088 wrapper = TabContentsWrapper::GetCurrentWrapperForContents(contents);
1253 1089
1254 // If the contents no longer exists, we start the download in the last active 1090 // If the contents no longer exists, we start the download in the last active
(...skipping 14 matching lines...) Expand all
1269 // Clears the last download path, used to initialize "save as" dialogs. 1105 // Clears the last download path, used to initialize "save as" dialogs.
1270 void DownloadManager::ClearLastDownloadPath() { 1106 void DownloadManager::ClearLastDownloadPath() {
1271 last_download_path_ = FilePath(); 1107 last_download_path_ = FilePath();
1272 } 1108 }
1273 1109
1274 void DownloadManager::NotifyModelChanged() { 1110 void DownloadManager::NotifyModelChanged() {
1275 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged()); 1111 FOR_EACH_OBSERVER(Observer, observers_, ModelChanged());
1276 } 1112 }
1277 1113
1278 DownloadItem* DownloadManager::GetDownloadItem(int download_id) { 1114 DownloadItem* DownloadManager::GetDownloadItem(int download_id) {
1279 // The |history_downloads_| map is indexed by the download's db_handle, 1115 DownloadItem* download = downloads_[download_id];
1280 // not its id, so we have to iterate. 1116 DVLOG(1) << __PRETTY_FUNCTION__ << " " << download_id << " " << download;
1281 for (DownloadMap::iterator it = history_downloads_.begin(); 1117 return download;
1282 it != history_downloads_.end(); ++it) {
1283 DownloadItem* item = it->second;
1284 if (item->id() == download_id)
1285 return item;
1286 }
1287 return NULL;
1288 } 1118 }
1289 1119
1290 DownloadItem* DownloadManager::GetActiveDownloadItem(int download_id) { 1120 DownloadItem* DownloadManager::GetActiveDownloadItem(int download_id) {
1291 DCHECK(ContainsKey(active_downloads_, download_id)); 1121 return GetDownloadItem(download_id);
1292 DownloadItem* download = active_downloads_[download_id];
1293 DCHECK(download != NULL);
1294 return download;
1295 } 1122 }
1296 1123
1297 // Confirm that everything in all maps is also in |downloads_|, and that 1124 // Confirm that there's only one of any download_id, and that they are all > -1
1298 // everything in |downloads_| is also in some other map.
1299 void DownloadManager::AssertContainersConsistent() const { 1125 void DownloadManager::AssertContainersConsistent() const {
1300 #if !defined(NDEBUG) 1126 std::vector<int> ids;
1301 // Turn everything into sets. 1127 for (DownloadMap::const_iterator iter = downloads_.begin();
1302 DownloadSet active_set, history_set; 1128 iter != downloads_.end(); ++iter) {
1303 const DownloadMap* input_maps[] = {&active_downloads_, &history_downloads_}; 1129 ids.push_back(iter->second->id());
1304 DownloadSet* local_sets[] = {&active_set, &history_set};
1305 DCHECK_EQ(ARRAYSIZE_UNSAFE(input_maps), ARRAYSIZE_UNSAFE(local_sets));
1306 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(input_maps); i++) {
1307 for (DownloadMap::const_iterator it = input_maps[i]->begin();
1308 it != input_maps[i]->end(); it++) {
1309 local_sets[i]->insert(&*it->second);
1310 }
1311 } 1130 }
1312 1131 std::sort(ids.begin(), ids.end());
1313 // Check if each set is fully present in downloads, and create a union. 1132 int prev_id = -1;
1314 const DownloadSet* all_sets[] = {&active_set, &history_set, 1133 for (std::vector<int>::const_iterator iter = ids.begin();
1315 &save_page_as_downloads_}; 1134 iter != ids.end(); ++iter) {
1316 DownloadSet downloads_union; 1135 CHECK_GT(*iter, prev_id) << *iter << " " << prev_id;
1317 for (int i = 0; i < static_cast<int>(ARRAYSIZE_UNSAFE(all_sets)); i++) { 1136 prev_id = *iter;
1318 DownloadSet remainder;
1319 std::insert_iterator<DownloadSet> insert_it(remainder, remainder.begin());
1320 std::set_difference(all_sets[i]->begin(), all_sets[i]->end(),
1321 downloads_.begin(), downloads_.end(),
1322 insert_it);
1323 DCHECK(remainder.empty());
1324 std::insert_iterator<DownloadSet>
1325 insert_union(downloads_union, downloads_union.end());
1326 std::set_union(downloads_union.begin(), downloads_union.end(),
1327 all_sets[i]->begin(), all_sets[i]->end(),
1328 insert_union);
1329 } 1137 }
1330
1331 // Is everything in downloads_ present in one of the other sets?
1332 DownloadSet remainder;
1333 std::insert_iterator<DownloadSet>
1334 insert_remainder(remainder, remainder.begin());
1335 std::set_difference(downloads_.begin(), downloads_.end(),
1336 downloads_union.begin(), downloads_union.end(),
1337 insert_remainder);
1338 DCHECK(remainder.empty());
1339 #endif
1340 } 1138 }
1341 1139
1342 // DownloadManager::OtherDownloadManagerObserver implementation ---------------- 1140 // DownloadManager::OtherDownloadManagerObserver implementation ----------------
1343 1141
1344 DownloadManager::OtherDownloadManagerObserver::OtherDownloadManagerObserver( 1142 DownloadManager::OtherDownloadManagerObserver::OtherDownloadManagerObserver(
1345 DownloadManager* observing_download_manager) 1143 DownloadManager* observing_download_manager)
1346 : observing_download_manager_(observing_download_manager), 1144 : observing_download_manager_(observing_download_manager),
1347 observed_download_manager_(NULL) { 1145 observed_download_manager_(NULL) {
1348 if (observing_download_manager->profile_->GetOriginalProfile() == 1146 if (observing_download_manager->profile_->GetOriginalProfile() ==
1349 observing_download_manager->profile_) { 1147 observing_download_manager->profile_) {
(...skipping 10 matching lines...) Expand all
1360 observed_download_manager_->RemoveObserver(this); 1158 observed_download_manager_->RemoveObserver(this);
1361 } 1159 }
1362 1160
1363 void DownloadManager::OtherDownloadManagerObserver::ModelChanged() { 1161 void DownloadManager::OtherDownloadManagerObserver::ModelChanged() {
1364 observing_download_manager_->NotifyModelChanged(); 1162 observing_download_manager_->NotifyModelChanged();
1365 } 1163 }
1366 1164
1367 void DownloadManager::OtherDownloadManagerObserver::ManagerGoingDown() { 1165 void DownloadManager::OtherDownloadManagerObserver::ManagerGoingDown() {
1368 observed_download_manager_ = NULL; 1166 observed_download_manager_ = NULL;
1369 } 1167 }
OLDNEW
« no previous file with comments | « chrome/browser/download/download_manager.h ('k') | chrome/browser/download/download_prefs.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698