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 32 namespace { 33 34 // Maximum number of downloads to show. TODO(glen): Remove this and instead 35 // stuff the downloads down the pipe slowly. 36 static const int kMaxDownloads = 150; 37 38 // Sort DownloadItems into descending order by their start time. 39 class 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 50 DownloadsDOMHandler::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 59 DownloadsDOMHandler::~DownloadsDOMHandler() { 60 ClearDownloadItems(); 61 download_manager_->RemoveObserver(this); 62 } 63 64 // DownloadsDOMHandler, public: ----------------------------------------------- 65 66 void DownloadsDOMHandler::Init() { 67 download_manager_->AddObserver(this); 68 } 69 70 void 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 97 void 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. 116 void 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 141 void 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 151 void DownloadsDOMHandler::HandleOpenFile(const ListValue* args) { 152 DownloadItem* file = GetDownloadByValue(args); 153 if (file) 154 file->OpenDownload(); 155 } 156 157 void 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 168 void DownloadsDOMHandler::HandleSaveDangerous(const ListValue* args) { 169 DownloadItem* file = GetDownloadByValue(args); 170 if (file) 171 download_manager_->DangerousDownloadValidated(file); 172 } 173 174 void DownloadsDOMHandler::HandleDiscardDangerous(const ListValue* args) { 175 DownloadItem* file = GetDownloadByValue(args); 176 if (file) 177 file->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD); 178 } 179 180 void DownloadsDOMHandler::HandleShow(const ListValue* args) { 181 DownloadItem* file = GetDownloadByValue(args); 182 if (file) 183 file->ShowDownloadInShell(); 184 } 185 186 void DownloadsDOMHandler::HandlePause(const ListValue* args) { 187 DownloadItem* file = GetDownloadByValue(args); 188 if (file) 189 file->TogglePause(); 190 } 191 192 void DownloadsDOMHandler::HandleRemove(const ListValue* args) { 193 DownloadItem* file = GetDownloadByValue(args); 194 if (file) 195 file->Remove(); 196 } 197 198 void DownloadsDOMHandler::HandleCancel(const ListValue* args) { 199 DownloadItem* file = GetDownloadByValue(args); 200 if (file) 201 file->Cancel(true); 202 } 203 204 void DownloadsDOMHandler::HandleClearAll(const ListValue* args) { 205 download_manager_->RemoveAllDownloads(); 206 } 207 208 // DownloadsDOMHandler, private: ---------------------------------------------- 209 210 void 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 223 void 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 232 DownloadItem* 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 243 DownloadItem* DownloadsDOMHandler::GetDownloadByValue(const ListValue* args) { 244 int id; 245 if (ExtractIntegerValue(args, &id)) { 246 return GetDownloadById(id); 247 } 248 return NULL; 249 } 250