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_ui.h"
47#include "extensions/browser/extension_system.h"
48#include "grit/generated_resources.h"
49#include "net/base/filename_util.h"
50#include "ui/base/l10n/time_format.h"
51#include "ui/gfx/image/image.h"
52
53using base::UserMetricsAction;
54using content::BrowserContext;
55using content::BrowserThread;
56
57namespace {
58
59// Maximum number of downloads to show. TODO(glen): Remove this and instead
60// stuff the downloads down the pipe slowly.
61static const size_t kMaxDownloads = 150;
62
63enum DownloadsDOMEvent {
64  DOWNLOADS_DOM_EVENT_GET_DOWNLOADS = 0,
65  DOWNLOADS_DOM_EVENT_OPEN_FILE = 1,
66  DOWNLOADS_DOM_EVENT_DRAG = 2,
67  DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS = 3,
68  DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS = 4,
69  DOWNLOADS_DOM_EVENT_SHOW = 5,
70  DOWNLOADS_DOM_EVENT_PAUSE = 6,
71  DOWNLOADS_DOM_EVENT_REMOVE = 7,
72  DOWNLOADS_DOM_EVENT_CANCEL = 8,
73  DOWNLOADS_DOM_EVENT_CLEAR_ALL = 9,
74  DOWNLOADS_DOM_EVENT_OPEN_FOLDER = 10,
75  DOWNLOADS_DOM_EVENT_RESUME = 11,
76  DOWNLOADS_DOM_EVENT_MAX
77};
78
79void CountDownloadsDOMEvents(DownloadsDOMEvent event) {
80  UMA_HISTOGRAM_ENUMERATION("Download.DOMEvent",
81                            event,
82                            DOWNLOADS_DOM_EVENT_MAX);
83}
84
85// Returns a string constant to be used as the |danger_type| value in
86// CreateDownloadItemValue().  Only return strings for DANGEROUS_FILE,
87// DANGEROUS_URL, DANGEROUS_CONTENT, and UNCOMMON_CONTENT because the
88// |danger_type| value is only defined if the value of |state| is |DANGEROUS|.
89const char* GetDangerTypeString(content::DownloadDangerType danger_type) {
90  switch (danger_type) {
91    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
92      return "DANGEROUS_FILE";
93    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
94      return "DANGEROUS_URL";
95    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
96      return "DANGEROUS_CONTENT";
97    case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
98      return "UNCOMMON_CONTENT";
99    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
100      return "DANGEROUS_HOST";
101    case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
102      return "POTENTIALLY_UNWANTED";
103    default:
104      // Don't return a danger type string if it is NOT_DANGEROUS or
105      // MAYBE_DANGEROUS_CONTENT.
106      NOTREACHED();
107      return "";
108  }
109}
110
111// Returns a JSON dictionary containing some of the attributes of |download|.
112// The JSON dictionary will also have a field "id" set to |id|, and a field
113// "otr" set to |incognito|.
114base::DictionaryValue* CreateDownloadItemValue(
115    content::DownloadItem* download_item,
116    bool incognito) {
117  // TODO(asanka): Move towards using download_model here for getting status and
118  // progress. The difference currently only matters to Drive downloads and
119  // those don't show up on the downloads page, but should.
120  DownloadItemModel download_model(download_item);
121  base::DictionaryValue* file_value = new base::DictionaryValue();
122
123  file_value->SetInteger(
124      "started", static_cast<int>(download_item->GetStartTime().ToTimeT()));
125  file_value->SetString(
126      "since_string", ui::TimeFormat::RelativeDate(
127          download_item->GetStartTime(), NULL));
128  file_value->SetString(
129      "date_string", base::TimeFormatShortDate(download_item->GetStartTime()));
130  file_value->SetInteger("id", download_item->GetId());
131
132  base::FilePath download_path(download_item->GetTargetFilePath());
133  file_value->Set("file_path", base::CreateFilePathValue(download_path));
134  file_value->SetString("file_url",
135                        net::FilePathToFileURL(download_path).spec());
136
137  extensions::DownloadedByExtension* by_ext =
138      extensions::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  base::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.
236bool 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
245DownloadsDOMHandler::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
260DownloadsDOMHandler::~DownloadsDOMHandler() {
261}
262
263// DownloadsDOMHandler, public: -----------------------------------------------
264
265void DownloadsDOMHandler::OnPageLoaded(const base::ListValue* args) {
266  SendCurrentDownloads();
267}
268
269void 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
311void DownloadsDOMHandler::OnDownloadCreated(
312    content::DownloadManager* manager, content::DownloadItem* download_item) {
313  if (IsDownloadDisplayable(*download_item))
314    ScheduleSendCurrentDownloads();
315}
316
317void 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
342void 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
358void 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
364void 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
371void 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->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
396void 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
403void 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
410void 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
417void 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
424void 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
431void 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
441void 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
448void 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
468void DownloadsDOMHandler::HandleOpenDownloadsFolder(
469    const base::ListValue* args) {
470  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FOLDER);
471  content::DownloadManager* manager = main_notifier_.GetManager();
472  if (manager) {
473    platform_util::OpenItem(
474        Profile::FromBrowserContext(manager->GetBrowserContext()),
475        DownloadPrefs::FromDownloadManager(manager)->DownloadPath());
476  }
477}
478
479// DownloadsDOMHandler, private: ----------------------------------------------
480
481void DownloadsDOMHandler::ScheduleSendCurrentDownloads() {
482  // Don't call SendCurrentDownloads() every time anything changes. Batch them
483  // together instead. This may handle hundreds of OnDownloadDestroyed() calls
484  // in a single UI message loop iteration when the user Clears All downloads.
485  if (update_scheduled_)
486    return;
487  update_scheduled_ = true;
488  BrowserThread::PostTask(
489      BrowserThread::UI, FROM_HERE,
490      base::Bind(&DownloadsDOMHandler::SendCurrentDownloads,
491                 weak_ptr_factory_.GetWeakPtr()));
492}
493
494void DownloadsDOMHandler::SendCurrentDownloads() {
495  update_scheduled_ = false;
496  content::DownloadManager::DownloadVector all_items, filtered_items;
497  if (main_notifier_.GetManager()) {
498    main_notifier_.GetManager()->GetAllDownloads(&all_items);
499    main_notifier_.GetManager()->CheckForHistoryFilesRemoval();
500  }
501  if (original_notifier_.get() && original_notifier_->GetManager()) {
502    original_notifier_->GetManager()->GetAllDownloads(&all_items);
503    original_notifier_->GetManager()->CheckForHistoryFilesRemoval();
504  }
505  DownloadQuery query;
506  if (search_terms_ && !search_terms_->empty()) {
507    query.AddFilter(DownloadQuery::FILTER_QUERY, *search_terms_.get());
508  }
509  query.AddFilter(base::Bind(&IsDownloadDisplayable));
510  query.AddSorter(DownloadQuery::SORT_START_TIME, DownloadQuery::DESCENDING);
511  query.Limit(kMaxDownloads);
512  query.Search(all_items.begin(), all_items.end(), &filtered_items);
513  base::ListValue results_value;
514  for (content::DownloadManager::DownloadVector::const_iterator
515       iter = filtered_items.begin(); iter != filtered_items.end(); ++iter) {
516    results_value.Append(CreateDownloadItemValue(
517        *iter,
518        (original_notifier_.get() &&
519          main_notifier_.GetManager() &&
520          (main_notifier_.GetManager()->GetDownload((*iter)->GetId()) ==
521          *iter))));
522  }
523  CallDownloadsList(results_value);
524}
525
526void DownloadsDOMHandler::ShowDangerPrompt(
527    content::DownloadItem* dangerous_item) {
528  DownloadDangerPrompt* danger_prompt = DownloadDangerPrompt::Create(
529      dangerous_item,
530      GetWebUIWebContents(),
531      false,
532      base::Bind(&DownloadsDOMHandler::DangerPromptDone,
533                 weak_ptr_factory_.GetWeakPtr(), dangerous_item->GetId()));
534  // danger_prompt will delete itself.
535  DCHECK(danger_prompt);
536}
537
538void DownloadsDOMHandler::DangerPromptDone(
539    int download_id, DownloadDangerPrompt::Action action) {
540  if (action != DownloadDangerPrompt::ACCEPT)
541    return;
542  content::DownloadItem* item = NULL;
543  if (main_notifier_.GetManager())
544    item = main_notifier_.GetManager()->GetDownload(download_id);
545  if (!item && original_notifier_.get() && original_notifier_->GetManager())
546    item = original_notifier_->GetManager()->GetDownload(download_id);
547  if (!item || item->IsDone())
548    return;
549  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
550  item->ValidateDangerousDownload();
551}
552
553bool DownloadsDOMHandler::IsDeletingHistoryAllowed() {
554  content::DownloadManager* manager = main_notifier_.GetManager();
555  return (manager &&
556          Profile::FromBrowserContext(manager->GetBrowserContext())->
557              GetPrefs()->GetBoolean(prefs::kAllowDeletingBrowserHistory));
558}
559
560content::DownloadItem* DownloadsDOMHandler::GetDownloadByValue(
561    const base::ListValue* args) {
562  int download_id = -1;
563  if (!ExtractIntegerValue(args, &download_id))
564    return NULL;
565  content::DownloadItem* item = NULL;
566  if (main_notifier_.GetManager())
567    item = main_notifier_.GetManager()->GetDownload(download_id);
568  if (!item && original_notifier_.get() && original_notifier_->GetManager())
569    item = original_notifier_->GetManager()->GetDownload(download_id);
570  return item;
571}
572
573content::WebContents* DownloadsDOMHandler::GetWebUIWebContents() {
574  return web_ui()->GetWebContents();
575}
576
577void DownloadsDOMHandler::CallDownloadsList(const base::ListValue& downloads) {
578  web_ui()->CallJavascriptFunction("downloadsList", downloads);
579}
580
581void DownloadsDOMHandler::CallDownloadUpdated(
582    const base::ListValue& download_item) {
583  web_ui()->CallJavascriptFunction("downloadUpdated", download_item);
584}
585