root/chrome/browser/ui/webui/downloads_dom_handler.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. CountDownloadsDOMEvents
  2. GetDangerTypeString
  3. CreateDownloadItemValue
  4. IsDownloadDisplayable
  5. weak_ptr_factory_
  6. OnPageLoaded
  7. RegisterMessages
  8. OnDownloadCreated
  9. OnDownloadUpdated
  10. OnDownloadRemoved
  11. HandleGetDownloads
  12. HandleOpenFile
  13. HandleDrag
  14. HandleSaveDangerous
  15. HandleDiscardDangerous
  16. HandleShow
  17. HandlePause
  18. HandleResume
  19. HandleRemove
  20. HandleCancel
  21. HandleClearAll
  22. HandleOpenDownloadsFolder
  23. ScheduleSendCurrentDownloads
  24. SendCurrentDownloads
  25. ShowDangerPrompt
  26. DangerPromptDone
  27. IsDeletingHistoryAllowed
  28. GetDownloadByValue
  29. GetWebUIWebContents
  30. CallDownloadsList
  31. CallDownloadUpdated

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/downloads_dom_handler.h"

#include <algorithm>
#include <functional>

#include "base/basictypes.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/i18n/rtl.h"
#include "base/i18n/time_formatting.h"
#include "base/memory/singleton.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "base/value_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_crx_util.h"
#include "chrome/browser/download/download_danger_prompt.h"
#include "chrome/browser/download/download_history.h"
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/download_query.h"
#include "chrome/browser/download/download_service.h"
#include "chrome/browser/download/download_service_factory.h"
#include "chrome/browser/download/drag_download_item.h"
#include "chrome/browser/extensions/api/downloads/downloads_api.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/fileicon_source.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_item.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "content/public/browser/web_ui.h"
#include "extensions/browser/extension_system.h"
#include "grit/generated_resources.h"
#include "net/base/net_util.h"
#include "ui/base/l10n/time_format.h"
#include "ui/gfx/image/image.h"

using base::UserMetricsAction;
using content::BrowserContext;
using content::BrowserThread;

