[Eraser strings] Remove unused Supervised User infobar and corresponding strings
[chromium-blink-merge.git] / chrome / browser / ui / webui / downloads_dom_handler.cc
blob5858b57bbba2698b1f47bd524ee2dd14cc9178a9
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.
5 #include "chrome/browser/ui/webui/downloads_dom_handler.h"
7 #include <algorithm>
8 #include <functional>
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/logging.h"
16 #include "base/memory/singleton.h"
17 #include "base/metrics/field_trial.h"
18 #include "base/metrics/histogram.h"
19 #include "base/prefs/pref_service.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_piece.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/supports_user_data.h"
24 #include "base/threading/thread.h"
25 #include "base/value_conversions.h"
26 #include "base/values.h"
27 #include "chrome/browser/browser_process.h"
28 #include "chrome/browser/download/download_crx_util.h"
29 #include "chrome/browser/download/download_danger_prompt.h"
30 #include "chrome/browser/download/download_history.h"
31 #include "chrome/browser/download/download_item_model.h"
32 #include "chrome/browser/download/download_prefs.h"
33 #include "chrome/browser/download/download_query.h"
34 #include "chrome/browser/download/download_service.h"
35 #include "chrome/browser/download/download_service_factory.h"
36 #include "chrome/browser/download/drag_download_item.h"
37 #include "chrome/browser/extensions/api/downloads/downloads_api.h"
38 #include "chrome/browser/extensions/extension_service.h"
39 #include "chrome/browser/platform_util.h"
40 #include "chrome/browser/profiles/profile.h"
41 #include "chrome/browser/ui/webui/fileicon_source.h"
42 #include "chrome/common/chrome_switches.h"
43 #include "chrome/common/pref_names.h"
44 #include "chrome/common/url_constants.h"
45 #include "content/public/browser/browser_thread.h"
46 #include "content/public/browser/download_item.h"
47 #include "content/public/browser/url_data_source.h"
48 #include "content/public/browser/user_metrics.h"
49 #include "content/public/browser/web_contents.h"
50 #include "content/public/browser/web_ui.h"
51 #include "extensions/browser/extension_system.h"
52 #include "net/base/filename_util.h"
53 #include "ui/base/l10n/time_format.h"
54 #include "ui/gfx/image/image.h"
56 using base::UserMetricsAction;
57 using content::BrowserContext;
58 using content::BrowserThread;
60 namespace {
62 // Maximum number of downloads to show. TODO(glen): Remove this and instead
63 // stuff the downloads down the pipe slowly.
64 static const size_t kMaxDownloads = 150;
66 enum DownloadsDOMEvent {
67 DOWNLOADS_DOM_EVENT_GET_DOWNLOADS = 0,
68 DOWNLOADS_DOM_EVENT_OPEN_FILE = 1,
69 DOWNLOADS_DOM_EVENT_DRAG = 2,
70 DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS = 3,
71 DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS = 4,
72 DOWNLOADS_DOM_EVENT_SHOW = 5,
73 DOWNLOADS_DOM_EVENT_PAUSE = 6,
74 DOWNLOADS_DOM_EVENT_REMOVE = 7,
75 DOWNLOADS_DOM_EVENT_CANCEL = 8,
76 DOWNLOADS_DOM_EVENT_CLEAR_ALL = 9,
77 DOWNLOADS_DOM_EVENT_OPEN_FOLDER = 10,
78 DOWNLOADS_DOM_EVENT_RESUME = 11,
79 DOWNLOADS_DOM_EVENT_MAX
82 void CountDownloadsDOMEvents(DownloadsDOMEvent event) {
83 UMA_HISTOGRAM_ENUMERATION("Download.DOMEvent",
84 event,
85 DOWNLOADS_DOM_EVENT_MAX);
88 // Returns a string constant to be used as the |danger_type| value in
89 // CreateDownloadItemValue(). Only return strings for DANGEROUS_FILE,
90 // DANGEROUS_URL, DANGEROUS_CONTENT, and UNCOMMON_CONTENT because the
91 // |danger_type| value is only defined if the value of |state| is |DANGEROUS|.
92 const char* GetDangerTypeString(content::DownloadDangerType danger_type) {
93 switch (danger_type) {
94 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
95 return "DANGEROUS_FILE";
96 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
97 return "DANGEROUS_URL";
98 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
99 return "DANGEROUS_CONTENT";
100 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
101 return "UNCOMMON_CONTENT";
102 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
103 return "DANGEROUS_HOST";
104 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
105 return "POTENTIALLY_UNWANTED";
106 default:
107 // Don't return a danger type string if it is NOT_DANGEROUS or
108 // MAYBE_DANGEROUS_CONTENT.
109 NOTREACHED();
110 return "";
114 // Returns a JSON dictionary containing some of the attributes of |download|.
115 // The JSON dictionary will also have a field "id" set to |id|, and a field
116 // "otr" set to |incognito|.
117 base::DictionaryValue* CreateDownloadItemValue(
118 content::DownloadItem* download_item,
119 bool incognito) {
120 // TODO(asanka): Move towards using download_model here for getting status and
121 // progress. The difference currently only matters to Drive downloads and
122 // those don't show up on the downloads page, but should.
123 DownloadItemModel download_model(download_item);
125 // The items which are to be written into file_value are also described in
126 // chrome/browser/resources/downloads/downloads.js in @typedef for
127 // BackendDownloadObject. Please update it whenever you add or remove
128 // any keys in file_value.
129 base::DictionaryValue* file_value = new base::DictionaryValue();
131 file_value->SetInteger(
132 "started", static_cast<int>(download_item->GetStartTime().ToTimeT()));
133 file_value->SetString(
134 "since_string", ui::TimeFormat::RelativeDate(
135 download_item->GetStartTime(), NULL));
136 file_value->SetString(
137 "date_string", base::TimeFormatShortDate(download_item->GetStartTime()));
138 file_value->SetString("id", base::Uint64ToString(download_item->GetId()));
140 base::FilePath download_path(download_item->GetTargetFilePath());
141 file_value->Set("file_path", base::CreateFilePathValue(download_path));
142 file_value->SetString("file_url",
143 net::FilePathToFileURL(download_path).spec());
145 extensions::DownloadedByExtension* by_ext =
146 extensions::DownloadedByExtension::Get(download_item);
147 if (by_ext) {
148 file_value->SetString("by_ext_id", by_ext->id());
149 file_value->SetString("by_ext_name", by_ext->name());
150 // Lookup the extension's current name() in case the user changed their
151 // language. This won't work if the extension was uninstalled, so the name
152 // might be the wrong language.
153 bool include_disabled = true;
154 const extensions::Extension* extension = extensions::ExtensionSystem::Get(
155 Profile::FromBrowserContext(download_item->GetBrowserContext()))->
156 extension_service()->GetExtensionById(by_ext->id(), include_disabled);
157 if (extension)
158 file_value->SetString("by_ext_name", extension->name());
161 // Keep file names as LTR.
162 base::string16 file_name =
163 download_item->GetFileNameToReportUser().LossyDisplayName();
164 file_name = base::i18n::GetDisplayStringInLTRDirectionality(file_name);
165 file_value->SetString("file_name", file_name);
166 file_value->SetString("url", download_item->GetURL().spec());
167 file_value->SetBoolean("otr", incognito);
168 file_value->SetInteger("total", static_cast<int>(
169 download_item->GetTotalBytes()));
170 file_value->SetBoolean("file_externally_removed",
171 download_item->GetFileExternallyRemoved());
172 file_value->SetBoolean("retry", false); // Overridden below if needed.
173 file_value->SetBoolean("resume", download_item->CanResume());
175 switch (download_item->GetState()) {
176 case content::DownloadItem::IN_PROGRESS: {
177 if (download_item->IsDangerous()) {
178 file_value->SetString("state", "DANGEROUS");
179 // These are the only danger states that the UI is equipped to handle.
180 DCHECK(download_item->GetDangerType() ==
181 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
182 download_item->GetDangerType() ==
183 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
184 download_item->GetDangerType() ==
185 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT ||
186 download_item->GetDangerType() ==
187 content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT ||
188 download_item->GetDangerType() ==
189 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST ||
190 download_item->GetDangerType() ==
191 content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED);
192 const char* danger_type_value =
193 GetDangerTypeString(download_item->GetDangerType());
194 file_value->SetString("danger_type", danger_type_value);
195 } else if (download_item->IsPaused()) {
196 file_value->SetString("state", "PAUSED");
197 } else {
198 file_value->SetString("state", "IN_PROGRESS");
200 file_value->SetString("progress_status_text",
201 download_model.GetTabProgressStatusText());
203 int percent = download_item->PercentComplete();
204 if (!switches::MdDownloadsEnabled())
205 percent = std::max(0, percent);
206 file_value->SetInteger("percent", percent);
208 file_value->SetInteger("received",
209 static_cast<int>(download_item->GetReceivedBytes()));
210 break;
213 case content::DownloadItem::INTERRUPTED:
214 file_value->SetString("state", "INTERRUPTED");
216 file_value->SetString("progress_status_text",
217 download_model.GetTabProgressStatusText());
219 if (download_item->CanResume()) {
220 file_value->SetInteger("percent",
221 static_cast<int>(download_item->PercentComplete()));
223 file_value->SetInteger("received",
224 static_cast<int>(download_item->GetReceivedBytes()));
225 file_value->SetString("last_reason_text",
226 download_model.GetInterruptReasonText());
227 if (content::DOWNLOAD_INTERRUPT_REASON_CRASH ==
228 download_item->GetLastReason() && !download_item->CanResume())
229 file_value->SetBoolean("retry", true);
230 break;
232 case content::DownloadItem::CANCELLED:
233 file_value->SetString("state", "CANCELLED");
234 file_value->SetBoolean("retry", true);
235 break;
237 case content::DownloadItem::COMPLETE:
238 DCHECK(!download_item->IsDangerous());
239 file_value->SetString("state", "COMPLETE");
240 break;
242 case content::DownloadItem::MAX_DOWNLOAD_STATE:
243 NOTREACHED();
246 return file_value;
249 // Filters out extension downloads and downloads that don't have a filename yet.
250 bool IsDownloadDisplayable(const content::DownloadItem& item) {
251 return !download_crx_util::IsExtensionDownload(item) &&
252 !item.IsTemporary() &&
253 !item.GetFileNameToReportUser().empty() &&
254 !item.GetTargetFilePath().empty() &&
255 DownloadItemModel(
256 const_cast<content::DownloadItem*>(&item)).ShouldShowInShelf();
259 } // namespace
261 DownloadsDOMHandler::DownloadsDOMHandler(
262 content::DownloadManager* download_manager)
263 : download_manager_(download_manager),
264 update_scheduled_(false),
265 weak_ptr_factory_(this) {
266 // Create our fileicon data source.
267 Profile* profile = Profile::FromBrowserContext(
268 download_manager->GetBrowserContext());
269 content::URLDataSource::Add(profile, new FileIconSource());
272 DownloadsDOMHandler::~DownloadsDOMHandler() {
273 FinalizeRemovals();
276 // DownloadsDOMHandler, public: -----------------------------------------------
278 void DownloadsDOMHandler::RegisterMessages() {
279 web_ui()->RegisterMessageCallback("getDownloads",
280 base::Bind(&DownloadsDOMHandler::HandleGetDownloads,
281 weak_ptr_factory_.GetWeakPtr()));
282 web_ui()->RegisterMessageCallback("openFile",
283 base::Bind(&DownloadsDOMHandler::HandleOpenFile,
284 weak_ptr_factory_.GetWeakPtr()));
285 web_ui()->RegisterMessageCallback("drag",
286 base::Bind(&DownloadsDOMHandler::HandleDrag,
287 weak_ptr_factory_.GetWeakPtr()));
288 web_ui()->RegisterMessageCallback("saveDangerous",
289 base::Bind(&DownloadsDOMHandler::HandleSaveDangerous,
290 weak_ptr_factory_.GetWeakPtr()));
291 web_ui()->RegisterMessageCallback("discardDangerous",
292 base::Bind(&DownloadsDOMHandler::HandleDiscardDangerous,
293 weak_ptr_factory_.GetWeakPtr()));
294 web_ui()->RegisterMessageCallback("show",
295 base::Bind(&DownloadsDOMHandler::HandleShow,
296 weak_ptr_factory_.GetWeakPtr()));
297 web_ui()->RegisterMessageCallback("pause",
298 base::Bind(&DownloadsDOMHandler::HandlePause,
299 weak_ptr_factory_.GetWeakPtr()));
300 web_ui()->RegisterMessageCallback("resume",
301 base::Bind(&DownloadsDOMHandler::HandleResume,
302 weak_ptr_factory_.GetWeakPtr()));
303 web_ui()->RegisterMessageCallback("remove",
304 base::Bind(&DownloadsDOMHandler::HandleRemove,
305 weak_ptr_factory_.GetWeakPtr()));
306 web_ui()->RegisterMessageCallback("undo",
307 base::Bind(&DownloadsDOMHandler::HandleUndo,
308 weak_ptr_factory_.GetWeakPtr()));
309 web_ui()->RegisterMessageCallback("cancel",
310 base::Bind(&DownloadsDOMHandler::HandleCancel,
311 weak_ptr_factory_.GetWeakPtr()));
312 web_ui()->RegisterMessageCallback("clearAll",
313 base::Bind(&DownloadsDOMHandler::HandleClearAll,
314 weak_ptr_factory_.GetWeakPtr()));
315 web_ui()->RegisterMessageCallback("openDownloadsFolder",
316 base::Bind(&DownloadsDOMHandler::HandleOpenDownloadsFolder,
317 weak_ptr_factory_.GetWeakPtr()));
320 void DownloadsDOMHandler::OnDownloadCreated(
321 content::DownloadManager* manager, content::DownloadItem* download_item) {
322 if (IsDownloadDisplayable(*download_item))
323 ScheduleSendCurrentDownloads();
324 else
325 new_downloads_.insert(download_item->GetId());
328 void DownloadsDOMHandler::OnDownloadUpdated(
329 content::DownloadManager* manager,
330 content::DownloadItem* download_item) {
331 if (update_scheduled_)
332 return;
334 bool showing_new_item = false;
336 if (new_downloads_.count(download_item->GetId())) {
337 // A new download (that the page doesn't know about yet) has been updated.
338 if (!IsDownloadDisplayable(*download_item)) {
339 // Item isn't ready to be displayed yet. Wait until it is.
340 return;
343 new_downloads_.erase(download_item->GetId());
344 showing_new_item = true;
347 if (showing_new_item || DownloadItemModel(download_item).IsBeingRevived() ||
348 !IsDownloadDisplayable(*download_item)) {
349 // A download will be shown or hidden by this update. Resend the list.
350 ScheduleSendCurrentDownloads();
351 return;
354 if (search_terms_ && !search_terms_->empty()) {
355 // Don't CallUpdateItem() if download_item doesn't match
356 // search_terms_.
357 // TODO(benjhayden): Consider splitting MatchesQuery() out to a function.
358 content::DownloadManager::DownloadVector all_items, filtered_items;
359 all_items.push_back(download_item);
360 DownloadQuery query;
361 query.AddFilter(DownloadQuery::FILTER_QUERY, *search_terms_);
362 query.Search(all_items.begin(), all_items.end(), &filtered_items);
363 if (filtered_items.empty())
364 return;
367 DCHECK(manager);
368 scoped_ptr<base::DictionaryValue> item(CreateDownloadItemValue(
369 download_item,
370 original_notifier_ && manager == GetMainNotifierManager()));
371 CallUpdateItem(*item);
374 void DownloadsDOMHandler::OnDownloadRemoved(
375 content::DownloadManager* manager,
376 content::DownloadItem* download_item) {
377 if (!DownloadItemModel(download_item).ShouldShowInShelf())
378 return;
380 // This relies on |download_item| being removed from DownloadManager in this
381 // MessageLoop iteration. |download_item| may not have been removed from
382 // DownloadManager when OnDownloadRemoved() is fired, so bounce off the
383 // MessageLoop to give it a chance to be removed. SendCurrentDownloads() looks
384 // at all downloads, and we do not tell it that |download_item| is being
385 // removed. If DownloadManager is ever changed to not immediately remove
386 // |download_item| from its map when OnDownloadRemoved is sent, then
387 // DownloadsDOMHandler::OnDownloadRemoved() will need to explicitly tell
388 // SendCurrentDownloads() that |download_item| was removed. A
389 // SupportsUserData::Data would be the correct way to do this.
390 ScheduleSendCurrentDownloads();
393 void DownloadsDOMHandler::HandleGetDownloads(const base::ListValue* args) {
394 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_GET_DOWNLOADS);
395 search_terms_.reset(args && !args->empty() ? args->DeepCopy() : NULL);
396 ScheduleSendCurrentDownloads();
398 if (!main_notifier_) {
399 main_notifier_.reset(new AllDownloadItemNotifier(download_manager_, this));
401 Profile* profile = Profile::FromBrowserContext(
402 download_manager_->GetBrowserContext());
403 if (profile->IsOffTheRecord()) {
404 original_notifier_.reset(new AllDownloadItemNotifier(
405 BrowserContext::GetDownloadManager(profile->GetOriginalProfile()),
406 this));
411 void DownloadsDOMHandler::HandleOpenFile(const base::ListValue* args) {
412 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FILE);
413 content::DownloadItem* file = GetDownloadByValue(args);
414 if (file)
415 file->OpenDownload();
418 void DownloadsDOMHandler::HandleDrag(const base::ListValue* args) {
419 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DRAG);
420 content::DownloadItem* file = GetDownloadByValue(args);
421 if (!file)
422 return;
424 content::WebContents* web_contents = GetWebUIWebContents();
425 // |web_contents| is only NULL in the test.
426 if (!web_contents)
427 return;
429 if (file->GetState() != content::DownloadItem::COMPLETE)
430 return;
432 gfx::Image* icon = g_browser_process->icon_manager()->LookupIconFromFilepath(
433 file->GetTargetFilePath(), IconLoader::NORMAL);
434 gfx::NativeView view = web_contents->GetNativeView();
436 // Enable nested tasks during DnD, while |DragDownload()| blocks.
437 base::MessageLoop::ScopedNestableTaskAllower allow(
438 base::MessageLoop::current());
439 DragDownloadItem(file, icon, view);
443 void DownloadsDOMHandler::HandleSaveDangerous(const base::ListValue* args) {
444 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
445 content::DownloadItem* file = GetDownloadByValue(args);
446 if (file)
447 ShowDangerPrompt(file);
450 void DownloadsDOMHandler::HandleDiscardDangerous(const base::ListValue* args) {
451 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS);
452 content::DownloadItem* file = GetDownloadByValue(args);
453 if (file)
454 file->Remove();
457 void DownloadsDOMHandler::HandleShow(const base::ListValue* args) {
458 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SHOW);
459 content::DownloadItem* file = GetDownloadByValue(args);
460 if (file)
461 file->ShowDownloadInShell();
464 void DownloadsDOMHandler::HandlePause(const base::ListValue* args) {
465 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_PAUSE);
466 content::DownloadItem* file = GetDownloadByValue(args);
467 if (file)
468 file->Pause();
471 void DownloadsDOMHandler::HandleResume(const base::ListValue* args) {
472 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_RESUME);
473 content::DownloadItem* file = GetDownloadByValue(args);
474 if (file)
475 file->Resume();
478 void DownloadsDOMHandler::HandleRemove(const base::ListValue* args) {
479 if (!IsDeletingHistoryAllowed())
480 return;
482 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_REMOVE);
483 content::DownloadItem* file = GetDownloadByValue(args);
484 if (!file)
485 return;
487 std::vector<content::DownloadItem*> downloads;
488 downloads.push_back(file);
489 RemoveDownloads(downloads);
492 void DownloadsDOMHandler::HandleUndo(const base::ListValue* args) {
493 // TODO(dbeam): handle more than removed downloads someday?
494 if (removals_.empty())
495 return;
497 const std::set<uint32> last_removed_ids = removals_.back();
498 removals_.pop_back();
500 for (auto id : last_removed_ids) {
501 content::DownloadItem* download = GetDownloadById(id);
502 if (!download)
503 continue;
505 DownloadItemModel model(download);
506 model.SetShouldShowInShelf(true);
507 model.SetIsBeingRevived(true);
509 download->UpdateObservers();
511 model.SetIsBeingRevived(false);
515 void DownloadsDOMHandler::HandleCancel(const base::ListValue* args) {
516 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CANCEL);
517 content::DownloadItem* file = GetDownloadByValue(args);
518 if (file)
519 file->Cancel(true);
522 void DownloadsDOMHandler::HandleClearAll(const base::ListValue* args) {
523 if (!IsDeletingHistoryAllowed()) {
524 // This should only be reached during tests.
525 return;
528 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CLEAR_ALL);
530 std::vector<content::DownloadItem*> downloads;
531 if (GetMainNotifierManager())
532 GetMainNotifierManager()->GetAllDownloads(&downloads);
533 if (GetOriginalNotifierManager())
534 GetOriginalNotifierManager()->GetAllDownloads(&downloads);
535 RemoveDownloads(downloads);
538 void DownloadsDOMHandler::RemoveDownloads(
539 const std::vector<content::DownloadItem*>& to_remove) {
540 std::set<uint32> ids;
542 for (auto* download : to_remove) {
543 DownloadItemModel item_model(download);
544 if (!item_model.ShouldShowInShelf() ||
545 download->GetState() == content::DownloadItem::IN_PROGRESS) {
546 continue;
549 item_model.SetShouldShowInShelf(false);
550 ids.insert(download->GetId());
551 download->UpdateObservers();
554 if (!ids.empty())
555 removals_.push_back(ids);
558 void DownloadsDOMHandler::HandleOpenDownloadsFolder(
559 const base::ListValue* args) {
560 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FOLDER);
561 content::DownloadManager* manager = GetMainNotifierManager();
562 if (manager) {
563 platform_util::OpenItem(
564 Profile::FromBrowserContext(manager->GetBrowserContext()),
565 DownloadPrefs::FromDownloadManager(manager)->DownloadPath(),
566 platform_util::OPEN_FOLDER, platform_util::OpenOperationCallback());
570 // DownloadsDOMHandler, private: ----------------------------------------------
572 void DownloadsDOMHandler::ScheduleSendCurrentDownloads() {
573 // Don't call SendCurrentDownloads() every time anything changes. Batch them
574 // together instead. This may handle hundreds of OnDownloadDestroyed() calls
575 // in a single UI message loop iteration when the user Clears All downloads.
576 if (update_scheduled_)
577 return;
579 update_scheduled_ = true;
581 BrowserThread::PostTask(
582 BrowserThread::UI, FROM_HERE,
583 base::Bind(&DownloadsDOMHandler::SendCurrentDownloads,
584 weak_ptr_factory_.GetWeakPtr()));
587 content::DownloadManager* DownloadsDOMHandler::GetMainNotifierManager() const {
588 return main_notifier_ ? main_notifier_->GetManager() : nullptr;
591 content::DownloadManager* DownloadsDOMHandler::GetOriginalNotifierManager()
592 const {
593 return original_notifier_ ? original_notifier_->GetManager() : nullptr;
596 void DownloadsDOMHandler::FinalizeRemovals() {
597 while (!removals_.empty()) {
598 const std::set<uint32> remove = removals_.back();
599 removals_.pop_back();
601 for (const auto id : remove) {
602 content::DownloadItem* download = GetDownloadById(id);
603 if (download)
604 download->Remove();
609 void DownloadsDOMHandler::SendCurrentDownloads() {
610 update_scheduled_ = false;
612 content::DownloadManager::DownloadVector all_items, filtered_items;
613 if (GetMainNotifierManager()) {
614 GetMainNotifierManager()->GetAllDownloads(&all_items);
615 GetMainNotifierManager()->CheckForHistoryFilesRemoval();
617 if (GetOriginalNotifierManager()) {
618 GetOriginalNotifierManager()->GetAllDownloads(&all_items);
619 GetOriginalNotifierManager()->CheckForHistoryFilesRemoval();
622 DownloadQuery query;
623 if (search_terms_ && !search_terms_->empty())
624 query.AddFilter(DownloadQuery::FILTER_QUERY, *search_terms_);
625 query.AddFilter(base::Bind(&IsDownloadDisplayable));
626 query.AddSorter(DownloadQuery::SORT_START_TIME, DownloadQuery::DESCENDING);
627 query.Limit(kMaxDownloads);
628 query.Search(all_items.begin(), all_items.end(), &filtered_items);
630 base::ListValue results_value;
631 for (auto* item : filtered_items) {
632 results_value.Append(CreateDownloadItemValue(
633 item,
634 original_notifier_ && GetMainNotifierManager() &&
635 GetMainNotifierManager()->GetDownload(item->GetId()) == item));
637 CallUpdateAll(results_value);
640 void DownloadsDOMHandler::ShowDangerPrompt(
641 content::DownloadItem* dangerous_item) {
642 DownloadDangerPrompt* danger_prompt = DownloadDangerPrompt::Create(
643 dangerous_item,
644 GetWebUIWebContents(),
645 false,
646 base::Bind(&DownloadsDOMHandler::DangerPromptDone,
647 weak_ptr_factory_.GetWeakPtr(), dangerous_item->GetId()));
648 // danger_prompt will delete itself.
649 DCHECK(danger_prompt);
652 void DownloadsDOMHandler::DangerPromptDone(
653 int download_id, DownloadDangerPrompt::Action action) {
654 if (action != DownloadDangerPrompt::ACCEPT)
655 return;
656 content::DownloadItem* item = NULL;
657 if (GetMainNotifierManager())
658 item = GetMainNotifierManager()->GetDownload(download_id);
659 if (!item && GetOriginalNotifierManager())
660 item = GetOriginalNotifierManager()->GetDownload(download_id);
661 if (!item || item->IsDone())
662 return;
663 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
664 item->ValidateDangerousDownload();
667 bool DownloadsDOMHandler::IsDeletingHistoryAllowed() {
668 content::DownloadManager* manager = GetMainNotifierManager();
669 return manager &&
670 Profile::FromBrowserContext(manager->GetBrowserContext())->
671 GetPrefs()->GetBoolean(prefs::kAllowDeletingBrowserHistory);
674 content::DownloadItem* DownloadsDOMHandler::GetDownloadByValue(
675 const base::ListValue* args) {
676 std::string download_id;
677 if (!args->GetString(0, &download_id)) {
678 NOTREACHED();
679 return nullptr;
682 uint64 id;
683 if (!base::StringToUint64(download_id, &id)) {
684 NOTREACHED();
685 return nullptr;
688 return GetDownloadById(static_cast<uint32>(id));
691 content::DownloadItem* DownloadsDOMHandler::GetDownloadById(uint32 id) {
692 content::DownloadItem* item = NULL;
693 if (GetMainNotifierManager())
694 item = GetMainNotifierManager()->GetDownload(id);
695 if (!item && GetOriginalNotifierManager())
696 item = GetOriginalNotifierManager()->GetDownload(id);
697 return item;
700 content::WebContents* DownloadsDOMHandler::GetWebUIWebContents() {
701 return web_ui()->GetWebContents();
704 void DownloadsDOMHandler::CallUpdateAll(const base::ListValue& list) {
705 web_ui()->CallJavascriptFunction("downloads.Manager.updateAll", list);
708 void DownloadsDOMHandler::CallUpdateItem(const base::DictionaryValue& item) {
709 web_ui()->CallJavascriptFunction("downloads.Manager.updateItem", item);