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