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