namespace {

// Maximum number of downloads to show. TODO(glen): Remove this and instead
// stuff the downloads down the pipe slowly.
static const size_t kMaxDownloads = 150;

enum DownloadsDOMEvent {
  DOWNLOADS_DOM_EVENT_GET_DOWNLOADS = 0,
  DOWNLOADS_DOM_EVENT_OPEN_FILE = 1,
  DOWNLOADS_DOM_EVENT_DRAG = 2,
  DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS = 3,
  DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS = 4,
  DOWNLOADS_DOM_EVENT_SHOW = 5,
  DOWNLOADS_DOM_EVENT_PAUSE = 6,
  DOWNLOADS_DOM_EVENT_REMOVE = 7,
  DOWNLOADS_DOM_EVENT_CANCEL = 8,
  DOWNLOADS_DOM_EVENT_CLEAR_ALL = 9,
  DOWNLOADS_DOM_EVENT_OPEN_FOLDER = 10,
  DOWNLOADS_DOM_EVENT_RESUME = 11,
  DOWNLOADS_DOM_EVENT_MAX
};

void CountDownloadsDOMEvents(DownloadsDOMEvent event) {
  UMA_HISTOGRAM_ENUMERATION("Download.DOMEvent",
                            event,
                            DOWNLOADS_DOM_EVENT_MAX);
}

// Returns a string constant to be used as the |danger_type| value in
// CreateDownloadItemValue().  Only return strings for DANGEROUS_FILE,
// DANGEROUS_URL, DANGEROUS_CONTENT, and UNCOMMON_CONTENT because the
// |danger_type| value is only defined if the value of |state| is |DANGEROUS|.
const char* GetDangerTypeString(content::DownloadDangerType danger_type) {
  switch (danger_type) {
    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
      return "DANGEROUS_FILE";
    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
      return "DANGEROUS_URL";
    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
      return "DANGEROUS_CONTENT";
    case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
      return "UNCOMMON_CONTENT";
    case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
      return "DANGEROUS_HOST";
    case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
      return "POTENTIALLY_UNWANTED";
    default:
      // Don't return a danger type string if it is NOT_DANGEROUS or
      // MAYBE_DANGEROUS_CONTENT.
      NOTREACHED();
      return "";
  }
}

// Returns a JSON dictionary containing some of the attributes of |download|.
// The JSON dictionary will also have a field "id" set to |id|, and a field
// "otr" set to |incognito|.
base::DictionaryValue* CreateDownloadItemValue(
    content::DownloadItem* download_item,
    bool incognito) {
  // TODO(asanka): Move towards using download_model here for getting status and
  // progress. The difference currently only matters to Drive downloads and
  // those don't show up on the downloads page, but should.
  DownloadItemModel download_model(download_item);
  base::DictionaryValue* file_value = new base::DictionaryValue();

  file_value->SetInteger(
      "started", static_cast<int>(download_item->GetStartTime().ToTimeT()));
  file_value->SetString(
      "since_string", ui::TimeFormat::RelativeDate(
          download_item->GetStartTime(), NULL));
  file_value->SetString(
      "date_string", base::TimeFormatShortDate(download_item->GetStartTime()));
  file_value->SetInteger("id", download_item->GetId());

  base::FilePath download_path(download_item->GetTargetFilePath());
  file_value->Set("file_path", base::CreateFilePathValue(download_path));
  file_value->SetString("file_url",
                        net::FilePathToFileURL(download_path).spec());

  DownloadedByExtension* by_ext = DownloadedByExtension::Get(download_item);
  if (by_ext) {
    file_value->SetString("by_ext_id", by_ext->id());
    file_value->SetString("by_ext_name", by_ext->name());
    // Lookup the extension's current name() in case the user changed their
    // language. This won't work if the extension was uninstalled, so the name
    // might be the wrong language.
    bool include_disabled = true;
    const extensions::Extension* extension = extensions::ExtensionSystem::Get(
        Profile::FromBrowserContext(download_item->GetBrowserContext()))->
      extension_service()->GetExtensionById(by_ext->id(), include_disabled);
    if (extension)
      file_value->SetString("by_ext_name", extension->name());
  }

  // Keep file names as LTR.
  base::string16 file_name =
    download_item->GetFileNameToReportUser().LossyDisplayName();
  file_name = base::i18n::GetDisplayStringInLTRDirectionality(file_name);
  file_value->SetString("file_name", file_name);
  file_value->SetString("url", download_item->GetURL().spec());
  file_value->SetBoolean("otr", incognito);
  file_value->SetInteger("total", static_cast<int>(
      download_item->GetTotalBytes()));
  file_value->SetBoolean("file_externally_removed",
                         download_item->GetFileExternallyRemoved());
  file_value->SetBoolean("retry", false); // Overridden below if needed.
  file_value->SetBoolean("resume", download_item->CanResume());

  switch (download_item->GetState()) {
    case content::DownloadItem::IN_PROGRESS:
      if (download_item->IsDangerous()) {
        file_value->SetString("state", "DANGEROUS");
        // These are the only danger states that the UI is equipped to handle.
        DCHECK(download_item->GetDangerType() ==
                   content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
               download_item->GetDangerType() ==
                   content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
               download_item->GetDangerType() ==
                   content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT ||
               download_item->GetDangerType() ==
                   content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT ||
               download_item->GetDangerType() ==
                   content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST ||
               download_item->GetDangerType() ==
                   content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED);
        const char* danger_type_value =
            GetDangerTypeString(download_item->GetDangerType());
        file_value->SetString("danger_type", danger_type_value);
      } else if (download_item->IsPaused()) {
        file_value->SetString("state", "PAUSED");
      } else {
        file_value->SetString("state", "IN_PROGRESS");
      }
      file_value->SetString("progress_status_text",
                            download_model.GetTabProgressStatusText());

      file_value->SetInteger("percent",
          static_cast<int>(download_item->PercentComplete()));
      file_value->SetInteger("received",
          static_cast<int>(download_item->GetReceivedBytes()));
      break;

    case content::DownloadItem::INTERRUPTED:
      file_value->SetString("state", "INTERRUPTED");

      file_value->SetString("progress_status_text",
                            download_model.GetTabProgressStatusText());

      file_value->SetInteger("percent",
          static_cast<int>(download_item->PercentComplete()));
      file_value->SetInteger("received",
          static_cast<int>(download_item->GetReceivedBytes()));
      file_value->SetString("last_reason_text",
                            download_model.GetInterruptReasonText());
      if (content::DOWNLOAD_INTERRUPT_REASON_CRASH ==
          download_item->GetLastReason() && !download_item->CanResume())
        file_value->SetBoolean("retry", true);
      break;

    case content::DownloadItem::CANCELLED:
      file_value->SetString("state", "CANCELLED");
      file_value->SetBoolean("retry", true);
      break;

    case content::DownloadItem::COMPLETE:
      DCHECK(!download_item->IsDangerous());
      file_value->SetString("state", "COMPLETE");
      break;

    case content::DownloadItem::MAX_DOWNLOAD_STATE:
      NOTREACHED() << "state undefined";
  }

  return file_value;
}

// Filters out extension downloads and downloads that don't have a filename yet.
bool IsDownloadDisplayable(const content::DownloadItem& item) {
  return (!download_crx_util::IsExtensionDownload(item) &&
          !item.IsTemporary() &&
          !item.GetFileNameToReportUser().empty() &&
          !item.GetTargetFilePath().empty());
}

}  // namespace

