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(¬ifiers_); 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