Home | History | Annotate | Download | only in download
      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 "chrome/browser/download/download_status_updater.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/logging.h"
     10 #include "base/stl_util.h"
     11 #include "chrome/browser/download/download_util.h"
     12 
     13 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
     14 #include "ui/linux_ui/linux_ui.h"
     15 #endif
     16 
     17 namespace {
     18 
     19 // DownloadStatusUpdater::UpdateAppIconDownloadProgress() expects to only be
     20 // called once when a DownloadItem completes, then not again (except perhaps
     21 // until it is resumed). The existence of WasInProgressData is effectively a
     22 // boolean that indicates whether that final UpdateAppIconDownloadProgress()
     23 // call has been made for a given DownloadItem. It is expected that there will
     24 // be many more non-in-progress downloads than in-progress downloads, so
     25 // WasInProgressData is set for in-progress downloads and cleared from
     26 // non-in-progress downloads instead of the other way around in order to save
     27 // memory.
     28 class WasInProgressData : public base::SupportsUserData::Data {
     29  public:
     30   static bool Get(content::DownloadItem* item) {
     31     return item->GetUserData(kKey) != NULL;
     32   }
     33 
     34   static void Clear(content::DownloadItem* item) {
     35     item->RemoveUserData(kKey);
     36   }
     37 
     38   explicit WasInProgressData(content::DownloadItem* item) {
     39     item->SetUserData(kKey, this);
     40   }
     41 
     42  private:
     43   static const char kKey[];
     44   DISALLOW_COPY_AND_ASSIGN(WasInProgressData);
     45 };
     46 
     47 const char WasInProgressData::kKey[] =
     48   "DownloadItem DownloadStatusUpdater WasInProgressData";
     49 
     50 }  // anonymous namespace
     51 
     52 DownloadStatusUpdater::DownloadStatusUpdater() {
     53 }
     54 
     55 DownloadStatusUpdater::~DownloadStatusUpdater() {
     56   STLDeleteElements(&notifiers_);
     57 }
     58 
     59 bool DownloadStatusUpdater::GetProgress(float* progress,
     60                                         int* download_count) const {
     61   *progress = 0;
     62   *download_count = 0;
     63   bool progress_certain = true;
     64   int64 received_bytes = 0;
     65   int64 total_bytes = 0;
     66 
     67   for (std::vector<AllDownloadItemNotifier*>::const_iterator it =
     68        notifiers_.begin(); it != notifiers_.end(); ++it) {
     69     if ((*it)->GetManager()) {
     70       content::DownloadManager::DownloadVector items;
     71       (*it)->GetManager()->GetAllDownloads(&items);
     72       for (content::DownloadManager::DownloadVector::const_iterator it =
     73           items.begin(); it != items.end(); ++it) {
     74         if ((*it)->GetState() == content::DownloadItem::IN_PROGRESS) {
     75           ++*download_count;
     76           if ((*it)->GetTotalBytes() <= 0) {
     77             // There may or may not be more data coming down this pipe.
     78             progress_certain = false;
     79           } else {
     80             received_bytes += (*it)->GetReceivedBytes();
     81             total_bytes += (*it)->GetTotalBytes();
     82           }
     83         }
     84       }
     85     }
     86   }
     87 
     88   if (total_bytes > 0)
     89     *progress = static_cast<float>(received_bytes) / total_bytes;
     90   return progress_certain;
     91 }
     92 
     93 void DownloadStatusUpdater::AddManager(content::DownloadManager* manager) {
     94   notifiers_.push_back(new AllDownloadItemNotifier(manager, this));
     95   content::DownloadManager::DownloadVector items;
     96   manager->GetAllDownloads(&items);
     97   for (content::DownloadManager::DownloadVector::const_iterator
     98        it = items.begin(); it != items.end(); ++it) {
     99     OnDownloadCreated(manager, *it);
    100   }
    101 }
    102 
    103 void DownloadStatusUpdater::OnDownloadCreated(
    104     content::DownloadManager* manager, content::DownloadItem* item) {
    105   // Ignore downloads loaded from history, which are in a terminal state.
    106   // TODO(benjhayden): Use the Observer interface to distinguish between
    107   // historical and started downloads.
    108   if (item->GetState() == content::DownloadItem::IN_PROGRESS) {
    109     UpdateAppIconDownloadProgress(item);
    110     new WasInProgressData(item);
    111   }
    112   // else, the lack of WasInProgressData indicates to OnDownloadUpdated that it
    113   // should not call UpdateAppIconDownloadProgress().
    114 }
    115 
    116 void DownloadStatusUpdater::OnDownloadUpdated(
    117     content::DownloadManager* manager, content::DownloadItem* item) {
    118   if (item->GetState() == content::DownloadItem::IN_PROGRESS) {
    119     // If the item was interrupted/cancelled and then resumed/restarted, then
    120     // set WasInProgress so that UpdateAppIconDownloadProgress() will be called
    121     // when it completes.
    122     if (!WasInProgressData::Get(item))
    123       new WasInProgressData(item);
    124   } else {
    125     // The item is now in a terminal state. If it was already in a terminal
    126     // state, then do not call UpdateAppIconDownloadProgress() again. If it is
    127     // now transitioning to a terminal state, then clear its WasInProgressData
    128     // so that UpdateAppIconDownloadProgress() won't be called after this final
    129     // call.
    130     if (!WasInProgressData::Get(item))
    131       return;
    132     WasInProgressData::Clear(item);
    133   }
    134   UpdateAppIconDownloadProgress(item);
    135 }
    136 
    137 #if defined(USE_AURA) || defined(OS_ANDROID)
    138 void DownloadStatusUpdater::UpdateAppIconDownloadProgress(
    139     content::DownloadItem* download) {
    140 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
    141   const ui::LinuxUI* linux_ui = ui::LinuxUI::instance();
    142   if (linux_ui) {
    143     float progress = 0;
    144     int download_count = 0;
    145     GetProgress(&progress, &download_count);
    146     linux_ui->SetDownloadCount(download_count);
    147     linux_ui->SetProgressFraction(progress);
    148   }
    149 #endif
    150   // TODO(avi): Implement for Android?
    151 }
    152 #endif
    153