DownloadsDOMHandler::DownloadsDOMHandler(content::DownloadManager* dlm)
    : main_notifier_(dlm, this),
      update_scheduled_(false),
      weak_ptr_factory_(this) {
  // Create our fileicon data source.
  Profile* profile = Profile::FromBrowserContext(dlm->GetBrowserContext());
  content::URLDataSource::Add(profile, new FileIconSource());

  if (profile->IsOffTheRecord()) {
    original_notifier_.reset(new AllDownloadItemNotifier(
        BrowserContext::GetDownloadManager(profile->GetOriginalProfile()),
        this));
  }
}

DownloadsDOMHandler::~DownloadsDOMHandler() {
}

// DownloadsDOMHandler, public: -----------------------------------------------

void DownloadsDOMHandler::OnPageLoaded(const base::ListValue* args) {
  SendCurrentDownloads();
}

void DownloadsDOMHandler::RegisterMessages() {
  web_ui()->RegisterMessageCallback("onPageLoaded",
      base::Bind(&DownloadsDOMHandler::OnPageLoaded,
                 weak_ptr_factory_.GetWeakPtr()));
  web_ui()->RegisterMessageCallback("getDownloads",
      base::Bind(&DownloadsDOMHandler::HandleGetDownloads,
                 weak_ptr_factory_.GetWeakPtr()));
  web_ui()->RegisterMessageCallback("openFile",
      base::Bind(&DownloadsDOMHandler::HandleOpenFile,
                 weak_ptr_factory_.GetWeakPtr()));
  web_ui()->RegisterMessageCallback("drag",
      base::Bind(&DownloadsDOMHandler::HandleDrag,
                 weak_ptr_factory_.GetWeakPtr()));
  web_ui()->RegisterMessageCallback("saveDangerous",
      base::Bind(&DownloadsDOMHandler::HandleSaveDangerous,
                 weak_ptr_factory_.GetWeakPtr()));
  web_ui()->RegisterMessageCallback("discardDangerous",
      base::Bind(&DownloadsDOMHandler::HandleDiscardDangerous,
                 weak_ptr_factory_.GetWeakPtr()));
  web_ui()->RegisterMessageCallback("show",
      base::Bind(&DownloadsDOMHandler::HandleShow,
                 weak_ptr_factory_.GetWeakPtr()));
  web_ui()->RegisterMessageCallback("pause",
      base::Bind(&DownloadsDOMHandler::HandlePause,
                 weak_ptr_factory_.GetWeakPtr()));
  web_ui()->RegisterMessageCallback("resume",
      base::Bind(&DownloadsDOMHandler::HandleResume,
                 weak_ptr_factory_.GetWeakPtr()));
  web_ui()->RegisterMessageCallback("remove",
      base::Bind(&DownloadsDOMHandler::HandleRemove,
                 weak_ptr_factory_.GetWeakPtr()));
  web_ui()->RegisterMessageCallback("cancel",
      base::Bind(&DownloadsDOMHandler::HandleCancel,
                 weak_ptr_factory_.GetWeakPtr()));
  web_ui()->RegisterMessageCallback("clearAll",
      base::Bind(&DownloadsDOMHandler::HandleClearAll,
                 weak_ptr_factory_.GetWeakPtr()));
  web_ui()->RegisterMessageCallback("openDownloadsFolder",
      base::Bind(&DownloadsDOMHandler::HandleOpenDownloadsFolder,
                 weak_ptr_factory_.GetWeakPtr()));
}

