- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / downloads_dom_handler.cc
1 // Copyright (c) 2012 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/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/i18n/rtl.h"
14 #include "base/i18n/time_formatting.h"
15 #include "base/memory/singleton.h"
16 #include "base/metrics/field_trial.h"
17 #include "base/metrics/histogram.h"
18 #include "base/prefs/pref_service.h"
19 #include "base/strings/string_piece.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/threading/thread.h"
22 #include "base/value_conversions.h"
23 #include "base/values.h"
24 #include "chrome/browser/browser_process.h"
25 #include "chrome/browser/download/download_crx_util.h"
26 #include "chrome/browser/download/download_danger_prompt.h"
27 #include "chrome/browser/download/download_history.h"
28 #include "chrome/browser/download/download_item_model.h"
29 #include "chrome/browser/download/download_prefs.h"
30 #include "chrome/browser/download/download_query.h"
31 #include "chrome/browser/download/download_service.h"
32 #include "chrome/browser/download/download_service_factory.h"
33 #include "chrome/browser/download/drag_download_item.h"
34 #include "chrome/browser/extensions/api/downloads/downloads_api.h"
35 #include "chrome/browser/extensions/extension_service.h"
36 #include "chrome/browser/extensions/extension_system.h"
37 #include "chrome/browser/platform_util.h"
38 #include "chrome/browser/profiles/profile.h"
39 #include "chrome/browser/ui/webui/fileicon_source.h"
40 #include "chrome/common/pref_names.h"
41 #include "chrome/common/url_constants.h"
42 #include "content/public/browser/browser_thread.h"
43 #include "content/public/browser/download_item.h"
44 #include "content/public/browser/url_data_source.h"
45 #include "content/public/browser/user_metrics.h"
46 #include "content/public/browser/web_contents.h"
47 #include "content/public/browser/web_contents_view.h"
48 #include "content/public/browser/web_ui.h"
49 #include "grit/generated_resources.h"
50 #include "net/base/net_util.h"
51 #include "ui/base/l10n/time_format.h"
52 #include "ui/gfx/image/image.h"
53
54 using content::BrowserContext;
55 using content::BrowserThread;
56 using content::UserMetricsAction;
57
58 namespace {
59
60 // Maximum number of downloads to show. TODO(glen): Remove this and instead
61 // stuff the downloads down the pipe slowly.
62 static const size_t kMaxDownloads = 150;
63
64 enum DownloadsDOMEvent {
65   DOWNLOADS_DOM_EVENT_GET_DOWNLOADS = 0,
66   DOWNLOADS_DOM_EVENT_OPEN_FILE = 1,
67   DOWNLOADS_DOM_EVENT_DRAG = 2,
68   DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS = 3,
69   DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS = 4,
70   DOWNLOADS_DOM_EVENT_SHOW = 5,
71   DOWNLOADS_DOM_EVENT_PAUSE = 6,
72   DOWNLOADS_DOM_EVENT_REMOVE = 7,
73   DOWNLOADS_DOM_EVENT_CANCEL = 8,
74   DOWNLOADS_DOM_EVENT_CLEAR_ALL = 9,
75   DOWNLOADS_DOM_EVENT_OPEN_FOLDER = 10,
76   DOWNLOADS_DOM_EVENT_RESUME = 11,
77   DOWNLOADS_DOM_EVENT_MAX
78 };
79
80 void CountDownloadsDOMEvents(DownloadsDOMEvent event) {
81   UMA_HISTOGRAM_ENUMERATION("Download.DOMEvent",
82                             event,
83                             DOWNLOADS_DOM_EVENT_MAX);
84 }
85
86 // Returns a string constant to be used as the |danger_type| value in
87 // CreateDownloadItemValue().  Only return strings for DANGEROUS_FILE,
88 // DANGEROUS_URL, DANGEROUS_CONTENT, and UNCOMMON_CONTENT because the
89 // |danger_type| value is only defined if the value of |state| is |DANGEROUS|.
90 const char* GetDangerTypeString(content::DownloadDangerType danger_type) {
91   switch (danger_type) {
92     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
93       return "DANGEROUS_FILE";
94     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
95       return "DANGEROUS_URL";
96     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
97       return "DANGEROUS_CONTENT";
98     case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
99       return "UNCOMMON_CONTENT";
100     case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
101       return "DANGEROUS_HOST";
102     case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
103       return "POTENTIALLY_UNWANTED";
104     default:
105       // Don't return a danger type string if it is NOT_DANGEROUS or
106       // MAYBE_DANGEROUS_CONTENT.
107       NOTREACHED();
108       return "";
109   }
110 }
111
112 // Returns a JSON dictionary containing some of the attributes of |download|.
113 // The JSON dictionary will also have a field "id" set to |id|, and a field
114 // "otr" set to |incognito|.
115 DictionaryValue* CreateDownloadItemValue(
116     content::DownloadItem* download_item,
117     bool incognito) {
118   // TODO(asanka): Move towards using download_model here for getting status and
119   // progress. The difference currently only matters to Drive downloads and
120   // those don't show up on the downloads page, but should.
121   DownloadItemModel download_model(download_item);
122   DictionaryValue* file_value = new DictionaryValue();
123
124   file_value->SetInteger(
125       "started", static_cast<int>(download_item->GetStartTime().ToTimeT()));
126   file_value->SetString(
127       "since_string", ui::TimeFormat::RelativeDate(
128           download_item->GetStartTime(), NULL));
129   file_value->SetString(
130       "date_string", base::TimeFormatShortDate(download_item->GetStartTime()));
131   file_value->SetInteger("id", download_item->GetId());
132
133   base::FilePath download_path(download_item->GetTargetFilePath());
134   file_value->Set("file_path", base::CreateFilePathValue(download_path));
135   file_value->SetString("file_url",
136                         net::FilePathToFileURL(download_path).spec());
137
138   DownloadedByExtension* by_ext = DownloadedByExtension::Get(download_item);
139   if (by_ext) {
140     file_value->SetString("by_ext_id", by_ext->id());
141     file_value->SetString("by_ext_name", by_ext->name());
142     // Lookup the extension's current name() in case the user changed their
143     // language. This won't work if the extension was uninstalled, so the name
144     // might be the wrong language.
145     bool include_disabled = true;
146     const extensions::Extension* extension = extensions::ExtensionSystem::Get(
147         Profile::FromBrowserContext(download_item->GetBrowserContext()))->
148       extension_service()->GetExtensionById(by_ext->id(), include_disabled);
149     if (extension)
150       file_value->SetString("by_ext_name", extension->name());
151   }
152
153   // Keep file names as LTR.
154   string16 file_name =
155     download_item->GetFileNameToReportUser().LossyDisplayName();
156   file_name = base::i18n::GetDisplayStringInLTRDirectionality(file_name);
157   file_value->SetString("file_name", file_name);
158   file_value->SetString("url", download_item->GetURL().spec());
159   file_value->SetBoolean("otr", incognito);
160   file_value->SetInteger("total", static_cast<int>(
161       download_item->GetTotalBytes()));
162   file_value->SetBoolean("file_externally_removed",
163                          download_item->GetFileExternallyRemoved());
164   file_value->SetBoolean("retry", false); // Overridden below if needed.
165   file_value->SetBoolean("resume", download_item->CanResume());
166
167   switch (download_item->GetState()) {
168     case content::DownloadItem::IN_PROGRESS:
169       if (download_item->IsDangerous()) {
170         file_value->SetString("state", "DANGEROUS");
171         // These are the only danger states that the UI is equipped to handle.
172         DCHECK(download_item->GetDangerType() ==
173                    content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
174                download_item->GetDangerType() ==
175                    content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
176                download_item->GetDangerType() ==
177                    content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT ||
178                download_item->GetDangerType() ==
179                    content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT ||
180                download_item->GetDangerType() ==
181                    content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST ||
182                download_item->GetDangerType() ==
183                    content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED);
184         const char* danger_type_value =
185             GetDangerTypeString(download_item->GetDangerType());
186         file_value->SetString("danger_type", danger_type_value);
187       } else if (download_item->IsPaused()) {
188         file_value->SetString("state", "PAUSED");
189       } else {
190         file_value->SetString("state", "IN_PROGRESS");
191       }
192       file_value->SetString("progress_status_text",
193                             download_model.GetTabProgressStatusText());
194
195       file_value->SetInteger("percent",
196           static_cast<int>(download_item->PercentComplete()));
197       file_value->SetInteger("received",
198           static_cast<int>(download_item->GetReceivedBytes()));
199       break;
200
201     case content::DownloadItem::INTERRUPTED:
202       file_value->SetString("state", "INTERRUPTED");
203
204       file_value->SetString("progress_status_text",
205                             download_model.GetTabProgressStatusText());
206
207       file_value->SetInteger("percent",
208           static_cast<int>(download_item->PercentComplete()));
209       file_value->SetInteger("received",
210           static_cast<int>(download_item->GetReceivedBytes()));
211       file_value->SetString("last_reason_text",
212                             download_model.GetInterruptReasonText());
213       if (content::DOWNLOAD_INTERRUPT_REASON_CRASH ==
214           download_item->GetLastReason() && !download_item->CanResume())
215         file_value->SetBoolean("retry", true);
216       break;
217
218     case content::DownloadItem::CANCELLED:
219       file_value->SetString("state", "CANCELLED");
220       file_value->SetBoolean("retry", true);
221       break;
222
223     case content::DownloadItem::COMPLETE:
224       DCHECK(!download_item->IsDangerous());
225       file_value->SetString("state", "COMPLETE");
226       break;
227
228     case content::DownloadItem::MAX_DOWNLOAD_STATE:
229       NOTREACHED() << "state undefined";
230   }
231
232   return file_value;
233 }
234
235 // Filters out extension downloads and downloads that don't have a filename yet.
236 bool IsDownloadDisplayable(const content::DownloadItem& item) {
237   return (!download_crx_util::IsExtensionDownload(item) &&
238           !item.IsTemporary() &&
239           !item.GetFileNameToReportUser().empty() &&
240           !item.GetTargetFilePath().empty());
241 }
242
243 }  // namespace
244
245 DownloadsDOMHandler::DownloadsDOMHandler(content::DownloadManager* dlm)
246     : main_notifier_(dlm, this),
247       update_scheduled_(false),
248       weak_ptr_factory_(this) {
249   // Create our fileicon data source.
250   Profile* profile = Profile::FromBrowserContext(dlm->GetBrowserContext());
251   content::URLDataSource::Add(profile, new FileIconSource());
252
253   if (profile->IsOffTheRecord()) {
254     original_notifier_.reset(new AllDownloadItemNotifier(
255         BrowserContext::GetDownloadManager(profile->GetOriginalProfile()),
256         this));
257   }
258 }
259
260 DownloadsDOMHandler::~DownloadsDOMHandler() {
261 }
262
263 // DownloadsDOMHandler, public: -----------------------------------------------
264
265 void DownloadsDOMHandler::OnPageLoaded(const base::ListValue* args) {
266   SendCurrentDownloads();
267 }
268
269 void DownloadsDOMHandler::RegisterMessages() {
270   web_ui()->RegisterMessageCallback("onPageLoaded",
271       base::Bind(&DownloadsDOMHandler::OnPageLoaded,
272                  weak_ptr_factory_.GetWeakPtr()));
273   web_ui()->RegisterMessageCallback("getDownloads",
274       base::Bind(&DownloadsDOMHandler::HandleGetDownloads,
275                  weak_ptr_factory_.GetWeakPtr()));
276   web_ui()->RegisterMessageCallback("openFile",
277       base::Bind(&DownloadsDOMHandler::HandleOpenFile,
278                  weak_ptr_factory_.GetWeakPtr()));
279   web_ui()->RegisterMessageCallback("drag",
280       base::Bind(&DownloadsDOMHandler::HandleDrag,
281                  weak_ptr_factory_.GetWeakPtr()));
282   web_ui()->RegisterMessageCallback("saveDangerous",
283       base::Bind(&DownloadsDOMHandler::HandleSaveDangerous,
284                  weak_ptr_factory_.GetWeakPtr()));
285   web_ui()->RegisterMessageCallback("discardDangerous",
286       base::Bind(&DownloadsDOMHandler::HandleDiscardDangerous,
287                  weak_ptr_factory_.GetWeakPtr()));
288   web_ui()->RegisterMessageCallback("show",
289       base::Bind(&DownloadsDOMHandler::HandleShow,
290                  weak_ptr_factory_.GetWeakPtr()));
291   web_ui()->RegisterMessageCallback("pause",
292       base::Bind(&DownloadsDOMHandler::HandlePause,
293                  weak_ptr_factory_.GetWeakPtr()));
294   web_ui()->RegisterMessageCallback("resume",
295       base::Bind(&DownloadsDOMHandler::HandleResume,
296                  weak_ptr_factory_.GetWeakPtr()));
297   web_ui()->RegisterMessageCallback("remove",
298       base::Bind(&DownloadsDOMHandler::HandleRemove,
299                  weak_ptr_factory_.GetWeakPtr()));
300   web_ui()->RegisterMessageCallback("cancel",
301       base::Bind(&DownloadsDOMHandler::HandleCancel,
302                  weak_ptr_factory_.GetWeakPtr()));
303   web_ui()->RegisterMessageCallback("clearAll",
304       base::Bind(&DownloadsDOMHandler::HandleClearAll,
305                  weak_ptr_factory_.GetWeakPtr()));
306   web_ui()->RegisterMessageCallback("openDownloadsFolder",
307       base::Bind(&DownloadsDOMHandler::HandleOpenDownloadsFolder,
308                  weak_ptr_factory_.GetWeakPtr()));
309 }
310
311 void DownloadsDOMHandler::OnDownloadCreated(
312     content::DownloadManager* manager, content::DownloadItem* download_item) {
313   if (IsDownloadDisplayable(*download_item))
314     ScheduleSendCurrentDownloads();
315 }
316
317 void DownloadsDOMHandler::OnDownloadUpdated(
318     content::DownloadManager* manager,
319     content::DownloadItem* download_item) {
320   if (IsDownloadDisplayable(*download_item)) {
321     if (search_terms_ && !search_terms_->empty()) {
322       // Don't CallDownloadUpdated() if download_item doesn't match
323       // search_terms_.
324       // TODO(benjhayden): Consider splitting MatchesQuery() out to a function.
325       content::DownloadManager::DownloadVector all_items, filtered_items;
326       all_items.push_back(download_item);
327       DownloadQuery query;
328       query.AddFilter(DownloadQuery::FILTER_QUERY, *search_terms_.get());
329       query.Search(all_items.begin(), all_items.end(), &filtered_items);
330       if (filtered_items.empty())
331         return;
332     }
333     base::ListValue results_value;
334     results_value.Append(CreateDownloadItemValue(
335         download_item,
336         (original_notifier_.get() &&
337           (manager == main_notifier_.GetManager()))));
338     CallDownloadUpdated(results_value);
339   }
340 }
341
342 void DownloadsDOMHandler::OnDownloadRemoved(
343     content::DownloadManager* manager,
344     content::DownloadItem* download_item) {
345   // This relies on |download_item| being removed from DownloadManager in this
346   // MessageLoop iteration. |download_item| may not have been removed from
347   // DownloadManager when OnDownloadRemoved() is fired, so bounce off the
348   // MessageLoop to give it a chance to be removed. SendCurrentDownloads() looks
349   // at all downloads, and we do not tell it that |download_item| is being
350   // removed. If DownloadManager is ever changed to not immediately remove
351   // |download_item| from its map when OnDownloadRemoved is sent, then
352   // DownloadsDOMHandler::OnDownloadRemoved() will need to explicitly tell
353   // SendCurrentDownloads() that |download_item| was removed. A
354   // SupportsUserData::Data would be the correct way to do this.
355   ScheduleSendCurrentDownloads();
356 }
357
358 void DownloadsDOMHandler::HandleGetDownloads(const base::ListValue* args) {
359   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_GET_DOWNLOADS);
360   search_terms_.reset((args && !args->empty()) ? args->DeepCopy() : NULL);
361   SendCurrentDownloads();
362 }
363
364 void DownloadsDOMHandler::HandleOpenFile(const base::ListValue* args) {
365   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FILE);
366   content::DownloadItem* file = GetDownloadByValue(args);
367   if (file)
368     file->OpenDownload();
369 }
370
371 void DownloadsDOMHandler::HandleDrag(const base::ListValue* args) {
372   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DRAG);
373   content::DownloadItem* file = GetDownloadByValue(args);
374   if (!file)
375     return;
376
377   content::WebContents* web_contents = GetWebUIWebContents();
378   // |web_contents| is only NULL in the test.
379   if (!web_contents)
380     return;
381
382   if (file->GetState() != content::DownloadItem::COMPLETE)
383     return;
384
385   gfx::Image* icon = g_browser_process->icon_manager()->LookupIconFromFilepath(
386       file->GetTargetFilePath(), IconLoader::NORMAL);
387   gfx::NativeView view = web_contents->GetView()->GetNativeView();
388   {
389     // Enable nested tasks during DnD, while |DragDownload()| blocks.
390     base::MessageLoop::ScopedNestableTaskAllower allow(
391         base::MessageLoop::current());
392     DragDownloadItem(file, icon, view);
393   }
394 }
395
396 void DownloadsDOMHandler::HandleSaveDangerous(const base::ListValue* args) {
397   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
398   content::DownloadItem* file = GetDownloadByValue(args);
399   if (file)
400     ShowDangerPrompt(file);
401 }
402
403 void DownloadsDOMHandler::HandleDiscardDangerous(const base::ListValue* args) {
404   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS);
405   content::DownloadItem* file = GetDownloadByValue(args);
406   if (file)
407     file->Remove();
408 }
409
410 void DownloadsDOMHandler::HandleShow(const base::ListValue* args) {
411   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SHOW);
412   content::DownloadItem* file = GetDownloadByValue(args);
413   if (file)
414     file->ShowDownloadInShell();
415 }
416
417 void DownloadsDOMHandler::HandlePause(const base::ListValue* args) {
418   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_PAUSE);
419   content::DownloadItem* file = GetDownloadByValue(args);
420   if (file)
421     file->Pause();
422 }
423
424 void DownloadsDOMHandler::HandleResume(const base::ListValue* args) {
425   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_RESUME);
426   content::DownloadItem* file = GetDownloadByValue(args);
427   if (file)
428     file->Resume();
429 }
430
431 void DownloadsDOMHandler::HandleRemove(const base::ListValue* args) {
432   if (!IsDeletingHistoryAllowed())
433     return;
434
435   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_REMOVE);
436   content::DownloadItem* file = GetDownloadByValue(args);
437   if (file)
438     file->Remove();
439 }
440
441 void DownloadsDOMHandler::HandleCancel(const base::ListValue* args) {
442   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CANCEL);
443   content::DownloadItem* file = GetDownloadByValue(args);
444   if (file)
445     file->Cancel(true);
446 }
447
448 void DownloadsDOMHandler::HandleClearAll(const base::ListValue* args) {
449   if (IsDeletingHistoryAllowed()) {
450     CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CLEAR_ALL);
451     // IsDeletingHistoryAllowed already checked for the existence of the
452     // manager.
453     main_notifier_.GetManager()->RemoveAllDownloads();
454
455     // If this is an incognito downloads page, clear All should clear main
456     // download manager as well.
457     if (original_notifier_.get() && original_notifier_->GetManager())
458       original_notifier_->GetManager()->RemoveAllDownloads();
459   }
460
461   // downloads.js always clears the display and relies on HandleClearAll to
462   // ScheduleSendCurrentDownloads(). If any downloads are removed, then
463   // OnDownloadRemoved() will call it, but if no downloads are actually removed,
464   // then HandleClearAll needs to call it manually.
465   ScheduleSendCurrentDownloads();
466 }
467
468 void DownloadsDOMHandler::HandleOpenDownloadsFolder(
469     const base::ListValue* args) {
470   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FOLDER);
471   if (main_notifier_.GetManager()) {
472     platform_util::OpenItem(DownloadPrefs::FromDownloadManager(
473         main_notifier_.GetManager())->DownloadPath());
474   }
475 }
476
477 // DownloadsDOMHandler, private: ----------------------------------------------
478
479 void DownloadsDOMHandler::ScheduleSendCurrentDownloads() {
480   // Don't call SendCurrentDownloads() every time anything changes. Batch them
481   // together instead. This may handle hundreds of OnDownloadDestroyed() calls
482   // in a single UI message loop iteration when the user Clears All downloads.
483   if (update_scheduled_)
484     return;
485   update_scheduled_ = true;
486   BrowserThread::PostTask(
487       BrowserThread::UI, FROM_HERE,
488       base::Bind(&DownloadsDOMHandler::SendCurrentDownloads,
489                  weak_ptr_factory_.GetWeakPtr()));
490 }
491
492 void DownloadsDOMHandler::SendCurrentDownloads() {
493   update_scheduled_ = false;
494   content::DownloadManager::DownloadVector all_items, filtered_items;
495   if (main_notifier_.GetManager()) {
496     main_notifier_.GetManager()->GetAllDownloads(&all_items);
497     main_notifier_.GetManager()->CheckForHistoryFilesRemoval();
498   }
499   if (original_notifier_.get() && original_notifier_->GetManager()) {
500     original_notifier_->GetManager()->GetAllDownloads(&all_items);
501     original_notifier_->GetManager()->CheckForHistoryFilesRemoval();
502   }
503   DownloadQuery query;
504   if (search_terms_ && !search_terms_->empty()) {
505     query.AddFilter(DownloadQuery::FILTER_QUERY, *search_terms_.get());
506   }
507   query.AddFilter(base::Bind(&IsDownloadDisplayable));
508   query.AddSorter(DownloadQuery::SORT_START_TIME, DownloadQuery::DESCENDING);
509   query.Limit(kMaxDownloads);
510   query.Search(all_items.begin(), all_items.end(), &filtered_items);
511   base::ListValue results_value;
512   for (content::DownloadManager::DownloadVector::const_iterator
513        iter = filtered_items.begin(); iter != filtered_items.end(); ++iter) {
514     results_value.Append(CreateDownloadItemValue(
515         *iter,
516         (original_notifier_.get() &&
517           main_notifier_.GetManager() &&
518           (main_notifier_.GetManager()->GetDownload((*iter)->GetId()) ==
519           *iter))));
520   }
521   CallDownloadsList(results_value);
522 }
523
524 void DownloadsDOMHandler::ShowDangerPrompt(
525     content::DownloadItem* dangerous_item) {
526   DownloadDangerPrompt* danger_prompt = DownloadDangerPrompt::Create(
527       dangerous_item,
528       GetWebUIWebContents(),
529       false,
530       base::Bind(&DownloadsDOMHandler::DangerPromptDone,
531                  weak_ptr_factory_.GetWeakPtr(), dangerous_item->GetId()));
532   // danger_prompt will delete itself.
533   DCHECK(danger_prompt);
534 }
535
536 void DownloadsDOMHandler::DangerPromptDone(
537     int download_id, DownloadDangerPrompt::Action action) {
538   if (action != DownloadDangerPrompt::ACCEPT)
539     return;
540   content::DownloadItem* item = NULL;
541   if (main_notifier_.GetManager())
542     item = main_notifier_.GetManager()->GetDownload(download_id);
543   if (!item && original_notifier_.get() && original_notifier_->GetManager())
544     item = original_notifier_->GetManager()->GetDownload(download_id);
545   if (!item || item->IsDone())
546     return;
547   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
548   item->ValidateDangerousDownload();
549 }
550
551 bool DownloadsDOMHandler::IsDeletingHistoryAllowed() {
552   content::DownloadManager* manager = main_notifier_.GetManager();
553   return (manager &&
554           Profile::FromBrowserContext(manager->GetBrowserContext())->
555               GetPrefs()->GetBoolean(prefs::kAllowDeletingBrowserHistory));
556 }
557
558 content::DownloadItem* DownloadsDOMHandler::GetDownloadByValue(
559     const base::ListValue* args) {
560   int download_id = -1;
561   if (!ExtractIntegerValue(args, &download_id))
562     return NULL;
563   content::DownloadItem* item = NULL;
564   if (main_notifier_.GetManager())
565     item = main_notifier_.GetManager()->GetDownload(download_id);
566   if (!item && original_notifier_.get() && original_notifier_->GetManager())
567     item = original_notifier_->GetManager()->GetDownload(download_id);
568   return item;
569 }
570
571 content::WebContents* DownloadsDOMHandler::GetWebUIWebContents() {
572   return web_ui()->GetWebContents();
573 }
574
575 void DownloadsDOMHandler::CallDownloadsList(const base::ListValue& downloads) {
576   web_ui()->CallJavascriptFunction("downloadsList", downloads);
577 }
578
579 void DownloadsDOMHandler::CallDownloadUpdated(
580     const base::ListValue& download_item) {
581   web_ui()->CallJavascriptFunction("downloadUpdated", download_item);
582 }