// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/download/download_status_updater.h" #include #include "base/logging.h" #include "base/stl_util.h" #if defined(OS_LINUX) && !defined(OS_CHROMEOS) #include "ui/views/linux_ui/linux_ui.h" #endif namespace { // DownloadStatusUpdater::UpdateAppIconDownloadProgress() expects to only be // called once when a DownloadItem completes, then not again (except perhaps // until it is resumed). The existence of WasInProgressData is effectively a // boolean that indicates whether that final UpdateAppIconDownloadProgress() // call has been made for a given DownloadItem. It is expected that there will // be many more non-in-progress downloads than in-progress downloads, so // WasInProgressData is set for in-progress downloads and cleared from // non-in-progress downloads instead of the other way around in order to save // memory. class WasInProgressData : public base::SupportsUserData::Data { public: static bool Get(content::DownloadItem* item) { return item->GetUserData(kKey) != NULL; } static void Clear(content::DownloadItem* item) { item->RemoveUserData(kKey); } explicit WasInProgressData(content::DownloadItem* item) { item->SetUserData(kKey, this); } private: static const char kKey[]; DISALLOW_COPY_AND_ASSIGN(WasInProgressData); }; const char WasInProgressData::kKey[] = "DownloadItem DownloadStatusUpdater WasInProgressData"; } // anonymous namespace DownloadStatusUpdater::DownloadStatusUpdater() { } DownloadStatusUpdater::~DownloadStatusUpdater() { STLDeleteElements(¬ifiers_); } bool DownloadStatusUpdater::GetProgress(float* progress, int* download_count) const { *progress = 0; *download_count = 0; bool progress_certain = true; int64 received_bytes = 0; int64 total_bytes = 0; for (std::vector::const_iterator it = notifiers_.begin(); it != notifiers_.end(); ++it) { if ((*it)->GetManager()) { content::DownloadManager::DownloadVector items; (*it)->GetManager()->GetAllDownloads(&items); for (content::DownloadManager::DownloadVector::const_iterator it = items.begin(); it != items.end(); ++it) { if ((*it)->GetState() == content::DownloadItem::IN_PROGRESS) { ++*download_count; if ((*it)->GetTotalBytes() <= 0) { // There may or may not be more data coming down this pipe. progress_certain = false; } else { received_bytes += (*it)->GetReceivedBytes(); total_bytes += (*it)->GetTotalBytes(); } } } } } if (total_bytes > 0) *progress = static_cast(received_bytes) / total_bytes; return progress_certain; } void DownloadStatusUpdater::AddManager(content::DownloadManager* manager) { notifiers_.push_back(new AllDownloadItemNotifier(manager, this)); content::DownloadManager::DownloadVector items; manager->GetAllDownloads(&items); for (content::DownloadManager::DownloadVector::const_iterator it = items.begin(); it != items.end(); ++it) { OnDownloadCreated(manager, *it); } } void DownloadStatusUpdater::OnDownloadCreated( content::DownloadManager* manager, content::DownloadItem* item) { // Ignore downloads loaded from history, which are in a terminal state. // TODO(benjhayden): Use the Observer interface to distinguish between // historical and started downloads. if (item->GetState() == content::DownloadItem::IN_PROGRESS) { UpdateAppIconDownloadProgress(item); new WasInProgressData(item); } // else, the lack of WasInProgressData indicates to OnDownloadUpdated that it // should not call UpdateAppIconDownloadProgress(). } void DownloadStatusUpdater::OnDownloadUpdated( content::DownloadManager* manager, content::DownloadItem* item) { if (item->GetState() == content::DownloadItem::IN_PROGRESS) { // If the item was interrupted/cancelled and then resumed/restarted, then // set WasInProgress so that UpdateAppIconDownloadProgress() will be called // when it completes. if (!WasInProgressData::Get(item)) new WasInProgressData(item); } else { // The item is now in a terminal state. If it was already in a terminal // state, then do not call UpdateAppIconDownloadProgress() again. If it is // now transitioning to a terminal state, then clear its WasInProgressData // so that UpdateAppIconDownloadProgress() won't be called after this final // call. if (!WasInProgressData::Get(item)) return; WasInProgressData::Clear(item); } UpdateAppIconDownloadProgress(item); } #if defined(OS_ANDROID) || (defined(USE_AURA) && !defined(OS_WIN)) void DownloadStatusUpdater::UpdateAppIconDownloadProgress( content::DownloadItem* download) { #if defined(OS_LINUX) && !defined(OS_CHROMEOS) const views::LinuxUI* linux_ui = views::LinuxUI::instance(); if (linux_ui) { float progress = 0; int download_count = 0; GetProgress(&progress, &download_count); linux_ui->SetDownloadCount(download_count); linux_ui->SetProgressFraction(progress); } #endif // TODO(avi): Implement for Android? } #endif