void DownloadsDOMHandler::OnDownloadCreated(
    content::DownloadManager* manager, content::DownloadItem* download_item) {
  if (IsDownloadDisplayable(*download_item))
    ScheduleSendCurrentDownloads();
}

void DownloadsDOMHandler::OnDownloadUpdated(
    content::DownloadManager* manager,
    content::DownloadItem* download_item) {
  if (IsDownloadDisplayable(*download_item)) {
    if (search_terms_ && !search_terms_->empty()) {
      // Don't CallDownloadUpdated() if download_item doesn't match
      // search_terms_.
      // TODO(benjhayden): Consider splitting MatchesQuery() out to a function.
      content::DownloadManager::DownloadVector all_items, filtered_items;
      all_items.push_back(download_item);
      DownloadQuery query;
      query.AddFilter(DownloadQuery::FILTER_QUERY, *search_terms_.get());
      query.Search(all_items.begin(), all_items.end(), &filtered_items);
      if (filtered_items.empty())
        return;
    }
    base::ListValue results_value;
    results_value.Append(CreateDownloadItemValue(
        download_item,
        (original_notifier_.get() &&
          (manager == main_notifier_.GetManager()))));
    CallDownloadUpdated(results_value);
  }
}

void DownloadsDOMHandler::OnDownloadRemoved(
    content::DownloadManager* manager,
    content::DownloadItem* download_item) {
  // This relies on |download_item| being removed from DownloadManager in this
  // MessageLoop iteration. |download_item| may not have been removed from
  // DownloadManager when OnDownloadRemoved() is fired, so bounce off the
  // MessageLoop to give it a chance to be removed. SendCurrentDownloads() looks
  // at all downloads, and we do not tell it that |download_item| is being
  // removed. If DownloadManager is ever changed to not immediately remove
  // |download_item| from its map when OnDownloadRemoved is sent, then
  // DownloadsDOMHandler::OnDownloadRemoved() will need to explicitly tell
  // SendCurrentDownloads() that |download_item| was removed. A
  // SupportsUserData::Data would be the correct way to do this.
  ScheduleSendCurrentDownloads();
}

void DownloadsDOMHandler::HandleGetDownloads(const base::ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_GET_DOWNLOADS);
  search_terms_.reset((args && !args->empty()) ? args->DeepCopy() : NULL);
  SendCurrentDownloads();
}

