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:
operator ()(const DownloadItem * lhs,const DownloadItem * rhs)43 bool operator()(const DownloadItem* lhs, const DownloadItem* rhs) {
44 return lhs->start_time() > rhs->start_time();
45 }
46 };
47
48 } // namespace
49
DownloadsDOMHandler(DownloadManager * dlm)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
~DownloadsDOMHandler()59 DownloadsDOMHandler::~DownloadsDOMHandler() {
60 ClearDownloadItems();
61 download_manager_->RemoveObserver(this);
62 }
63
64 // DownloadsDOMHandler, public: -----------------------------------------------
65
Init()66 void DownloadsDOMHandler::Init() {
67 download_manager_->AddObserver(this);
68 }
69
RegisterMessages()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
OnDownloadUpdated(DownloadItem * download)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.
ModelChanged()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
HandleGetDownloads(const ListValue * args)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
HandleOpenFile(const ListValue * args)151 void DownloadsDOMHandler::HandleOpenFile(const ListValue* args) {
152 DownloadItem* file = GetDownloadByValue(args);
153 if (file)
154 file->OpenDownload();
155 }
156
HandleDrag(const ListValue * args)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
HandleSaveDangerous(const ListValue * args)168 void DownloadsDOMHandler::HandleSaveDangerous(const ListValue* args) {
169 DownloadItem* file = GetDownloadByValue(args);
170 if (file)
171 download_manager_->DangerousDownloadValidated(file);
172 }
173
HandleDiscardDangerous(const ListValue * args)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
HandleShow(const ListValue * args)180 void DownloadsDOMHandler::HandleShow(const ListValue* args) {
181 DownloadItem* file = GetDownloadByValue(args);
182 if (file)
183 file->ShowDownloadInShell();
184 }
185
HandlePause(const ListValue * args)186 void DownloadsDOMHandler::HandlePause(const ListValue* args) {
187 DownloadItem* file = GetDownloadByValue(args);
188 if (file)
189 file->TogglePause();
190 }
191
HandleRemove(const ListValue * args)192 void DownloadsDOMHandler::HandleRemove(const ListValue* args) {
193 DownloadItem* file = GetDownloadByValue(args);
194 if (file)
195 file->Remove();
196 }
197
HandleCancel(const ListValue * args)198 void DownloadsDOMHandler::HandleCancel(const ListValue* args) {
199 DownloadItem* file = GetDownloadByValue(args);
200 if (file)
201 file->Cancel(true);
202 }
203
HandleClearAll(const ListValue * args)204 void DownloadsDOMHandler::HandleClearAll(const ListValue* args) {
205 download_manager_->RemoveAllDownloads();
206 }
207
208 // DownloadsDOMHandler, private: ----------------------------------------------
209
SendCurrentDownloads()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
ClearDownloadItems()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
GetDownloadById(int id)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
GetDownloadByValue(const ListValue * args)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