// Copyright (c) 2011 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/ui/webui/downloads_dom_handler.h" #include <algorithm> #include <functional> #include "base/basictypes.h" #include "base/callback.h" #include "base/memory/singleton.h" #include "base/string_piece.h" #include "base/threading/thread.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/download/download_history.h" #include "chrome/browser/download/download_item.h" #include "chrome/browser/download/download_util.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/chrome_url_data_manager.h" #include "chrome/browser/ui/webui/fileicon_source.h" #include "chrome/common/jstemplate_builder.h" #include "chrome/common/url_constants.h" #include "content/browser/browser_thread.h" #include "content/browser/tab_contents/tab_contents.h" #include "grit/generated_resources.h" #include "ui/gfx/image.h" namespace { // Maximum number of downloads to show. TODO(glen): Remove this and instead // stuff the downloads down the pipe slowly. static const int kMaxDownloads = 150; // Sort DownloadItems into descending order by their start time. class DownloadItemSorter : public std::binary_function<DownloadItem*, DownloadItem*, bool> { public: bool operator()(const DownloadItem* lhs, const DownloadItem* rhs) { return lhs->start_time() > rhs->start_time(); } }; } // namespace DownloadsDOMHandler::DownloadsDOMHandler(DownloadManager* dlm) : search_text_(), download_manager_(dlm), callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { // Create our fileicon data source. dlm->profile()->GetChromeURLDataManager()->AddDataSource( new FileIconSource()); } DownloadsDOMHandler::~DownloadsDOMHandler() { ClearDownloadItems(); download_manager_->RemoveObserver(this); } // DownloadsDOMHandler, public: ----------------------------------------------- void DownloadsDOMHandler::Init() { download_manager_->AddObserver(this); } void DownloadsDOMHandler::RegisterMessages() { web_ui_->RegisterMessageCallback("getDownloads", NewCallback(this, &DownloadsDOMHandler::HandleGetDownloads)); web_ui_->RegisterMessageCallback("openFile", NewCallback(this, &DownloadsDOMHandler::HandleOpenFile)); web_ui_->RegisterMessageCallback("drag", NewCallback(this, &DownloadsDOMHandler::HandleDrag)); web_ui_->RegisterMessageCallback("saveDangerous", NewCallback(this, &DownloadsDOMHandler::HandleSaveDangerous)); web_ui_->RegisterMessageCallback("discardDangerous", NewCallback(this, &DownloadsDOMHandler::HandleDiscardDangerous)); web_ui_->RegisterMessageCallback("show", NewCallback(this, &DownloadsDOMHandler::HandleShow)); web_ui_->RegisterMessageCallback("togglepause", NewCallback(this, &DownloadsDOMHandler::HandlePause)); web_ui_->RegisterMessageCallback("resume", NewCallback(this, &DownloadsDOMHandler::HandlePause)); web_ui_->RegisterMessageCallback("remove", NewCallback(this, &DownloadsDOMHandler::HandleRemove)); web_ui_->RegisterMessageCallback("cancel", NewCallback(this, &DownloadsDOMHandler::HandleCancel)); web_ui_->RegisterMessageCallback("clearAll", NewCallback(this, &DownloadsDOMHandler::HandleClearAll)); } void DownloadsDOMHandler::OnDownloadUpdated(DownloadItem* download) { // Get the id for the download. Our downloads are sorted latest to first, // and the id is the index into that list. We should be careful of sync // errors between the UI and the download_items_ list (we may wish to use // something other than 'id'). OrderedDownloads::iterator it = find(download_items_.begin(), download_items_.end(), download); if (it == download_items_.end()) return; const int id = static_cast<int>(it - download_items_.begin()); ListValue results_value; results_value.Append(download_util::CreateDownloadItemValue(download, id)); web_ui_->CallJavascriptFunction("downloadUpdated", results_value); } // A download has started or been deleted. Query our DownloadManager for the // current set of downloads. void DownloadsDOMHandler::ModelChanged() { ClearDownloadItems(); download_manager_->SearchDownloads(WideToUTF16(search_text_), &download_items_); sort(download_items_.begin(), download_items_.end(), DownloadItemSorter()); // Scan for any in progress downloads and add ourself to them as an observer. for (OrderedDownloads::iterator it = download_items_.begin(); it != download_items_.end(); ++it) { if (static_cast<int>(it - download_items_.begin()) > kMaxDownloads) break; DownloadItem* download = *it; if (download->IsInProgress()) { // We want to know what happens as the download progresses. download->AddObserver(this); } else if (download->safety_state() == DownloadItem::DANGEROUS) { // We need to be notified when the user validates the dangerous download. download->AddObserver(this); } } SendCurrentDownloads(); } void DownloadsDOMHandler::HandleGetDownloads(const ListValue* args) { std::wstring new_search = UTF16ToWideHack(ExtractStringValue(args)); if (search_text_.compare(new_search) != 0) { search_text_ = new_search; ModelChanged(); } else { SendCurrentDownloads(); } } void DownloadsDOMHandler::HandleOpenFile(const ListValue* args) { DownloadItem* file = GetDownloadByValue(args); if (file) file->OpenDownload(); } void DownloadsDOMHandler::HandleDrag(const ListValue* args) { DownloadItem* file = GetDownloadByValue(args); if (file) { IconManager* im = g_browser_process->icon_manager(); gfx::Image* icon = im->LookupIcon(file->GetUserVerifiedFilePath(), IconLoader::NORMAL); gfx::NativeView view = web_ui_->tab_contents()->GetNativeView(); download_util::DragDownload(file, icon, view); } } void DownloadsDOMHandler::HandleSaveDangerous(const ListValue* args) { DownloadItem* file = GetDownloadByValue(args); if (file) download_manager_->DangerousDownloadValidated(file); } void DownloadsDOMHandler::HandleDiscardDangerous(const ListValue* args) { DownloadItem* file = GetDownloadByValue(args); if (file) file->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD); } void DownloadsDOMHandler::HandleShow(const ListValue* args) { DownloadItem* file = GetDownloadByValue(args); if (file) file->ShowDownloadInShell(); } void DownloadsDOMHandler::HandlePause(const ListValue* args) { DownloadItem* file = GetDownloadByValue(args); if (file) file->TogglePause(); } void DownloadsDOMHandler::HandleRemove(const ListValue* args) { DownloadItem* file = GetDownloadByValue(args); if (file) file->Remove(); } void DownloadsDOMHandler::HandleCancel(const ListValue* args) { DownloadItem* file = GetDownloadByValue(args); if (file) file->Cancel(true); } void DownloadsDOMHandler::HandleClearAll(const ListValue* args) { download_manager_->RemoveAllDownloads(); } // DownloadsDOMHandler, private: ---------------------------------------------- void DownloadsDOMHandler::SendCurrentDownloads() { ListValue results_value; for (OrderedDownloads::iterator it = download_items_.begin(); it != download_items_.end(); ++it) { int index = static_cast<int>(it - download_items_.begin()); if (index > kMaxDownloads) break; results_value.Append(download_util::CreateDownloadItemValue(*it, index)); } web_ui_->CallJavascriptFunction("downloadsList", results_value); } void DownloadsDOMHandler::ClearDownloadItems() { // Clear out old state and remove self as observer for each download. for (OrderedDownloads::iterator it = download_items_.begin(); it != download_items_.end(); ++it) { (*it)->RemoveObserver(this); } download_items_.clear(); } DownloadItem* DownloadsDOMHandler::GetDownloadById(int id) { for (OrderedDownloads::iterator it = download_items_.begin(); it != download_items_.end(); ++it) { if (static_cast<int>(it - download_items_.begin() == id)) { return (*it); } } return NULL; } DownloadItem* DownloadsDOMHandler::GetDownloadByValue(const ListValue* args) { int id; if (ExtractIntegerValue(args, &id)) { return GetDownloadById(id); } return NULL; }