void DownloadsDOMHandler::HandleOpenFile(const base::ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FILE);
  content::DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->OpenDownload();
}

void DownloadsDOMHandler::HandleDrag(const base::ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DRAG);
  content::DownloadItem* file = GetDownloadByValue(args);
  if (!file)
    return;

  content::WebContents* web_contents = GetWebUIWebContents();
  // |web_contents| is only NULL in the test.
  if (!web_contents)
    return;

  if (file->GetState() != content::DownloadItem::COMPLETE)
    return;

  gfx::Image* icon = g_browser_process->icon_manager()->LookupIconFromFilepath(
      file->GetTargetFilePath(), IconLoader::NORMAL);
  gfx::NativeView view = web_contents->GetView()->GetNativeView();
  {
    // Enable nested tasks during DnD, while |DragDownload()| blocks.
    base::MessageLoop::ScopedNestableTaskAllower allow(
        base::MessageLoop::current());
    DragDownloadItem(file, icon, view);
  }
}

void DownloadsDOMHandler::HandleSaveDangerous(const base::ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
  content::DownloadItem* file = GetDownloadByValue(args);
  if (file)
    ShowDangerPrompt(file);
}

void DownloadsDOMHandler::HandleDiscardDangerous(const base::ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS);
  content::DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->Remove();
}

void DownloadsDOMHandler::HandleShow(const base::ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SHOW);
  content::DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->ShowDownloadInShell();
}

void DownloadsDOMHandler::HandlePause(const base::ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_PAUSE);
  content::DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->Pause();
}

void DownloadsDOMHandler::HandleResume(const base::ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_RESUME);
  content::DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->Resume();
}

void DownloadsDOMHandler::HandleRemove(const base::ListValue* args) {
  if (!IsDeletingHistoryAllowed())
    return;

  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_REMOVE);
  content::DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->Remove();
}

void DownloadsDOMHandler::HandleCancel(const base::ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CANCEL);
  content::DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->Cancel(true);
}

void DownloadsDOMHandler::HandleClearAll(const base::ListValue* args) {
  if (IsDeletingHistoryAllowed()) {
    CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CLEAR_ALL);
    // IsDeletingHistoryAllowed already checked for the existence of the
    // manager.
    main_notifier_.GetManager()->RemoveAllDownloads();

    // If this is an incognito downloads page, clear All should clear main
    // download manager as well.
    if (original_notifier_.get() && original_notifier_->GetManager())
      original_notifier_->GetManager()->RemoveAllDownloads();
  }

  // downloads.js always clears the display and relies on HandleClearAll to
  // ScheduleSendCurrentDownloads(). If any downloads are removed, then
  // OnDownloadRemoved() will call it, but if no downloads are actually removed,
  // then HandleClearAll needs to call it manually.
  ScheduleSendCurrentDownloads();
}

void DownloadsDOMHandler::HandleOpenDownloadsFolder(
    const base::ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FOLDER);
  content::DownloadManager* manager = main_notifier_.GetManager();
  if (manager) {
    platform_util::OpenItem(
        Profile::FromBrowserContext(manager->GetBrowserContext()),
        DownloadPrefs::FromDownloadManager(manager)->DownloadPath());
  }
}

// DownloadsDOMHandler, private: ----------------------------------------------

void DownloadsDOMHandler::ScheduleSendCurrentDownloads() {
  // Don't call SendCurrentDownloads() every time anything changes. Batch them
  // together instead. This may handle hundreds of OnDownloadDestroyed() calls
  // in a single UI message loop iteration when the user Clears All downloads.
  if (update_scheduled_)
    return;
  update_scheduled_ = true;
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      base::Bind(&DownloadsDOMHandler::SendCurrentDownloads,
                 weak_ptr_factory_.GetWeakPtr()));
}

