Vectorize sad tab image.
[chromium-blink-merge.git] / chrome / browser / ui / webui / downloads_dom_handler.cc
blob0ce308cf19ff74995a8aad7d3e3e66ebd8139d58
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/downloads_util.h"
42 #include "chrome/browser/ui/webui/fileicon_source.h"
43 #include "chrome/common/chrome_switches.h"
44 #include "chrome/common/pref_names.h"
45 #include "chrome/common/url_constants.h"
46 #include "content/public/browser/browser_thread.h"
47 #include "content/public/browser/download_item.h"
48 #include "content/public/browser/url_data_source.h"
49 #include "content/public/browser/user_metrics.h"
50 #include "content/public/browser/web_contents.h"
51 #include "content/public/browser/web_ui.h"
52 #include "extensions/browser/extension_system.h"
53 #include "net/base/filename_util.h"
54 #include "third_party/icu/source/i18n/unicode/datefmt.h"
55 #include "ui/base/l10n/time_format.h"
56 #include "ui/gfx/image/image.h"
58 using base::UserMetricsAction;
59 using content::BrowserContext;
60 using content::BrowserThread;
62 namespace {
64 // Maximum number of downloads to show. TODO(glen): Remove this and instead
65 // stuff the downloads down the pipe slowly.
66 size_t MaxNumberOfDownloads() {
67 return MdDownloadsEnabled() ? 50 : 150;
70 enum DownloadsDOMEvent {
71 DOWNLOADS_DOM_EVENT_GET_DOWNLOADS = 0,
72 DOWNLOADS_DOM_EVENT_OPEN_FILE = 1,
73 DOWNLOADS_DOM_EVENT_DRAG = 2,
74 DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS = 3,
75 DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS = 4,
76 DOWNLOADS_DOM_EVENT_SHOW = 5,
77 DOWNLOADS_DOM_EVENT_PAUSE = 6,
78 DOWNLOADS_DOM_EVENT_REMOVE = 7,
79 DOWNLOADS_DOM_EVENT_CANCEL = 8,
80 DOWNLOADS_DOM_EVENT_CLEAR_ALL = 9,
81 DOWNLOADS_DOM_EVENT_OPEN_FOLDER = 10,
82 DOWNLOADS_DOM_EVENT_RESUME = 11,
83 DOWNLOADS_DOM_EVENT_MAX
86 void CountDownloadsDOMEvents(DownloadsDOMEvent event) {
87 UMA_HISTOGRAM_ENUMERATION("Download.DOMEvent",
88 event,
89 DOWNLOADS_DOM_EVENT_MAX);
92 // Returns a string constant to be used as the |danger_type| value in
93 // CreateDownloadItemValue(). Only return strings for DANGEROUS_FILE,
94 // DANGEROUS_URL, DANGEROUS_CONTENT, and UNCOMMON_CONTENT because the
95 // |danger_type| value is only defined if the value of |state| is |DANGEROUS|.
96 const char* GetDangerTypeString(content::DownloadDangerType danger_type) {
97 switch (danger_type) {
98 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
99 return "DANGEROUS_FILE";
100 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
101 return "DANGEROUS_URL";
102 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
103 return "DANGEROUS_CONTENT";
104 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
105 return "UNCOMMON_CONTENT";
106 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
107 return "DANGEROUS_HOST";
108 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
109 return "POTENTIALLY_UNWANTED";
110 default:
111 // Don't return a danger type string if it is NOT_DANGEROUS or
112 // MAYBE_DANGEROUS_CONTENT.
113 NOTREACHED();
114 return "";
118 // TODO(dbeam): if useful elsewhere, move to base/i18n/time_formatting.h?
119 base::string16 TimeFormatLongDate(const base::Time& time) {
120 scoped_ptr<icu::DateFormat> formatter(
121 icu::DateFormat::createDateInstance(icu::DateFormat::kLong));
122 icu::UnicodeString date_string;
123 formatter->format(static_cast<UDate>(time.ToDoubleT() * 1000), date_string);
124 return base::string16(date_string.getBuffer(),
125 static_cast<size_t>(date_string.length()));
128 // Returns a JSON dictionary containing some of the attributes of |download|.
129 // The JSON dictionary will also have a field "id" set to |id|, and a field
130 // "otr" set to |incognito|.
131 base::DictionaryValue* CreateDownloadItemValue(
132 content::DownloadItem* download_item,
133 bool incognito) {
134 // TODO(asanka): Move towards using download_model here for getting status and
135 // progress. The difference currently only matters to Drive downloads and
136 // those don't show up on the downloads page, but should.
137 DownloadItemModel download_model(download_item);
139 // The items which are to be written into file_value are also described in
140 // chrome/browser/resources/downloads/downloads.js in @typedef for
141 // BackendDownloadObject. Please update it whenever you add or remove
142 // any keys in file_value.
143 base::DictionaryValue* file_value = new base::DictionaryValue();
145 file_value->SetInteger(
146 "started", static_cast<int>(download_item->GetStartTime().ToTimeT()));
147 file_value->SetString(
148 "since_string", ui::TimeFormat::RelativeDate(
149 download_item->GetStartTime(), NULL));
151 base::Time start_time = download_item->GetStartTime();
152 base::string16 date_string = MdDownloadsEnabled() ?
153 TimeFormatLongDate(start_time) : base::TimeFormatShortDate(start_time);
154 file_value->SetString("date_string", date_string);
156 file_value->SetString("id", base::Uint64ToString(download_item->GetId()));
158 base::FilePath download_path(download_item->GetTargetFilePath());
159 file_value->Set("file_path", base::CreateFilePathValue(download_path));
160 file_value->SetString("file_url",
161 net::FilePathToFileURL(download_path).spec());
163 extensions::DownloadedByExtension* by_ext =
164 extensions::DownloadedByExtension::Get(download_item);
165 std::string by_ext_id;
166 std::string by_ext_name;
167 if (by_ext) {
168 by_ext_id = by_ext->id();
169 // TODO(dbeam): why doesn't DownloadsByExtension::name() return a string16?
170 by_ext_name = by_ext->name();
172 // Lookup the extension's current name() in case the user changed their
173 // language. This won't work if the extension was uninstalled, so the name
174 // might be the wrong language.
175 bool include_disabled = true;
176 const extensions::Extension* extension = extensions::ExtensionSystem::Get(
177 Profile::FromBrowserContext(download_item->GetBrowserContext()))->
178 extension_service()->GetExtensionById(by_ext->id(), include_disabled);
179 if (extension)
180 file_value->SetString("by_ext_name", extension->name());
182 file_value->SetString("by_ext_id", by_ext_id);
183 file_value->SetString("by_ext_name", by_ext_name);
185 // Keep file names as LTR.
186 base::string16 file_name =
187 download_item->GetFileNameToReportUser().LossyDisplayName();
188 file_name = base::i18n::GetDisplayStringInLTRDirectionality(file_name);
189 file_value->SetString("file_name", file_name);
190 file_value->SetString("url", download_item->GetURL().spec());
191 file_value->SetBoolean("otr", incognito);
192 file_value->SetInteger("total", static_cast<int>(
193 download_item->GetTotalBytes()));
194 file_value->SetBoolean("file_externally_removed",
195 download_item->GetFileExternallyRemoved());
196 file_value->SetBoolean("resume", download_item->CanResume());
198 const char* danger_type = "";
199 base::string16 last_reason_text;
200 // -2 is invalid, -1 means indeterminate, and 0-100 are in-progress.
201 int percent = -2;
202 base::string16 progress_status_text;
203 bool retry = false;
204 const char* state = nullptr;
206 switch (download_item->GetState()) {
207 case content::DownloadItem::IN_PROGRESS: {
208 if (download_item->IsDangerous()) {
209 state = "DANGEROUS";
210 // These are the only danger states that the UI is equipped to handle.
211 DCHECK(download_item->GetDangerType() ==
212 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
213 download_item->GetDangerType() ==
214 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
215 download_item->GetDangerType() ==
216 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT ||
217 download_item->GetDangerType() ==
218 content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT ||
219 download_item->GetDangerType() ==
220 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST ||
221 download_item->GetDangerType() ==
222 content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED);
223 danger_type = GetDangerTypeString(download_item->GetDangerType());
224 } else if (download_item->IsPaused()) {
225 state = "PAUSED";
226 } else {
227 state = "IN_PROGRESS";
229 progress_status_text = download_model.GetTabProgressStatusText();
231 percent = download_item->PercentComplete();
232 if (!MdDownloadsEnabled())
233 percent = std::max(0, percent);
235 break;
238 case content::DownloadItem::INTERRUPTED:
239 state = "INTERRUPTED";
240 progress_status_text = download_model.GetTabProgressStatusText();
242 if (download_item->CanResume())
243 percent = download_item->PercentComplete();
245 last_reason_text = download_model.GetInterruptReasonText();
246 if (content::DOWNLOAD_INTERRUPT_REASON_CRASH ==
247 download_item->GetLastReason() && !download_item->CanResume()) {
248 retry = true;
250 break;
252 case content::DownloadItem::CANCELLED:
253 state = "CANCELLED";
254 retry = true;
255 break;
257 case content::DownloadItem::COMPLETE:
258 DCHECK(!download_item->IsDangerous());
259 state = "COMPLETE";
260 break;
262 case content::DownloadItem::MAX_DOWNLOAD_STATE:
263 NOTREACHED();
266 DCHECK(state);
268 file_value->SetString("danger_type", danger_type);
269 file_value->SetString("last_reason_text", last_reason_text);
270 file_value->SetInteger("percent", percent);
271 file_value->SetString("progress_status_text", progress_status_text);
272 file_value->SetBoolean("retry", retry);
273 file_value->SetString("state", state);
275 return file_value;
278 // Filters out extension downloads and downloads that don't have a filename yet.
279 bool IsDownloadDisplayable(const content::DownloadItem& item) {
280 return !download_crx_util::IsExtensionDownload(item) &&
281 !item.IsTemporary() &&
282 !item.GetFileNameToReportUser().empty() &&
283 !item.GetTargetFilePath().empty() &&
284 DownloadItemModel(
285 const_cast<content::DownloadItem*>(&item)).ShouldShowInShelf();
288 } // namespace
290 DownloadsDOMHandler::DownloadsDOMHandler(
291 content::DownloadManager* download_manager)
292 : download_manager_(download_manager),
293 update_scheduled_(false),
294 weak_ptr_factory_(this) {
295 // Create our fileicon data source.
296 Profile* profile = Profile::FromBrowserContext(
297 download_manager->GetBrowserContext());
298 content::URLDataSource::Add(profile, new FileIconSource());
301 DownloadsDOMHandler::~DownloadsDOMHandler() {
302 FinalizeRemovals();
305 // DownloadsDOMHandler, public: -----------------------------------------------
307 void DownloadsDOMHandler::RegisterMessages() {
308 web_ui()->RegisterMessageCallback("getDownloads",
309 base::Bind(&DownloadsDOMHandler::HandleGetDownloads,
310 weak_ptr_factory_.GetWeakPtr()));
311 web_ui()->RegisterMessageCallback("openFile",
312 base::Bind(&DownloadsDOMHandler::HandleOpenFile,
313 weak_ptr_factory_.GetWeakPtr()));
314 web_ui()->RegisterMessageCallback("drag",
315 base::Bind(&DownloadsDOMHandler::HandleDrag,
316 weak_ptr_factory_.GetWeakPtr()));
317 web_ui()->RegisterMessageCallback("saveDangerous",
318 base::Bind(&DownloadsDOMHandler::HandleSaveDangerous,
319 weak_ptr_factory_.GetWeakPtr()));
320 web_ui()->RegisterMessageCallback("discardDangerous",
321 base::Bind(&DownloadsDOMHandler::HandleDiscardDangerous,
322 weak_ptr_factory_.GetWeakPtr()));
323 web_ui()->RegisterMessageCallback("show",
324 base::Bind(&DownloadsDOMHandler::HandleShow,
325 weak_ptr_factory_.GetWeakPtr()));
326 web_ui()->RegisterMessageCallback("pause",
327 base::Bind(&DownloadsDOMHandler::HandlePause,
328 weak_ptr_factory_.GetWeakPtr()));
329 web_ui()->RegisterMessageCallback("resume",
330 base::Bind(&DownloadsDOMHandler::HandleResume,
331 weak_ptr_factory_.GetWeakPtr()));
332 web_ui()->RegisterMessageCallback("remove",
333 base::Bind(&DownloadsDOMHandler::HandleRemove,
334 weak_ptr_factory_.GetWeakPtr()));
335 web_ui()->RegisterMessageCallback("undo",
336 base::Bind(&DownloadsDOMHandler::HandleUndo,
337 weak_ptr_factory_.GetWeakPtr()));
338 web_ui()->RegisterMessageCallback("cancel",
339 base::Bind(&DownloadsDOMHandler::HandleCancel,
340 weak_ptr_factory_.GetWeakPtr()));
341 web_ui()->RegisterMessageCallback("clearAll",
342 base::Bind(&DownloadsDOMHandler::HandleClearAll,
343 weak_ptr_factory_.GetWeakPtr()));
344 web_ui()->RegisterMessageCallback("openDownloadsFolder",
345 base::Bind(&DownloadsDOMHandler::HandleOpenDownloadsFolder,
346 weak_ptr_factory_.GetWeakPtr()));
349 void DownloadsDOMHandler::OnDownloadCreated(
350 content::DownloadManager* manager, content::DownloadItem* download_item) {
351 if (IsDownloadDisplayable(*download_item))
352 ScheduleSendCurrentDownloads();
353 else
354 new_downloads_.insert(download_item->GetId());
357 void DownloadsDOMHandler::OnDownloadUpdated(
358 content::DownloadManager* manager,
359 content::DownloadItem* download_item) {
360 if (update_scheduled_)
361 return;
363 bool showing_new_item = false;
365 if (new_downloads_.count(download_item->GetId())) {
366 // A new download (that the page doesn't know about yet) has been updated.
367 if (!IsDownloadDisplayable(*download_item)) {
368 // Item isn't ready to be displayed yet. Wait until it is.
369 return;
372 new_downloads_.erase(download_item->GetId());
373 showing_new_item = true;
376 if (showing_new_item || DownloadItemModel(download_item).IsBeingRevived() ||
377 !IsDownloadDisplayable(*download_item)) {
378 // A download will be shown or hidden by this update. Resend the list.
379 ScheduleSendCurrentDownloads();
380 return;
383 if (search_terms_ && !search_terms_->empty()) {
384 // Don't CallUpdateItem() if download_item doesn't match
385 // search_terms_.
386 // TODO(benjhayden): Consider splitting MatchesQuery() out to a function.
387 content::DownloadManager::DownloadVector all_items, filtered_items;
388 all_items.push_back(download_item);
389 DownloadQuery query;
390 query.AddFilter(DownloadQuery::FILTER_QUERY, *search_terms_);
391 query.Search(all_items.begin(), all_items.end(), &filtered_items);
392 if (filtered_items.empty())
393 return;
396 DCHECK(manager);
397 scoped_ptr<base::DictionaryValue> item(CreateDownloadItemValue(
398 download_item,
399 original_notifier_ && manager == GetMainNotifierManager()));
400 CallUpdateItem(*item);
403 void DownloadsDOMHandler::OnDownloadRemoved(
404 content::DownloadManager* manager,
405 content::DownloadItem* download_item) {
406 if (!DownloadItemModel(download_item).ShouldShowInShelf())
407 return;
409 // This relies on |download_item| being removed from DownloadManager in this
410 // MessageLoop iteration. |download_item| may not have been removed from
411 // DownloadManager when OnDownloadRemoved() is fired, so bounce off the
412 // MessageLoop to give it a chance to be removed. SendCurrentDownloads() looks
413 // at all downloads, and we do not tell it that |download_item| is being
414 // removed. If DownloadManager is ever changed to not immediately remove
415 // |download_item| from its map when OnDownloadRemoved is sent, then
416 // DownloadsDOMHandler::OnDownloadRemoved() will need to explicitly tell
417 // SendCurrentDownloads() that |download_item| was removed. A
418 // SupportsUserData::Data would be the correct way to do this.
419 ScheduleSendCurrentDownloads();
422 void DownloadsDOMHandler::HandleGetDownloads(const base::ListValue* args) {
423 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_GET_DOWNLOADS);
424 search_terms_.reset(args && !args->empty() ? args->DeepCopy() : NULL);
425 ScheduleSendCurrentDownloads();
427 if (!main_notifier_) {
428 main_notifier_.reset(new AllDownloadItemNotifier(download_manager_, this));
430 Profile* profile = Profile::FromBrowserContext(
431 download_manager_->GetBrowserContext());
432 if (profile->IsOffTheRecord()) {
433 original_notifier_.reset(new AllDownloadItemNotifier(
434 BrowserContext::GetDownloadManager(profile->GetOriginalProfile()),
435 this));
440 void DownloadsDOMHandler::HandleOpenFile(const base::ListValue* args) {
441 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FILE);
442 content::DownloadItem* file = GetDownloadByValue(args);
443 if (file)
444 file->OpenDownload();
447 void DownloadsDOMHandler::HandleDrag(const base::ListValue* args) {
448 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DRAG);
449 content::DownloadItem* file = GetDownloadByValue(args);
450 if (!file)
451 return;
453 content::WebContents* web_contents = GetWebUIWebContents();
454 // |web_contents| is only NULL in the test.
455 if (!web_contents)
456 return;
458 if (file->GetState() != content::DownloadItem::COMPLETE)
459 return;
461 gfx::Image* icon = g_browser_process->icon_manager()->LookupIconFromFilepath(
462 file->GetTargetFilePath(), IconLoader::NORMAL);
463 gfx::NativeView view = web_contents->GetNativeView();
465 // Enable nested tasks during DnD, while |DragDownload()| blocks.
466 base::MessageLoop::ScopedNestableTaskAllower allow(
467 base::MessageLoop::current());
468 DragDownloadItem(file, icon, view);
472 void DownloadsDOMHandler::HandleSaveDangerous(const base::ListValue* args) {
473 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
474 content::DownloadItem* file = GetDownloadByValue(args);
475 if (file)
476 ShowDangerPrompt(file);
479 void DownloadsDOMHandler::HandleDiscardDangerous(const base::ListValue* args) {
480 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS);
481 content::DownloadItem* file = GetDownloadByValue(args);
482 if (file)
483 file->Remove();
486 void DownloadsDOMHandler::HandleShow(const base::ListValue* args) {
487 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SHOW);
488 content::DownloadItem* file = GetDownloadByValue(args);
489 if (file)
490 file->ShowDownloadInShell();
493 void DownloadsDOMHandler::HandlePause(const base::ListValue* args) {
494 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_PAUSE);
495 content::DownloadItem* file = GetDownloadByValue(args);
496 if (file)
497 file->Pause();
500 void DownloadsDOMHandler::HandleResume(const base::ListValue* args) {
501 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_RESUME);
502 content::DownloadItem* file = GetDownloadByValue(args);
503 if (file)
504 file->Resume();
507 void DownloadsDOMHandler::HandleRemove(const base::ListValue* args) {
508 if (!IsDeletingHistoryAllowed())
509 return;
511 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_REMOVE);
512 content::DownloadItem* file = GetDownloadByValue(args);
513 if (!file)
514 return;
516 std::vector<content::DownloadItem*> downloads;
517 downloads.push_back(file);
518 RemoveDownloads(downloads);
521 void DownloadsDOMHandler::HandleUndo(const base::ListValue* args) {
522 // TODO(dbeam): handle more than removed downloads someday?
523 if (removals_.empty())
524 return;
526 const std::set<uint32> last_removed_ids = removals_.back();
527 removals_.pop_back();
529 for (auto id : last_removed_ids) {
530 content::DownloadItem* download = GetDownloadById(id);
531 if (!download)
532 continue;
534 DownloadItemModel model(download);
535 model.SetShouldShowInShelf(true);
536 model.SetIsBeingRevived(true);
538 download->UpdateObservers();
540 model.SetIsBeingRevived(false);
544 void DownloadsDOMHandler::HandleCancel(const base::ListValue* args) {
545 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CANCEL);
546 content::DownloadItem* file = GetDownloadByValue(args);
547 if (file)
548 file->Cancel(true);
551 void DownloadsDOMHandler::HandleClearAll(const base::ListValue* args) {
552 if (!IsDeletingHistoryAllowed()) {
553 // This should only be reached during tests.
554 return;
557 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CLEAR_ALL);
559 std::vector<content::DownloadItem*> downloads;
560 if (GetMainNotifierManager())
561 GetMainNotifierManager()->GetAllDownloads(&downloads);
562 if (GetOriginalNotifierManager())
563 GetOriginalNotifierManager()->GetAllDownloads(&downloads);
564 RemoveDownloads(downloads);
567 void DownloadsDOMHandler::RemoveDownloads(
568 const std::vector<content::DownloadItem*>& to_remove) {
569 std::set<uint32> ids;
571 for (auto* download : to_remove) {
572 DownloadItemModel item_model(download);
573 if (!item_model.ShouldShowInShelf() ||
574 download->GetState() == content::DownloadItem::IN_PROGRESS) {
575 continue;
578 item_model.SetShouldShowInShelf(false);
579 ids.insert(download->GetId());
580 download->UpdateObservers();
583 if (!ids.empty())
584 removals_.push_back(ids);
587 void DownloadsDOMHandler::HandleOpenDownloadsFolder(
588 const base::ListValue* args) {
589 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FOLDER);
590 content::DownloadManager* manager = GetMainNotifierManager();
591 if (manager) {
592 platform_util::OpenItem(
593 Profile::FromBrowserContext(manager->GetBrowserContext()),
594 DownloadPrefs::FromDownloadManager(manager)->DownloadPath(),
595 platform_util::OPEN_FOLDER, platform_util::OpenOperationCallback());
599 // DownloadsDOMHandler, private: ----------------------------------------------
601 void DownloadsDOMHandler::ScheduleSendCurrentDownloads() {
602 // Don't call SendCurrentDownloads() every time anything changes. Batch them
603 // together instead. This may handle hundreds of OnDownloadDestroyed() calls
604 // in a single UI message loop iteration when the user Clears All downloads.
605 if (update_scheduled_)
606 return;
608 update_scheduled_ = true;
610 BrowserThread::PostTask(
611 BrowserThread::UI, FROM_HERE,
612 base::Bind(&DownloadsDOMHandler::SendCurrentDownloads,
613 weak_ptr_factory_.GetWeakPtr()));
616 content::DownloadManager* DownloadsDOMHandler::GetMainNotifierManager() const {
617 return main_notifier_ ? main_notifier_->GetManager() : nullptr;
620 content::DownloadManager* DownloadsDOMHandler::GetOriginalNotifierManager()
621 const {
622 return original_notifier_ ? original_notifier_->GetManager() : nullptr;
625 void DownloadsDOMHandler::FinalizeRemovals() {
626 while (!removals_.empty()) {
627 const std::set<uint32> remove = removals_.back();
628 removals_.pop_back();
630 for (const auto id : remove) {
631 content::DownloadItem* download = GetDownloadById(id);
632 if (download)
633 download->Remove();
638 void DownloadsDOMHandler::SendCurrentDownloads() {
639 update_scheduled_ = false;
641 content::DownloadManager::DownloadVector all_items, filtered_items;
642 if (GetMainNotifierManager()) {
643 GetMainNotifierManager()->GetAllDownloads(&all_items);
644 GetMainNotifierManager()->CheckForHistoryFilesRemoval();
646 if (GetOriginalNotifierManager()) {
647 GetOriginalNotifierManager()->GetAllDownloads(&all_items);
648 GetOriginalNotifierManager()->CheckForHistoryFilesRemoval();
651 DownloadQuery query;
652 if (search_terms_ && !search_terms_->empty())
653 query.AddFilter(DownloadQuery::FILTER_QUERY, *search_terms_);
654 query.AddFilter(base::Bind(&IsDownloadDisplayable));
655 query.AddSorter(DownloadQuery::SORT_START_TIME, DownloadQuery::DESCENDING);
656 query.Limit(MaxNumberOfDownloads());
657 query.Search(all_items.begin(), all_items.end(), &filtered_items);
659 base::ListValue results_value;
660 for (auto* item : filtered_items) {
661 results_value.Append(CreateDownloadItemValue(
662 item,
663 original_notifier_ && GetMainNotifierManager() &&
664 GetMainNotifierManager()->GetDownload(item->GetId()) == item));
666 CallUpdateAll(results_value);
669 void DownloadsDOMHandler::ShowDangerPrompt(
670 content::DownloadItem* dangerous_item) {
671 DownloadDangerPrompt* danger_prompt = DownloadDangerPrompt::Create(
672 dangerous_item,
673 GetWebUIWebContents(),
674 false,
675 base::Bind(&DownloadsDOMHandler::DangerPromptDone,
676 weak_ptr_factory_.GetWeakPtr(), dangerous_item->GetId()));
677 // danger_prompt will delete itself.
678 DCHECK(danger_prompt);
681 void DownloadsDOMHandler::DangerPromptDone(
682 int download_id, DownloadDangerPrompt::Action action) {
683 if (action != DownloadDangerPrompt::ACCEPT)
684 return;
685 content::DownloadItem* item = NULL;
686 if (GetMainNotifierManager())
687 item = GetMainNotifierManager()->GetDownload(download_id);
688 if (!item && GetOriginalNotifierManager())
689 item = GetOriginalNotifierManager()->GetDownload(download_id);
690 if (!item || item->IsDone())
691 return;
692 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
693 item->ValidateDangerousDownload();
696 bool DownloadsDOMHandler::IsDeletingHistoryAllowed() {
697 content::DownloadManager* manager = GetMainNotifierManager();
698 return manager &&
699 Profile::FromBrowserContext(manager->GetBrowserContext())->
700 GetPrefs()->GetBoolean(prefs::kAllowDeletingBrowserHistory);
703 content::DownloadItem* DownloadsDOMHandler::GetDownloadByValue(
704 const base::ListValue* args) {
705 std::string download_id;
706 if (!args->GetString(0, &download_id)) {
707 NOTREACHED();
708 return nullptr;
711 uint64 id;
712 if (!base::StringToUint64(download_id, &id)) {
713 NOTREACHED();
714 return nullptr;
717 return GetDownloadById(static_cast<uint32>(id));
720 content::DownloadItem* DownloadsDOMHandler::GetDownloadById(uint32 id) {
721 content::DownloadItem* item = NULL;
722 if (GetMainNotifierManager())
723 item = GetMainNotifierManager()->GetDownload(id);
724 if (!item && GetOriginalNotifierManager())
725 item = GetOriginalNotifierManager()->GetDownload(id);
726 return item;
729 content::WebContents* DownloadsDOMHandler::GetWebUIWebContents() {
730 return web_ui()->GetWebContents();
733 void DownloadsDOMHandler::CallUpdateAll(const base::ListValue& list) {
734 web_ui()->CallJavascriptFunction("downloads.Manager.updateAll", list);
737 void DownloadsDOMHandler::CallUpdateItem(const base::DictionaryValue& item) {
738 web_ui()->CallJavascriptFunction("downloads.Manager.updateItem", item);