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