1// Copyright (c) 2012 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 "content/browser/download/download_manager_impl.h"
6
7#include <iterator>
8
9#include "base/bind.h"
10#include "base/callback.h"
11#include "base/debug/alias.h"
12#include "base/i18n/case_conversion.h"
13#include "base/logging.h"
14#include "base/message_loop/message_loop.h"
15#include "base/stl_util.h"
16#include "base/strings/stringprintf.h"
17#include "base/strings/sys_string_conversions.h"
18#include "base/supports_user_data.h"
19#include "base/synchronization/lock.h"
20#include "build/build_config.h"
21#include "content/browser/byte_stream.h"
22#include "content/browser/download/download_create_info.h"
23#include "content/browser/download/download_file_factory.h"
24#include "content/browser/download/download_item_factory.h"
25#include "content/browser/download/download_item_impl.h"
26#include "content/browser/download/download_stats.h"
27#include "content/browser/loader/resource_dispatcher_host_impl.h"
28#include "content/browser/renderer_host/render_view_host_impl.h"
29#include "content/browser/web_contents/web_contents_impl.h"
30#include "content/public/browser/browser_context.h"
31#include "content/public/browser/browser_thread.h"
32#include "content/public/browser/content_browser_client.h"
33#include "content/public/browser/download_interrupt_reasons.h"
34#include "content/public/browser/download_manager_delegate.h"
35#include "content/public/browser/download_url_parameters.h"
36#include "content/public/browser/notification_service.h"
37#include "content/public/browser/notification_types.h"
38#include "content/public/browser/render_process_host.h"
39#include "content/public/browser/resource_context.h"
40#include "content/public/browser/web_contents_delegate.h"
41#include "content/public/common/referrer.h"
42#include "net/base/load_flags.h"
43#include "net/base/upload_bytes_element_reader.h"
44#include "net/base/upload_data_stream.h"
45#include "net/url_request/url_request_context.h"
46
47namespace content {
48namespace {
49
50void BeginDownload(scoped_ptr<DownloadUrlParameters> params,
51                   uint32 download_id) {
52  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
53  // ResourceDispatcherHost{Base} is-not-a URLRequest::Delegate, and
54  // DownloadUrlParameters can-not include resource_dispatcher_host_impl.h, so
55  // we must down cast. RDHI is the only subclass of RDH as of 2012 May 4.
56  scoped_ptr<net::URLRequest> request(
57      params->resource_context()->GetRequestContext()->CreateRequest(
58          params->url(), NULL));
59  request->set_load_flags(request->load_flags() | params->load_flags());
60  request->set_method(params->method());
61  if (!params->post_body().empty()) {
62    const std::string& body = params->post_body();
63    scoped_ptr<net::UploadElementReader> reader(
64        net::UploadOwnedBytesElementReader::CreateWithString(body));
65    request->set_upload(make_scoped_ptr(
66        net::UploadDataStream::CreateWithReader(reader.Pass(), 0)));
67  }
68  if (params->post_id() >= 0) {
69    // The POST in this case does not have an actual body, and only works
70    // when retrieving data from cache. This is done because we don't want
71    // to do a re-POST without user consent, and currently don't have a good
72    // plan on how to display the UI for that.
73    DCHECK(params->prefer_cache());
74    DCHECK(params->method() == "POST");
75    ScopedVector<net::UploadElementReader> element_readers;
76    request->set_upload(make_scoped_ptr(
77        new net::UploadDataStream(&element_readers, params->post_id())));
78  }
79
80  // If we're not at the beginning of the file, retrieve only the remaining
81  // portion.
82  bool has_last_modified = !params->last_modified().empty();
83  bool has_etag = !params->etag().empty();
84
85  // If we've asked for a range, we want to make sure that we only
86  // get that range if our current copy of the information is good.
87  // We shouldn't be asked to continue if we don't have a verifier.
88  DCHECK(params->offset() == 0 || has_etag || has_last_modified);
89
90  if (params->offset() > 0) {
91    request->SetExtraRequestHeaderByName(
92        "Range",
93        base::StringPrintf("bytes=%" PRId64 "-", params->offset()),
94        true);
95
96    if (has_last_modified) {
97      request->SetExtraRequestHeaderByName("If-Unmodified-Since",
98                                           params->last_modified(),
99                                           true);
100    }
101    if (has_etag) {
102      request->SetExtraRequestHeaderByName("If-Match", params->etag(), true);
103    }
104  }
105
106  for (DownloadUrlParameters::RequestHeadersType::const_iterator iter
107           = params->request_headers_begin();
108       iter != params->request_headers_end();
109       ++iter) {
110    request->SetExtraRequestHeaderByName(
111        iter->first, iter->second, false/*overwrite*/);
112  }
113
114  scoped_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo());
115  save_info->file_path = params->file_path();
116  save_info->suggested_name = params->suggested_name();
117  save_info->offset = params->offset();
118  save_info->hash_state = params->hash_state();
119  save_info->prompt_for_save_location = params->prompt();
120  save_info->file_stream = params->GetFileStream();
121
122  ResourceDispatcherHost::Get()->BeginDownload(
123      request.Pass(),
124      params->referrer(),
125      params->content_initiated(),
126      params->resource_context(),
127      params->render_process_host_id(),
128      params->render_view_host_routing_id(),
129      params->prefer_cache(),
130      save_info.Pass(),
131      download_id,
132      params->callback());
133}
134
135class MapValueIteratorAdapter {
136 public:
137  explicit MapValueIteratorAdapter(
138      base::hash_map<int64, DownloadItem*>::const_iterator iter)
139    : iter_(iter) {
140  }
141  ~MapValueIteratorAdapter() {}
142
143  DownloadItem* operator*() { return iter_->second; }
144
145  MapValueIteratorAdapter& operator++() {
146    ++iter_;
147    return *this;
148  }
149
150  bool operator!=(const MapValueIteratorAdapter& that) const {
151    return iter_ != that.iter_;
152  }
153
154 private:
155  base::hash_map<int64, DownloadItem*>::const_iterator iter_;
156  // Allow copy and assign.
157};
158
159void EnsureNoPendingDownloadJobsOnFile(bool* result) {
160  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
161  *result = (DownloadFile::GetNumberOfDownloadFiles() == 0);
162  BrowserThread::PostTask(
163      BrowserThread::UI, FROM_HERE, base::MessageLoop::QuitClosure());
164}
165
166class DownloadItemFactoryImpl : public DownloadItemFactory {
167 public:
168  DownloadItemFactoryImpl() {}
169  virtual ~DownloadItemFactoryImpl() {}
170
171  virtual DownloadItemImpl* CreatePersistedItem(
172      DownloadItemImplDelegate* delegate,
173      uint32 download_id,
174      const base::FilePath& current_path,
175      const base::FilePath& target_path,
176      const std::vector<GURL>& url_chain,
177      const GURL& referrer_url,
178      const base::Time& start_time,
179      const base::Time& end_time,
180      const std::string& etag,
181      const std::string& last_modified,
182      int64 received_bytes,
183      int64 total_bytes,
184      DownloadItem::DownloadState state,
185      DownloadDangerType danger_type,
186      DownloadInterruptReason interrupt_reason,
187      bool opened,
188      const net::BoundNetLog& bound_net_log) OVERRIDE {
189    return new DownloadItemImpl(
190        delegate,
191        download_id,
192        current_path,
193        target_path,
194        url_chain,
195        referrer_url,
196        start_time,
197        end_time,
198        etag,
199        last_modified,
200        received_bytes,
201        total_bytes,
202        state,
203        danger_type,
204        interrupt_reason,
205        opened,
206        bound_net_log);
207  }
208
209  virtual DownloadItemImpl* CreateActiveItem(
210      DownloadItemImplDelegate* delegate,
211      uint32 download_id,
212      const DownloadCreateInfo& info,
213      const net::BoundNetLog& bound_net_log) OVERRIDE {
214    return new DownloadItemImpl(delegate, download_id, info, bound_net_log);
215  }
216
217  virtual DownloadItemImpl* CreateSavePageItem(
218      DownloadItemImplDelegate* delegate,
219      uint32 download_id,
220      const base::FilePath& path,
221      const GURL& url,
222      const std::string& mime_type,
223      scoped_ptr<DownloadRequestHandleInterface> request_handle,
224      const net::BoundNetLog& bound_net_log) OVERRIDE {
225    return new DownloadItemImpl(delegate, download_id, path, url,
226                                mime_type, request_handle.Pass(),
227                                bound_net_log);
228  }
229};
230
231}  // namespace
232
233DownloadManagerImpl::DownloadManagerImpl(
234    net::NetLog* net_log,
235    BrowserContext* browser_context)
236    : item_factory_(new DownloadItemFactoryImpl()),
237      file_factory_(new DownloadFileFactory()),
238      history_size_(0),
239      shutdown_needed_(true),
240      browser_context_(browser_context),
241      delegate_(NULL),
242      net_log_(net_log),
243      weak_factory_(this) {
244  DCHECK(browser_context);
245}
246
247DownloadManagerImpl::~DownloadManagerImpl() {
248  DCHECK(!shutdown_needed_);
249}
250
251DownloadItemImpl* DownloadManagerImpl::CreateActiveItem(
252    uint32 id, const DownloadCreateInfo& info) {
253  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
254  DCHECK(!ContainsKey(downloads_, id));
255  net::BoundNetLog bound_net_log =
256      net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_DOWNLOAD);
257  DownloadItemImpl* download =
258      item_factory_->CreateActiveItem(this, id, info, bound_net_log);
259  downloads_[id] = download;
260  return download;
261}
262
263void DownloadManagerImpl::GetNextId(const DownloadIdCallback& callback) {
264  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
265  if (delegate_) {
266    delegate_->GetNextId(callback);
267    return;
268  }
269  static uint32 next_id = content::DownloadItem::kInvalidId + 1;
270  callback.Run(next_id++);
271}
272
273void DownloadManagerImpl::DetermineDownloadTarget(
274    DownloadItemImpl* item, const DownloadTargetCallback& callback) {
275  // Note that this next call relies on
276  // DownloadItemImplDelegate::DownloadTargetCallback and
277  // DownloadManagerDelegate::DownloadTargetCallback having the same
278  // type.  If the types ever diverge, gasket code will need to
279  // be written here.
280  if (!delegate_ || !delegate_->DetermineDownloadTarget(item, callback)) {
281    base::FilePath target_path = item->GetForcedFilePath();
282    // TODO(asanka): Determine a useful path if |target_path| is empty.
283    callback.Run(target_path,
284                 DownloadItem::TARGET_DISPOSITION_OVERWRITE,
285                 DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
286                 target_path);
287  }
288}
289
290bool DownloadManagerImpl::ShouldCompleteDownload(
291    DownloadItemImpl* item, const base::Closure& complete_callback) {
292  if (!delegate_ ||
293      delegate_->ShouldCompleteDownload(item, complete_callback)) {
294    return true;
295  }
296  // Otherwise, the delegate has accepted responsibility to run the
297  // callback when the download is ready for completion.
298  return false;
299}
300
301bool DownloadManagerImpl::ShouldOpenFileBasedOnExtension(
302    const base::FilePath& path) {
303  if (!delegate_)
304    return false;
305
306  return delegate_->ShouldOpenFileBasedOnExtension(path);
307}
308
309bool DownloadManagerImpl::ShouldOpenDownload(
310    DownloadItemImpl* item, const ShouldOpenDownloadCallback& callback) {
311  if (!delegate_)
312    return true;
313
314  // Relies on DownloadItemImplDelegate::ShouldOpenDownloadCallback and
315  // DownloadManagerDelegate::DownloadOpenDelayedCallback "just happening"
316  // to have the same type :-}.
317  return delegate_->ShouldOpenDownload(item, callback);
318}
319
320void DownloadManagerImpl::SetDelegate(DownloadManagerDelegate* delegate) {
321  delegate_ = delegate;
322}
323
324DownloadManagerDelegate* DownloadManagerImpl::GetDelegate() const {
325  return delegate_;
326}
327
328void DownloadManagerImpl::Shutdown() {
329  VLOG(20) << __FUNCTION__ << "()"
330           << " shutdown_needed_ = " << shutdown_needed_;
331  if (!shutdown_needed_)
332    return;
333  shutdown_needed_ = false;
334
335  FOR_EACH_OBSERVER(Observer, observers_, ManagerGoingDown(this));
336  // TODO(benjhayden): Consider clearing observers_.
337
338  // If there are in-progress downloads, cancel them. This also goes for
339  // dangerous downloads which will remain in history if they aren't explicitly
340  // accepted or discarded. Canceling will remove the intermediate download
341  // file.
342  for (DownloadMap::iterator it = downloads_.begin(); it != downloads_.end();
343       ++it) {
344    DownloadItemImpl* download = it->second;
345    if (download->GetState() == DownloadItem::IN_PROGRESS)
346      download->Cancel(false);
347  }
348  STLDeleteValues(&downloads_);
349  downloads_.clear();
350
351  // We'll have nothing more to report to the observers after this point.
352  observers_.Clear();
353
354  if (delegate_)
355    delegate_->Shutdown();
356  delegate_ = NULL;
357}
358
359void DownloadManagerImpl::StartDownload(
360    scoped_ptr<DownloadCreateInfo> info,
361    scoped_ptr<ByteStreamReader> stream,
362    const DownloadUrlParameters::OnStartedCallback& on_started) {
363  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
364  DCHECK(info);
365  uint32 download_id = info->download_id;
366  const bool new_download = (download_id == content::DownloadItem::kInvalidId);
367  base::Callback<void(uint32)> got_id(base::Bind(
368      &DownloadManagerImpl::StartDownloadWithId,
369      weak_factory_.GetWeakPtr(),
370      base::Passed(info.Pass()),
371      base::Passed(stream.Pass()),
372      on_started,
373      new_download));
374  if (new_download) {
375    GetNextId(got_id);
376  } else {
377    got_id.Run(download_id);
378  }
379}
380
381void DownloadManagerImpl::StartDownloadWithId(
382    scoped_ptr<DownloadCreateInfo> info,
383    scoped_ptr<ByteStreamReader> stream,
384    const DownloadUrlParameters::OnStartedCallback& on_started,
385    bool new_download,
386    uint32 id) {
387  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
388  DCHECK_NE(content::DownloadItem::kInvalidId, id);
389  DownloadItemImpl* download = NULL;
390  if (new_download) {
391    download = CreateActiveItem(id, *info);
392  } else {
393    DownloadMap::iterator item_iterator = downloads_.find(id);
394    // Trying to resume an interrupted download.
395    if (item_iterator == downloads_.end() ||
396        (item_iterator->second->GetState() == DownloadItem::CANCELLED)) {
397      // If the download is no longer known to the DownloadManager, then it was
398      // removed after it was resumed. Ignore. If the download is cancelled
399      // while resuming, then also ignore the request.
400      info->request_handle.CancelRequest();
401      if (!on_started.is_null())
402        on_started.Run(NULL, net::ERR_ABORTED);
403      return;
404    }
405    download = item_iterator->second;
406    DCHECK_EQ(DownloadItem::INTERRUPTED, download->GetState());
407  }
408
409  base::FilePath default_download_directory;
410  if (delegate_) {
411    base::FilePath website_save_directory;  // Unused
412    bool skip_dir_check = false;            // Unused
413    delegate_->GetSaveDir(GetBrowserContext(), &website_save_directory,
414                          &default_download_directory, &skip_dir_check);
415  }
416
417  // Create the download file and start the download.
418  scoped_ptr<DownloadFile> download_file(
419      file_factory_->CreateFile(
420          info->save_info.Pass(), default_download_directory,
421          info->url(), info->referrer_url,
422          delegate_->GenerateFileHash(),
423          stream.Pass(), download->GetBoundNetLog(),
424          download->DestinationObserverAsWeakPtr()));
425
426  // Attach the client ID identifying the app to the AV system.
427  if (download_file.get() && delegate_) {
428    download_file->SetClientGuid(
429        delegate_->ApplicationClientIdForFileScanning());
430  }
431
432  scoped_ptr<DownloadRequestHandleInterface> req_handle(
433      new DownloadRequestHandle(info->request_handle));
434  download->Start(download_file.Pass(), req_handle.Pass());
435
436  // For interrupted downloads, Start() will transition the state to
437  // IN_PROGRESS and consumers will be notified via OnDownloadUpdated().
438  // For new downloads, we notify here, rather than earlier, so that
439  // the download_file is bound to download and all the usual
440  // setters (e.g. Cancel) work.
441  if (new_download)
442    FOR_EACH_OBSERVER(Observer, observers_, OnDownloadCreated(this, download));
443
444  if (!on_started.is_null())
445    on_started.Run(download, net::OK);
446}
447
448void DownloadManagerImpl::CheckForHistoryFilesRemoval() {
449  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
450  for (DownloadMap::iterator it = downloads_.begin();
451       it != downloads_.end(); ++it) {
452    DownloadItemImpl* item = it->second;
453    CheckForFileRemoval(item);
454  }
455}
456
457void DownloadManagerImpl::CheckForFileRemoval(DownloadItemImpl* download_item) {
458  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
459  if ((download_item->GetState() == DownloadItem::COMPLETE) &&
460      !download_item->GetFileExternallyRemoved() &&
461      delegate_) {
462    delegate_->CheckForFileExistence(
463        download_item,
464        base::Bind(&DownloadManagerImpl::OnFileExistenceChecked,
465                   weak_factory_.GetWeakPtr(), download_item->GetId()));
466  }
467}
468
469void DownloadManagerImpl::OnFileExistenceChecked(uint32 download_id,
470                                                 bool result) {
471  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
472  if (!result) {  // File does not exist.
473    if (ContainsKey(downloads_, download_id))
474      downloads_[download_id]->OnDownloadedFileRemoved();
475  }
476}
477
478BrowserContext* DownloadManagerImpl::GetBrowserContext() const {
479  return browser_context_;
480}
481
482void DownloadManagerImpl::CreateSavePackageDownloadItem(
483    const base::FilePath& main_file_path,
484    const GURL& page_url,
485    const std::string& mime_type,
486    scoped_ptr<DownloadRequestHandleInterface> request_handle,
487    const DownloadItemImplCreated& item_created) {
488  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
489  GetNextId(base::Bind(
490      &DownloadManagerImpl::CreateSavePackageDownloadItemWithId,
491      weak_factory_.GetWeakPtr(),
492      main_file_path,
493      page_url,
494      mime_type,
495      base::Passed(request_handle.Pass()),
496      item_created));
497}
498
499void DownloadManagerImpl::CreateSavePackageDownloadItemWithId(
500    const base::FilePath& main_file_path,
501    const GURL& page_url,
502    const std::string& mime_type,
503    scoped_ptr<DownloadRequestHandleInterface> request_handle,
504    const DownloadItemImplCreated& item_created,
505    uint32 id) {
506  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
507  DCHECK_NE(content::DownloadItem::kInvalidId, id);
508  DCHECK(!ContainsKey(downloads_, id));
509  net::BoundNetLog bound_net_log =
510      net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_DOWNLOAD);
511  DownloadItemImpl* download_item = item_factory_->CreateSavePageItem(
512      this,
513      id,
514      main_file_path,
515      page_url,
516      mime_type,
517      request_handle.Pass(),
518      bound_net_log);
519  downloads_[download_item->GetId()] = download_item;
520  FOR_EACH_OBSERVER(Observer, observers_, OnDownloadCreated(
521      this, download_item));
522  if (!item_created.is_null())
523    item_created.Run(download_item);
524}
525
526void DownloadManagerImpl::OnSavePackageSuccessfullyFinished(
527    DownloadItem* download_item) {
528  FOR_EACH_OBSERVER(Observer, observers_,
529                    OnSavePackageSuccessfullyFinished(this, download_item));
530}
531
532// Resume a download of a specific URL. We send the request to the
533// ResourceDispatcherHost, and let it send us responses like a regular
534// download.
535void DownloadManagerImpl::ResumeInterruptedDownload(
536    scoped_ptr<content::DownloadUrlParameters> params,
537    uint32 id) {
538  RecordDownloadSource(INITIATED_BY_RESUMPTION);
539  BrowserThread::PostTask(
540      BrowserThread::IO,
541      FROM_HERE,
542      base::Bind(&BeginDownload, base::Passed(&params), id));
543}
544
545void DownloadManagerImpl::SetDownloadItemFactoryForTesting(
546    scoped_ptr<DownloadItemFactory> item_factory) {
547  item_factory_ = item_factory.Pass();
548}
549
550void DownloadManagerImpl::SetDownloadFileFactoryForTesting(
551    scoped_ptr<DownloadFileFactory> file_factory) {
552  file_factory_ = file_factory.Pass();
553}
554
555DownloadFileFactory* DownloadManagerImpl::GetDownloadFileFactoryForTesting() {
556  return file_factory_.get();
557}
558
559void DownloadManagerImpl::DownloadRemoved(DownloadItemImpl* download) {
560  if (!download)
561    return;
562
563  uint32 download_id = download->GetId();
564  if (downloads_.find(download_id) == downloads_.end())
565    return;
566
567  delete download;
568  downloads_.erase(download_id);
569}
570
571int DownloadManagerImpl::RemoveDownloadsBetween(base::Time remove_begin,
572                                                base::Time remove_end) {
573  int count = 0;
574  DownloadMap::const_iterator it = downloads_.begin();
575  while (it != downloads_.end()) {
576    DownloadItemImpl* download = it->second;
577
578    // Increment done here to protect against invalidation below.
579    ++it;
580
581    if (download->GetStartTime() >= remove_begin &&
582        (remove_end.is_null() || download->GetStartTime() < remove_end) &&
583        (download->GetState() != DownloadItem::IN_PROGRESS)) {
584      // Erases the download from downloads_.
585      download->Remove();
586      count++;
587    }
588  }
589  return count;
590}
591
592int DownloadManagerImpl::RemoveDownloads(base::Time remove_begin) {
593  return RemoveDownloadsBetween(remove_begin, base::Time());
594}
595
596int DownloadManagerImpl::RemoveAllDownloads() {
597  // The null times make the date range unbounded.
598  int num_deleted = RemoveDownloadsBetween(base::Time(), base::Time());
599  RecordClearAllSize(num_deleted);
600  return num_deleted;
601}
602
603void DownloadManagerImpl::DownloadUrl(
604    scoped_ptr<DownloadUrlParameters> params) {
605  if (params->post_id() >= 0) {
606    // Check this here so that the traceback is more useful.
607    DCHECK(params->prefer_cache());
608    DCHECK(params->method() == "POST");
609  }
610  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(
611      &BeginDownload, base::Passed(&params),
612      content::DownloadItem::kInvalidId));
613}
614
615void DownloadManagerImpl::AddObserver(Observer* observer) {
616  observers_.AddObserver(observer);
617}
618
619void DownloadManagerImpl::RemoveObserver(Observer* observer) {
620  observers_.RemoveObserver(observer);
621}
622
623DownloadItem* DownloadManagerImpl::CreateDownloadItem(
624    uint32 id,
625    const base::FilePath& current_path,
626    const base::FilePath& target_path,
627    const std::vector<GURL>& url_chain,
628    const GURL& referrer_url,
629    const base::Time& start_time,
630    const base::Time& end_time,
631    const std::string& etag,
632    const std::string& last_modified,
633    int64 received_bytes,
634    int64 total_bytes,
635    DownloadItem::DownloadState state,
636    DownloadDangerType danger_type,
637    DownloadInterruptReason interrupt_reason,
638    bool opened) {
639  DCHECK(!ContainsKey(downloads_, id));
640  if (ContainsKey(downloads_, id))
641    return NULL;
642  DownloadItemImpl* item = item_factory_->CreatePersistedItem(
643      this,
644      id,
645      current_path,
646      target_path,
647      url_chain,
648      referrer_url,
649      start_time,
650      end_time,
651      etag,
652      last_modified,
653      received_bytes,
654      total_bytes,
655      state,
656      danger_type,
657      interrupt_reason,
658      opened,
659      net::BoundNetLog::Make(net_log_, net::NetLog::SOURCE_DOWNLOAD));
660  downloads_[id] = item;
661  FOR_EACH_OBSERVER(Observer, observers_, OnDownloadCreated(this, item));
662  VLOG(20) << __FUNCTION__ << "() download = " << item->DebugString(true);
663  return item;
664}
665
666int DownloadManagerImpl::InProgressCount() const {
667  int count = 0;
668  for (DownloadMap::const_iterator it = downloads_.begin();
669       it != downloads_.end(); ++it) {
670    if (it->second->GetState() == DownloadItem::IN_PROGRESS)
671      ++count;
672  }
673  return count;
674}
675
676DownloadItem* DownloadManagerImpl::GetDownload(uint32 download_id) {
677  return ContainsKey(downloads_, download_id) ? downloads_[download_id] : NULL;
678}
679
680void DownloadManagerImpl::GetAllDownloads(DownloadVector* downloads) {
681  for (DownloadMap::iterator it = downloads_.begin();
682       it != downloads_.end(); ++it) {
683    downloads->push_back(it->second);
684  }
685}
686
687void DownloadManagerImpl::OpenDownload(DownloadItemImpl* download) {
688  int num_unopened = 0;
689  for (DownloadMap::iterator it = downloads_.begin();
690       it != downloads_.end(); ++it) {
691    DownloadItemImpl* item = it->second;
692    if ((item->GetState() == DownloadItem::COMPLETE) &&
693        !item->GetOpened())
694      ++num_unopened;
695  }
696  RecordOpensOutstanding(num_unopened);
697
698  if (delegate_)
699    delegate_->OpenDownload(download);
700}
701
702void DownloadManagerImpl::ShowDownloadInShell(DownloadItemImpl* download) {
703  if (delegate_)
704    delegate_->ShowDownloadInShell(download);
705}
706
707}  // namespace content
708