1// Copyright (c) 2011 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/ui/webui/downloads_dom_handler.h" 6 7#include <algorithm> 8#include <functional> 9 10#include "base/basictypes.h" 11#include "base/callback.h" 12#include "base/memory/singleton.h" 13#include "base/string_piece.h" 14#include "base/threading/thread.h" 15#include "base/utf_string_conversions.h" 16#include "base/values.h" 17#include "chrome/browser/browser_process.h" 18#include "chrome/browser/download/download_history.h" 19#include "chrome/browser/download/download_item.h" 20#include "chrome/browser/download/download_util.h" 21#include "chrome/browser/metrics/user_metrics.h" 22#include "chrome/browser/profiles/profile.h" 23#include "chrome/browser/ui/webui/chrome_url_data_manager.h" 24#include "chrome/browser/ui/webui/fileicon_source.h" 25#include "chrome/common/jstemplate_builder.h" 26#include "chrome/common/url_constants.h" 27#include "content/browser/browser_thread.h" 28#include "content/browser/tab_contents/tab_contents.h" 29#include "grit/generated_resources.h" 30#include "ui/gfx/image.h" 31 32namespace { 33 34// Maximum number of downloads to show. TODO(glen): Remove this and instead 35// stuff the downloads down the pipe slowly. 36static const int kMaxDownloads = 150; 37 38// Sort DownloadItems into descending order by their start time. 39class DownloadItemSorter : public std::binary_function<DownloadItem*, 40 DownloadItem*, 41 bool> { 42 public: 43 bool operator()(const DownloadItem* lhs, const DownloadItem* rhs) { 44 return lhs->start_time() > rhs->start_time(); 45 } 46}; 47 48} // namespace 49 50DownloadsDOMHandler::DownloadsDOMHandler(DownloadManager* dlm) 51 : search_text_(), 52 download_manager_(dlm), 53 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { 54 // Create our fileicon data source. 55 dlm->profile()->GetChromeURLDataManager()->AddDataSource( 56 new FileIconSource()); 57} 58 59DownloadsDOMHandler::~DownloadsDOMHandler() { 60 ClearDownloadItems(); 61 download_manager_->RemoveObserver(this); 62} 63 64// DownloadsDOMHandler, public: ----------------------------------------------- 65 66void DownloadsDOMHandler::Init() { 67 download_manager_->AddObserver(this); 68} 69 70void DownloadsDOMHandler::RegisterMessages() { 71 web_ui_->RegisterMessageCallback("getDownloads", 72 NewCallback(this, &DownloadsDOMHandler::HandleGetDownloads)); 73 web_ui_->RegisterMessageCallback("openFile", 74 NewCallback(this, &DownloadsDOMHandler::HandleOpenFile)); 75 76 web_ui_->RegisterMessageCallback("drag", 77 NewCallback(this, &DownloadsDOMHandler::HandleDrag)); 78 79 web_ui_->RegisterMessageCallback("saveDangerous", 80 NewCallback(this, &DownloadsDOMHandler::HandleSaveDangerous)); 81 web_ui_->RegisterMessageCallback("discardDangerous", 82 NewCallback(this, &DownloadsDOMHandler::HandleDiscardDangerous)); 83 web_ui_->RegisterMessageCallback("show", 84 NewCallback(this, &DownloadsDOMHandler::HandleShow)); 85 web_ui_->RegisterMessageCallback("togglepause", 86 NewCallback(this, &DownloadsDOMHandler::HandlePause)); 87 web_ui_->RegisterMessageCallback("resume", 88 NewCallback(this, &DownloadsDOMHandler::HandlePause)); 89 web_ui_->RegisterMessageCallback("remove", 90 NewCallback(this, &DownloadsDOMHandler::HandleRemove)); 91 web_ui_->RegisterMessageCallback("cancel", 92 NewCallback(this, &DownloadsDOMHandler::HandleCancel)); 93 web_ui_->RegisterMessageCallback("clearAll", 94 NewCallback(this, &DownloadsDOMHandler::HandleClearAll)); 95} 96 97void DownloadsDOMHandler::OnDownloadUpdated(DownloadItem* download) { 98 // Get the id for the download. Our downloads are sorted latest to first, 99 // and the id is the index into that list. We should be careful of sync 100 // errors between the UI and the download_items_ list (we may wish to use 101 // something other than 'id'). 102 OrderedDownloads::iterator it = find(download_items_.begin(), 103 download_items_.end(), 104 download); 105 if (it == download_items_.end()) 106 return; 107 const int id = static_cast<int>(it - download_items_.begin()); 108 109 ListValue results_value; 110 results_value.Append(download_util::CreateDownloadItemValue(download, id)); 111 web_ui_->CallJavascriptFunction("downloadUpdated", results_value); 112} 113 114// A download has started or been deleted. Query our DownloadManager for the 115// current set of downloads. 116void DownloadsDOMHandler::ModelChanged() { 117 ClearDownloadItems(); 118 download_manager_->SearchDownloads(WideToUTF16(search_text_), 119 &download_items_); 120 sort(download_items_.begin(), download_items_.end(), DownloadItemSorter()); 121 122 // Scan for any in progress downloads and add ourself to them as an observer. 123 for (OrderedDownloads::iterator it = download_items_.begin(); 124 it != download_items_.end(); ++it) { 125 if (static_cast<int>(it - download_items_.begin()) > kMaxDownloads) 126 break; 127 128 DownloadItem* download = *it; 129 if (download->IsInProgress()) { 130 // We want to know what happens as the download progresses. 131 download->AddObserver(this); 132 } else if (download->safety_state() == DownloadItem::DANGEROUS) { 133 // We need to be notified when the user validates the dangerous download. 134 download->AddObserver(this); 135 } 136 } 137 138 SendCurrentDownloads(); 139} 140 141void DownloadsDOMHandler::HandleGetDownloads(const ListValue* args) { 142 std::wstring new_search = UTF16ToWideHack(ExtractStringValue(args)); 143 if (search_text_.compare(new_search) != 0) { 144 search_text_ = new_search; 145 ModelChanged(); 146 } else { 147 SendCurrentDownloads(); 148 } 149} 150 151void DownloadsDOMHandler::HandleOpenFile(const ListValue* args) { 152 DownloadItem* file = GetDownloadByValue(args); 153 if (file) 154 file->OpenDownload(); 155} 156 157void DownloadsDOMHandler::HandleDrag(const ListValue* args) { 158 DownloadItem* file = GetDownloadByValue(args); 159 if (file) { 160 IconManager* im = g_browser_process->icon_manager(); 161 gfx::Image* icon = im->LookupIcon(file->GetUserVerifiedFilePath(), 162 IconLoader::NORMAL); 163 gfx::NativeView view = web_ui_->tab_contents()->GetNativeView(); 164 download_util::DragDownload(file, icon, view); 165 } 166} 167 168void DownloadsDOMHandler::HandleSaveDangerous(const ListValue* args) { 169 DownloadItem* file = GetDownloadByValue(args); 170 if (file) 171 download_manager_->DangerousDownloadValidated(file); 172} 173 174void DownloadsDOMHandler::HandleDiscardDangerous(const ListValue* args) { 175 DownloadItem* file = GetDownloadByValue(args); 176 if (file) 177 file->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD); 178} 179 180void DownloadsDOMHandler::HandleShow(const ListValue* args) { 181 DownloadItem* file = GetDownloadByValue(args); 182 if (file) 183 file->ShowDownloadInShell(); 184} 185 186void DownloadsDOMHandler::HandlePause(const ListValue* args) { 187 DownloadItem* file = GetDownloadByValue(args); 188 if (file) 189 file->TogglePause(); 190} 191 192void DownloadsDOMHandler::HandleRemove(const ListValue* args) { 193 DownloadItem* file = GetDownloadByValue(args); 194 if (file) 195 file->Remove(); 196} 197 198void DownloadsDOMHandler::HandleCancel(const ListValue* args) { 199 DownloadItem* file = GetDownloadByValue(args); 200 if (file) 201 file->Cancel(true); 202} 203 204void DownloadsDOMHandler::HandleClearAll(const ListValue* args) { 205 download_manager_->RemoveAllDownloads(); 206} 207 208// DownloadsDOMHandler, private: ---------------------------------------------- 209 210void DownloadsDOMHandler::SendCurrentDownloads() { 211 ListValue results_value; 212 for (OrderedDownloads::iterator it = download_items_.begin(); 213 it != download_items_.end(); ++it) { 214 int index = static_cast<int>(it - download_items_.begin()); 215 if (index > kMaxDownloads) 216 break; 217 results_value.Append(download_util::CreateDownloadItemValue(*it, index)); 218 } 219 220 web_ui_->CallJavascriptFunction("downloadsList", results_value); 221} 222 223void DownloadsDOMHandler::ClearDownloadItems() { 224 // Clear out old state and remove self as observer for each download. 225 for (OrderedDownloads::iterator it = download_items_.begin(); 226 it != download_items_.end(); ++it) { 227 (*it)->RemoveObserver(this); 228 } 229 download_items_.clear(); 230} 231 232DownloadItem* DownloadsDOMHandler::GetDownloadById(int id) { 233 for (OrderedDownloads::iterator it = download_items_.begin(); 234 it != download_items_.end(); ++it) { 235 if (static_cast<int>(it - download_items_.begin() == id)) { 236 return (*it); 237 } 238 } 239 240 return NULL; 241} 242 243DownloadItem* DownloadsDOMHandler::GetDownloadByValue(const ListValue* args) { 244 int id; 245 if (ExtractIntegerValue(args, &id)) { 246 return GetDownloadById(id); 247 } 248 return NULL; 249} 250