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