void DownloadsDOMHandler::SendCurrentDownloads() {
  update_scheduled_ = false;
  content::DownloadManager::DownloadVector all_items, filtered_items;
  if (main_notifier_.GetManager()) {
    main_notifier_.GetManager()->GetAllDownloads(&all_items);
    main_notifier_.GetManager()->CheckForHistoryFilesRemoval();
  }
  if (original_notifier_.get() && original_notifier_->GetManager()) {
    original_notifier_->GetManager()->GetAllDownloads(&all_items);
    original_notifier_->GetManager()->CheckForHistoryFilesRemoval();
  }
  DownloadQuery query;
  if (search_terms_ && !search_terms_->empty()) {
    query.AddFilter(DownloadQuery::FILTER_QUERY, *search_terms_.get());
  }
  query.AddFilter(base::Bind(&IsDownloadDisplayable));
  query.AddSorter(DownloadQuery::SORT_START_TIME, DownloadQuery::DESCENDING);
  query.Limit(kMaxDownloads);
  query.Search(all_items.begin(), all_items.end(), &filtered_items);
  base::ListValue results_value;
  for (content::DownloadManager::DownloadVector::const_iterator
       iter = filtered_items.begin(); iter != filtered_items.end(); ++iter) {
    results_value.Append(CreateDownloadItemValue(
        *iter,
        (original_notifier_.get() &&
          main_notifier_.GetManager() &&
          (main_notifier_.GetManager()->GetDownload((*iter)->GetId()) ==
          *iter))));
  }
  CallDownloadsList(results_value);
}

void DownloadsDOMHandler::ShowDangerPrompt(
    content::DownloadItem* dangerous_item) {
  DownloadDangerPrompt* danger_prompt = DownloadDangerPrompt::Create(
      dangerous_item,
      GetWebUIWebContents(),
      false,
      base::Bind(&DownloadsDOMHandler::DangerPromptDone,
                 weak_ptr_factory_.GetWeakPtr(), dangerous_item->GetId()));
  // danger_prompt will delete itself.
  DCHECK(danger_prompt);
}

void DownloadsDOMHandler::DangerPromptDone(
    int download_id, DownloadDangerPrompt::Action action) {
  if (action != DownloadDangerPrompt::ACCEPT)
    return;
  content::DownloadItem* item = NULL;
  if (main_notifier_.GetManager())
    item = main_notifier_.GetManager()->GetDownload(download_id);
  if (!item && original_notifier_.get() && original_notifier_->GetManager())
    item = original_notifier_->GetManager()->GetDownload(download_id);
  if (!item || item->IsDone())
    return;
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
  item->ValidateDangerousDownload();
}

bool DownloadsDOMHandler::IsDeletingHistoryAllowed() {
  content::DownloadManager* manager = main_notifier_.GetManager();
  return (manager &&
          Profile::FromBrowserContext(manager->GetBrowserContext())->
              GetPrefs()->GetBoolean(prefs::kAllowDeletingBrowserHistory));
}

content::DownloadItem* DownloadsDOMHandler::GetDownloadByValue(
    const base::ListValue* args) {
  int download_id = -1;
  if (!ExtractIntegerValue(args, &download_id))
    return NULL;
  content::DownloadItem* item = NULL;
  if (main_notifier_.GetManager())
    item = main_notifier_.GetManager()->GetDownload(download_id);
  if (!item && original_notifier_.get() && original_notifier_->GetManager())
    item = original_notifier_->GetManager()->GetDownload(download_id);
  return item;
}

content::WebContents* DownloadsDOMHandler::GetWebUIWebContents() {
  return web_ui()->GetWebContents();
}

void DownloadsDOMHandler::CallDownloadsList(const base::ListValue& downloads) {
  web_ui()->CallJavascriptFunction("downloadsList", downloads);
}

void DownloadsDOMHandler::CallDownloadUpdated(
    const base::ListValue& download_item) {
  web_ui()->CallJavascriptFunction("downloadUpdated", download_item);
}

/* [<][>][^][v][top][bottom][index][help] */