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