Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / ui / webui / downloads_dom_handler.cc
blob38489289f31bdc2b908158e246d8f081d3b52430
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 static const size_t kMaxDownloads = 150;
68 enum DownloadsDOMEvent {
69 DOWNLOADS_DOM_EVENT_GET_DOWNLOADS = 0,
70 DOWNLOADS_DOM_EVENT_OPEN_FILE = 1,
71 DOWNLOADS_DOM_EVENT_DRAG = 2,
72 DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS = 3,
73 DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS = 4,
74 DOWNLOADS_DOM_EVENT_SHOW = 5,
75 DOWNLOADS_DOM_EVENT_PAUSE = 6,
76 DOWNLOADS_DOM_EVENT_REMOVE = 7,
77 DOWNLOADS_DOM_EVENT_CANCEL = 8,
78 DOWNLOADS_DOM_EVENT_CLEAR_ALL = 9,
79 DOWNLOADS_DOM_EVENT_OPEN_FOLDER = 10,
80 DOWNLOADS_DOM_EVENT_RESUME = 11,
81 DOWNLOADS_DOM_EVENT_MAX
84 void CountDownloadsDOMEvents(DownloadsDOMEvent event) {
85 UMA_HISTOGRAM_ENUMERATION("Download.DOMEvent",
86 event,
87 DOWNLOADS_DOM_EVENT_MAX);
90 // Returns a string constant to be used as the |danger_type| value in
91 // CreateDownloadItemValue(). Only return strings for DANGEROUS_FILE,
92 // DANGEROUS_URL, DANGEROUS_CONTENT, and UNCOMMON_CONTENT because the
93 // |danger_type| value is only defined if the value of |state| is |DANGEROUS|.
94 const char* GetDangerTypeString(content::DownloadDangerType danger_type) {
95 switch (danger_type) {
96 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
97 return "DANGEROUS_FILE";
98 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
99 return "DANGEROUS_URL";
100 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
101 return "DANGEROUS_CONTENT";
102 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
103 return "UNCOMMON_CONTENT";
104 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
105 return "DANGEROUS_HOST";
106 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
107 return "POTENTIALLY_UNWANTED";
108 default:
109 // Don't return a danger type string if it is NOT_DANGEROUS or
110 // MAYBE_DANGEROUS_CONTENT.
111 NOTREACHED();
112 return "";
116 // TODO(dbeam): if useful elsewhere, move to base/i18n/time_formatting.h?
117 base::string16 TimeFormatLongDate(const base::Time& time) {
118 scoped_ptr<icu::DateFormat> formatter(
119 icu::DateFormat::createDateInstance(icu::DateFormat::kLong));
120 icu::UnicodeString date_string;
121 formatter->format(static_cast<UDate>(time.ToDoubleT() * 1000), date_string);
122 return base::string16(date_string.getBuffer(),
123 static_cast<size_t>(date_string.length()));
126 // Returns a JSON dictionary containing some of the attributes of |download|.
127 // The JSON dictionary will also have a field "id" set to |id|, and a field
128 // "otr" set to |incognito|.
129 base::DictionaryValue* CreateDownloadItemValue(
130 content::DownloadItem* download_item,
131 bool incognito) {
132 // TODO(asanka): Move towards using download_model here for getting status and
133 // progress. The difference currently only matters to Drive downloads and
134 // those don't show up on the downloads page, but should.
135 DownloadItemModel download_model(download_item);
137 // The items which are to be written into file_value are also described in
138 // chrome/browser/resources/downloads/downloads.js in @typedef for
139 // BackendDownloadObject. Please update it whenever you add or remove
140 // any keys in file_value.
141 base::DictionaryValue* file_value = new base::DictionaryValue();
143 file_value->SetInteger(
144 "started", static_cast<int>(download_item->GetStartTime().ToTimeT()));
145 file_value->SetString(
146 "since_string", ui::TimeFormat::RelativeDate(
147 download_item->GetStartTime(), NULL));
149 base::Time start_time = download_item->GetStartTime();
150 base::string16 date_string = MdDownloadsEnabled() ?
151 TimeFormatLongDate(start_time) : base::TimeFormatShortDate(start_time);
152 file_value->SetString("date_string", date_string);
154 file_value->SetString("id", base::Uint64ToString(download_item->GetId()));
156 base::FilePath download_path(download_item->GetTargetFilePath());
157 file_value->Set("file_path", base::CreateFilePathValue(download_path));
158 file_value->SetString("file_url",
159 net::FilePathToFileURL(download_path).spec());
161 extensions::DownloadedByExtension* by_ext =
162 extensions::DownloadedByExtension::Get(download_item);
163 if (by_ext) {
164 file_value->SetString("by_ext_id", by_ext->id());
165 file_value->SetString("by_ext_name", by_ext->name());
166 // Lookup the extension's current name() in case the user changed their
167 // language. This won't work if the extension was uninstalled, so the name
168 // might be the wrong language.
169 bool include_disabled = true;
170 const extensions::Extension* extension = extensions::ExtensionSystem::Get(
171 Profile::FromBrowserContext(download_item->GetBrowserContext()))->
172 extension_service()->GetExtensionById(by_ext->id(), include_disabled);
173 if (extension)
174 file_value->SetString("by_ext_name", extension->name());
177 // Keep file names as LTR.
178 base::string16 file_name =
179 download_item->GetFileNameToReportUser().LossyDisplayName();
180 file_name = base::i18n::GetDisplayStringInLTRDirectionality(file_name);
181 file_value->SetString("file_name", file_name);
182 file_value->SetString("url", download_item->GetURL().spec());
183 file_value->SetBoolean("otr", incognito);
184 file_value->SetInteger("total", static_cast<int>(
185 download_item->GetTotalBytes()));
186 file_value->SetBoolean("file_externally_removed",
187 download_item->GetFileExternallyRemoved());
188 file_value->SetBoolean("retry", false); // Overridden below if needed.
189 file_value->SetBoolean("resume", download_item->CanResume());
191 switch (download_item->GetState()) {
192 case content::DownloadItem::IN_PROGRESS: {
193 if (download_item->IsDangerous()) {
194 file_value->SetString("state", "DANGEROUS");
195 // These are the only danger states that the UI is equipped to handle.
196 DCHECK(download_item->GetDangerType() ==
197 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE ||
198 download_item->GetDangerType() ==
199 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL ||
200 download_item->GetDangerType() ==
201 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT ||
202 download_item->GetDangerType() ==
203 content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT ||
204 download_item->GetDangerType() ==
205 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST ||
206 download_item->GetDangerType() ==
207 content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED);
208 const char* danger_type_value =
209 GetDangerTypeString(download_item->GetDangerType());
210 file_value->SetString("danger_type", danger_type_value);
211 } else if (download_item->IsPaused()) {
212 file_value->SetString("state", "PAUSED");
213 } else {
214 file_value->SetString("state", "IN_PROGRESS");
216 file_value->SetString("progress_status_text",
217 download_model.GetTabProgressStatusText());
219 int percent = download_item->PercentComplete();
220 if (!MdDownloadsEnabled())
221 percent = std::max(0, percent);
222 file_value->SetInteger("percent", percent);
224 file_value->SetInteger("received",
225 static_cast<int>(download_item->GetReceivedBytes()));
226 break;
229 case content::DownloadItem::INTERRUPTED:
230 file_value->SetString("state", "INTERRUPTED");
232 file_value->SetString("progress_status_text",
233 download_model.GetTabProgressStatusText());
235 if (download_item->CanResume()) {
236 file_value->SetInteger("percent",
237 static_cast<int>(download_item->PercentComplete()));
239 file_value->SetInteger("received",
240 static_cast<int>(download_item->GetReceivedBytes()));
241 file_value->SetString("last_reason_text",
242 download_model.GetInterruptReasonText());
243 if (content::DOWNLOAD_INTERRUPT_REASON_CRASH ==
244 download_item->GetLastReason() && !download_item->CanResume())
245 file_value->SetBoolean("retry", true);
246 break;
248 case content::DownloadItem::CANCELLED:
249 file_value->SetString("state", "CANCELLED");
250 file_value->SetBoolean("retry", true);
251 break;
253 case content::DownloadItem::COMPLETE:
254 DCHECK(!download_item->IsDangerous());
255 file_value->SetString("state", "COMPLETE");
256 break;
258 case content::DownloadItem::MAX_DOWNLOAD_STATE:
259 NOTREACHED();
262 return file_value;
265 // Filters out extension downloads and downloads that don't have a filename yet.
266 bool IsDownloadDisplayable(const content::DownloadItem& item) {
267 return !download_crx_util::IsExtensionDownload(item) &&
268 !item.IsTemporary() &&
269 !item.GetFileNameToReportUser().empty() &&
270 !item.GetTargetFilePath().empty() &&
271 DownloadItemModel(
272 const_cast<content::DownloadItem*>(&item)).ShouldShowInShelf();
275 } // namespace
277 DownloadsDOMHandler::DownloadsDOMHandler(
278 content::DownloadManager* download_manager)
279 : download_manager_(download_manager),
280 update_scheduled_(false),
281 weak_ptr_factory_(this) {
282 // Create our fileicon data source.
283 Profile* profile = Profile::FromBrowserContext(
284 download_manager->GetBrowserContext());
285 content::URLDataSource::Add(profile, new FileIconSource());
288 DownloadsDOMHandler::~DownloadsDOMHandler() {
289 FinalizeRemovals();
292 // DownloadsDOMHandler, public: -----------------------------------------------
294 void DownloadsDOMHandler::RegisterMessages() {
295 web_ui()->RegisterMessageCallback("getDownloads",
296 base::Bind(&DownloadsDOMHandler::HandleGetDownloads,
297 weak_ptr_factory_.GetWeakPtr()));
298 web_ui()->RegisterMessageCallback("openFile",
299 base::Bind(&DownloadsDOMHandler::HandleOpenFile,
300 weak_ptr_factory_.GetWeakPtr()));
301 web_ui()->RegisterMessageCallback("drag",
302 base::Bind(&DownloadsDOMHandler::HandleDrag,
303 weak_ptr_factory_.GetWeakPtr()));
304 web_ui()->RegisterMessageCallback("saveDangerous",
305 base::Bind(&DownloadsDOMHandler::HandleSaveDangerous,
306 weak_ptr_factory_.GetWeakPtr()));
307 web_ui()->RegisterMessageCallback("discardDangerous",
308 base::Bind(&DownloadsDOMHandler::HandleDiscardDangerous,
309 weak_ptr_factory_.GetWeakPtr()));
310 web_ui()->RegisterMessageCallback("show",
311 base::Bind(&DownloadsDOMHandler::HandleShow,
312 weak_ptr_factory_.GetWeakPtr()));
313 web_ui()->RegisterMessageCallback("pause",
314 base::Bind(&DownloadsDOMHandler::HandlePause,
315 weak_ptr_factory_.GetWeakPtr()));
316 web_ui()->RegisterMessageCallback("resume",
317 base::Bind(&DownloadsDOMHandler::HandleResume,
318 weak_ptr_factory_.GetWeakPtr()));
319 web_ui()->RegisterMessageCallback("remove",
320 base::Bind(&DownloadsDOMHandler::HandleRemove,
321 weak_ptr_factory_.GetWeakPtr()));
322 web_ui()->RegisterMessageCallback("undo",
323 base::Bind(&DownloadsDOMHandler::HandleUndo,
324 weak_ptr_factory_.GetWeakPtr()));
325 web_ui()->RegisterMessageCallback("cancel",
326 base::Bind(&DownloadsDOMHandler::HandleCancel,
327 weak_ptr_factory_.GetWeakPtr()));
328 web_ui()->RegisterMessageCallback("clearAll",
329 base::Bind(&DownloadsDOMHandler::HandleClearAll,
330 weak_ptr_factory_.GetWeakPtr()));
331 web_ui()->RegisterMessageCallback("openDownloadsFolder",
332 base::Bind(&DownloadsDOMHandler::HandleOpenDownloadsFolder,
333 weak_ptr_factory_.GetWeakPtr()));
336 void DownloadsDOMHandler::OnDownloadCreated(
337 content::DownloadManager* manager, content::DownloadItem* download_item) {
338 if (IsDownloadDisplayable(*download_item))
339 ScheduleSendCurrentDownloads();
340 else
341 new_downloads_.insert(download_item->GetId());
344 void DownloadsDOMHandler::OnDownloadUpdated(
345 content::DownloadManager* manager,
346 content::DownloadItem* download_item) {
347 if (update_scheduled_)
348 return;
350 bool showing_new_item = false;
352 if (new_downloads_.count(download_item->GetId())) {
353 // A new download (that the page doesn't know about yet) has been updated.
354 if (!IsDownloadDisplayable(*download_item)) {
355 // Item isn't ready to be displayed yet. Wait until it is.
356 return;
359 new_downloads_.erase(download_item->GetId());
360 showing_new_item = true;
363 if (showing_new_item || DownloadItemModel(download_item).IsBeingRevived() ||
364 !IsDownloadDisplayable(*download_item)) {
365 // A download will be shown or hidden by this update. Resend the list.
366 ScheduleSendCurrentDownloads();
367 return;
370 if (search_terms_ && !search_terms_->empty()) {
371 // Don't CallUpdateItem() if download_item doesn't match
372 // search_terms_.
373 // TODO(benjhayden): Consider splitting MatchesQuery() out to a function.
374 content::DownloadManager::DownloadVector all_items, filtered_items;
375 all_items.push_back(download_item);
376 DownloadQuery query;
377 query.AddFilter(DownloadQuery::FILTER_QUERY, *search_terms_);
378 query.Search(all_items.begin(), all_items.end(), &filtered_items);
379 if (filtered_items.empty())
380 return;
383 DCHECK(manager);
384 scoped_ptr<base::DictionaryValue> item(CreateDownloadItemValue(
385 download_item,
386 original_notifier_ && manager == GetMainNotifierManager()));
387 CallUpdateItem(*item);
390 void DownloadsDOMHandler::OnDownloadRemoved(
391 content::DownloadManager* manager,
392 content::DownloadItem* download_item) {
393 if (!DownloadItemModel(download_item).ShouldShowInShelf())
394 return;
396 // This relies on |download_item| being removed from DownloadManager in this
397 // MessageLoop iteration. |download_item| may not have been removed from
398 // DownloadManager when OnDownloadRemoved() is fired, so bounce off the
399 // MessageLoop to give it a chance to be removed. SendCurrentDownloads() looks
400 // at all downloads, and we do not tell it that |download_item| is being
401 // removed. If DownloadManager is ever changed to not immediately remove
402 // |download_item| from its map when OnDownloadRemoved is sent, then
403 // DownloadsDOMHandler::OnDownloadRemoved() will need to explicitly tell
404 // SendCurrentDownloads() that |download_item| was removed. A
405 // SupportsUserData::Data would be the correct way to do this.
406 ScheduleSendCurrentDownloads();
409 void DownloadsDOMHandler::HandleGetDownloads(const base::ListValue* args) {
410 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_GET_DOWNLOADS);
411 search_terms_.reset(args && !args->empty() ? args->DeepCopy() : NULL);
412 ScheduleSendCurrentDownloads();
414 if (!main_notifier_) {
415 main_notifier_.reset(new AllDownloadItemNotifier(download_manager_, this));
417 Profile* profile = Profile::FromBrowserContext(
418 download_manager_->GetBrowserContext());
419 if (profile->IsOffTheRecord()) {
420 original_notifier_.reset(new AllDownloadItemNotifier(
421 BrowserContext::GetDownloadManager(profile->GetOriginalProfile()),
422 this));
427 void DownloadsDOMHandler::HandleOpenFile(const base::ListValue* args) {
428 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FILE);
429 content::DownloadItem* file = GetDownloadByValue(args);
430 if (file)
431 file->OpenDownload();
434 void DownloadsDOMHandler::HandleDrag(const base::ListValue* args) {
435 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DRAG);
436 content::DownloadItem* file = GetDownloadByValue(args);
437 if (!file)
438 return;
440 content::WebContents* web_contents = GetWebUIWebContents();
441 // |web_contents| is only NULL in the test.
442 if (!web_contents)
443 return;
445 if (file->GetState() != content::DownloadItem::COMPLETE)
446 return;
448 gfx::Image* icon = g_browser_process->icon_manager()->LookupIconFromFilepath(
449 file->GetTargetFilePath(), IconLoader::NORMAL);
450 gfx::NativeView view = web_contents->GetNativeView();
452 // Enable nested tasks during DnD, while |DragDownload()| blocks.
453 base::MessageLoop::ScopedNestableTaskAllower allow(
454 base::MessageLoop::current());
455 DragDownloadItem(file, icon, view);
459 void DownloadsDOMHandler::HandleSaveDangerous(const base::ListValue* args) {
460 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
461 content::DownloadItem* file = GetDownloadByValue(args);
462 if (file)
463 ShowDangerPrompt(file);
466 void DownloadsDOMHandler::HandleDiscardDangerous(const base::ListValue* args) {
467 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS);
468 content::DownloadItem* file = GetDownloadByValue(args);
469 if (file)
470 file->Remove();
473 void DownloadsDOMHandler::HandleShow(const base::ListValue* args) {
474 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SHOW);
475 content::DownloadItem* file = GetDownloadByValue(args);
476 if (file)
477 file->ShowDownloadInShell();
480 void DownloadsDOMHandler::HandlePause(const base::ListValue* args) {
481 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_PAUSE);
482 content::DownloadItem* file = GetDownloadByValue(args);
483 if (file)
484 file->Pause();
487 void DownloadsDOMHandler::HandleResume(const base::ListValue* args) {
488 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_RESUME);
489 content::DownloadItem* file = GetDownloadByValue(args);
490 if (file)
491 file->Resume();
494 void DownloadsDOMHandler::HandleRemove(const base::ListValue* args) {
495 if (!IsDeletingHistoryAllowed())
496 return;
498 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_REMOVE);
499 content::DownloadItem* file = GetDownloadByValue(args);
500 if (!file)
501 return;
503 std::vector<content::DownloadItem*> downloads;
504 downloads.push_back(file);
505 RemoveDownloads(downloads);
508 void DownloadsDOMHandler::HandleUndo(const base::ListValue* args) {
509 // TODO(dbeam): handle more than removed downloads someday?
510 if (removals_.empty())
511 return;
513 const std::set<uint32> last_removed_ids = removals_.back();
514 removals_.pop_back();
516 for (auto id : last_removed_ids) {
517 content::DownloadItem* download = GetDownloadById(id);
518 if (!download)
519 continue;
521 DownloadItemModel model(download);
522 model.SetShouldShowInShelf(true);
523 model.SetIsBeingRevived(true);
525 download->UpdateObservers();
527 model.SetIsBeingRevived(false);
531 void DownloadsDOMHandler::HandleCancel(const base::ListValue* args) {
532 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CANCEL);
533 content::DownloadItem* file = GetDownloadByValue(args);
534 if (file)
535 file->Cancel(true);
538 void DownloadsDOMHandler::HandleClearAll(const base::ListValue* args) {
539 if (!IsDeletingHistoryAllowed()) {
540 // This should only be reached during tests.
541 return;
544 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CLEAR_ALL);
546 std::vector<content::DownloadItem*> downloads;
547 if (GetMainNotifierManager())
548 GetMainNotifierManager()->GetAllDownloads(&downloads);
549 if (GetOriginalNotifierManager())
550 GetOriginalNotifierManager()->GetAllDownloads(&downloads);
551 RemoveDownloads(downloads);
554 void DownloadsDOMHandler::RemoveDownloads(
555 const std::vector<content::DownloadItem*>& to_remove) {
556 std::set<uint32> ids;
558 for (auto* download : to_remove) {
559 DownloadItemModel item_model(download);
560 if (!item_model.ShouldShowInShelf() ||
561 download->GetState() == content::DownloadItem::IN_PROGRESS) {
562 continue;
565 item_model.SetShouldShowInShelf(false);
566 ids.insert(download->GetId());
567 download->UpdateObservers();
570 if (!ids.empty())
571 removals_.push_back(ids);
574 void DownloadsDOMHandler::HandleOpenDownloadsFolder(
575 const base::ListValue* args) {
576 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FOLDER);
577 content::DownloadManager* manager = GetMainNotifierManager();
578 if (manager) {
579 platform_util::OpenItem(
580 Profile::FromBrowserContext(manager->GetBrowserContext()),
581 DownloadPrefs::FromDownloadManager(manager)->DownloadPath(),
582 platform_util::OPEN_FOLDER, platform_util::OpenOperationCallback());
586 // DownloadsDOMHandler, private: ----------------------------------------------
588 void DownloadsDOMHandler::ScheduleSendCurrentDownloads() {
589 // Don't call SendCurrentDownloads() every time anything changes. Batch them
590 // together instead. This may handle hundreds of OnDownloadDestroyed() calls
591 // in a single UI message loop iteration when the user Clears All downloads.
592 if (update_scheduled_)
593 return;
595 update_scheduled_ = true;
597 BrowserThread::PostTask(
598 BrowserThread::UI, FROM_HERE,
599 base::Bind(&DownloadsDOMHandler::SendCurrentDownloads,
600 weak_ptr_factory_.GetWeakPtr()));
603 content::DownloadManager* DownloadsDOMHandler::GetMainNotifierManager() const {
604 return main_notifier_ ? main_notifier_->GetManager() : nullptr;
607 content::DownloadManager* DownloadsDOMHandler::GetOriginalNotifierManager()
608 const {
609 return original_notifier_ ? original_notifier_->GetManager() : nullptr;
612 void DownloadsDOMHandler::FinalizeRemovals() {
613 while (!removals_.empty()) {
614 const std::set<uint32> remove = removals_.back();
615 removals_.pop_back();
617 for (const auto id : remove) {
618 content::DownloadItem* download = GetDownloadById(id);
619 if (download)
620 download->Remove();
625 void DownloadsDOMHandler::SendCurrentDownloads() {
626 update_scheduled_ = false;
628 content::DownloadManager::DownloadVector all_items, filtered_items;
629 if (GetMainNotifierManager()) {
630 GetMainNotifierManager()->GetAllDownloads(&all_items);
631 GetMainNotifierManager()->CheckForHistoryFilesRemoval();
633 if (GetOriginalNotifierManager()) {
634 GetOriginalNotifierManager()->GetAllDownloads(&all_items);
635 GetOriginalNotifierManager()->CheckForHistoryFilesRemoval();
638 DownloadQuery query;
639 if (search_terms_ && !search_terms_->empty())
640 query.AddFilter(DownloadQuery::FILTER_QUERY, *search_terms_);
641 query.AddFilter(base::Bind(&IsDownloadDisplayable));
642 query.AddSorter(DownloadQuery::SORT_START_TIME, DownloadQuery::DESCENDING);
643 query.Limit(kMaxDownloads);
644 query.Search(all_items.begin(), all_items.end(), &filtered_items);
646 base::ListValue results_value;
647 for (auto* item : filtered_items) {
648 results_value.Append(CreateDownloadItemValue(
649 item,
650 original_notifier_ && GetMainNotifierManager() &&
651 GetMainNotifierManager()->GetDownload(item->GetId()) == item));
653 CallUpdateAll(results_value);
656 void DownloadsDOMHandler::ShowDangerPrompt(
657 content::DownloadItem* dangerous_item) {
658 DownloadDangerPrompt* danger_prompt = DownloadDangerPrompt::Create(
659 dangerous_item,
660 GetWebUIWebContents(),
661 false,
662 base::Bind(&DownloadsDOMHandler::DangerPromptDone,
663 weak_ptr_factory_.GetWeakPtr(), dangerous_item->GetId()));
664 // danger_prompt will delete itself.
665 DCHECK(danger_prompt);
668 void DownloadsDOMHandler::DangerPromptDone(
669 int download_id, DownloadDangerPrompt::Action action) {
670 if (action != DownloadDangerPrompt::ACCEPT)
671 return;
672 content::DownloadItem* item = NULL;
673 if (GetMainNotifierManager())
674 item = GetMainNotifierManager()->GetDownload(download_id);
675 if (!item && GetOriginalNotifierManager())
676 item = GetOriginalNotifierManager()->GetDownload(download_id);
677 if (!item || item->IsDone())
678 return;
679 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
680 item->ValidateDangerousDownload();
683 bool DownloadsDOMHandler::IsDeletingHistoryAllowed() {
684 content::DownloadManager* manager = GetMainNotifierManager();
685 return manager &&
686 Profile::FromBrowserContext(manager->GetBrowserContext())->
687 GetPrefs()->GetBoolean(prefs::kAllowDeletingBrowserHistory);
690 content::DownloadItem* DownloadsDOMHandler::GetDownloadByValue(
691 const base::ListValue* args) {
692 std::string download_id;
693 if (!args->GetString(0, &download_id)) {
694 NOTREACHED();
695 return nullptr;
698 uint64 id;
699 if (!base::StringToUint64(download_id, &id)) {
700 NOTREACHED();
701 return nullptr;
704 return GetDownloadById(static_cast<uint32>(id));
707 content::DownloadItem* DownloadsDOMHandler::GetDownloadById(uint32 id) {
708 content::DownloadItem* item = NULL;
709 if (GetMainNotifierManager())
710 item = GetMainNotifierManager()->GetDownload(id);
711 if (!item && GetOriginalNotifierManager())
712 item = GetOriginalNotifierManager()->GetDownload(id);
713 return item;
716 content::WebContents* DownloadsDOMHandler::GetWebUIWebContents() {
717 return web_ui()->GetWebContents();
720 void DownloadsDOMHandler::CallUpdateAll(const base::ListValue& list) {
721 web_ui()->CallJavascriptFunction("downloads.Manager.updateAll", list);
724 void DownloadsDOMHandler::CallUpdateItem(const base::DictionaryValue& item) {
725 web_ui()->CallJavascriptFunction("downloads.Manager.updateItem", item);