Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(45)

Side by Side Diff: chrome/browser/ui/webui/downloads_dom_handler.cc

Issue 722953002: downloads: add the ability to undo download removal. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: comment Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/webui/downloads_dom_handler.h" 5 #include "chrome/browser/ui/webui/downloads_dom_handler.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <functional> 8 #include <functional>
9 9
10 #include "base/basictypes.h" 10 #include "base/basictypes.h"
11 #include "base/bind.h" 11 #include "base/bind.h"
12 #include "base/bind_helpers.h" 12 #include "base/bind_helpers.h"
13 #include "base/i18n/rtl.h" 13 #include "base/i18n/rtl.h"
14 #include "base/i18n/time_formatting.h" 14 #include "base/i18n/time_formatting.h"
15 #include "base/memory/singleton.h" 15 #include "base/memory/singleton.h"
16 #include "base/metrics/field_trial.h" 16 #include "base/metrics/field_trial.h"
17 #include "base/metrics/histogram.h" 17 #include "base/metrics/histogram.h"
18 #include "base/prefs/pref_service.h" 18 #include "base/prefs/pref_service.h"
19 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_piece.h" 20 #include "base/strings/string_piece.h"
20 #include "base/strings/utf_string_conversions.h" 21 #include "base/strings/utf_string_conversions.h"
21 #include "base/threading/thread.h" 22 #include "base/threading/thread.h"
22 #include "base/value_conversions.h" 23 #include "base/value_conversions.h"
23 #include "base/values.h" 24 #include "base/values.h"
24 #include "chrome/browser/browser_process.h" 25 #include "chrome/browser/browser_process.h"
25 #include "chrome/browser/download/download_crx_util.h" 26 #include "chrome/browser/download/download_crx_util.h"
26 #include "chrome/browser/download/download_danger_prompt.h" 27 #include "chrome/browser/download/download_danger_prompt.h"
27 #include "chrome/browser/download/download_history.h" 28 #include "chrome/browser/download/download_history.h"
28 #include "chrome/browser/download/download_item_model.h" 29 #include "chrome/browser/download/download_item_model.h"
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
124 // any keys in file_value. 125 // any keys in file_value.
125 base::DictionaryValue* file_value = new base::DictionaryValue(); 126 base::DictionaryValue* file_value = new base::DictionaryValue();
126 127
127 file_value->SetInteger( 128 file_value->SetInteger(
128 "started", static_cast<int>(download_item->GetStartTime().ToTimeT())); 129 "started", static_cast<int>(download_item->GetStartTime().ToTimeT()));
129 file_value->SetString( 130 file_value->SetString(
130 "since_string", ui::TimeFormat::RelativeDate( 131 "since_string", ui::TimeFormat::RelativeDate(
131 download_item->GetStartTime(), NULL)); 132 download_item->GetStartTime(), NULL));
132 file_value->SetString( 133 file_value->SetString(
133 "date_string", base::TimeFormatShortDate(download_item->GetStartTime())); 134 "date_string", base::TimeFormatShortDate(download_item->GetStartTime()));
134 file_value->SetInteger("id", download_item->GetId()); 135 file_value->SetString("id", base::Uint64ToString(download_item->GetId()));
arv (Not doing code reviews) 2014/11/13 19:02:30 JS numbers cannot represent Uint64. This might lea
Dan Beam 2014/11/19 23:09:32 the JS only gets a string (now)
135 136
136 base::FilePath download_path(download_item->GetTargetFilePath()); 137 base::FilePath download_path(download_item->GetTargetFilePath());
137 file_value->Set("file_path", base::CreateFilePathValue(download_path)); 138 file_value->Set("file_path", base::CreateFilePathValue(download_path));
138 file_value->SetString("file_url", 139 file_value->SetString("file_url",
139 net::FilePathToFileURL(download_path).spec()); 140 net::FilePathToFileURL(download_path).spec());
140 141
141 extensions::DownloadedByExtension* by_ext = 142 extensions::DownloadedByExtension* by_ext =
142 extensions::DownloadedByExtension::Get(download_item); 143 extensions::DownloadedByExtension::Get(download_item);
143 if (by_ext) { 144 if (by_ext) {
144 file_value->SetString("by_ext_id", by_ext->id()); 145 file_value->SetString("by_ext_id", by_ext->id());
145 file_value->SetString("by_ext_name", by_ext->name()); 146 file_value->SetString("by_ext_name", by_ext->name());
146 // Lookup the extension's current name() in case the user changed their 147 // 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 // language. This won't work if the extension was uninstalled, so the name
148 // might be the wrong language. 149 // might be the wrong language.
149 bool include_disabled = true; 150 bool include_disabled = true;
150 const extensions::Extension* extension = extensions::ExtensionSystem::Get( 151 const extensions::Extension* extension = extensions::ExtensionSystem::Get(
151 Profile::FromBrowserContext(download_item->GetBrowserContext()))-> 152 Profile::FromBrowserContext(download_item->GetBrowserContext()))->
152 extension_service()->GetExtensionById(by_ext->id(), include_disabled); 153 extension_service()->GetExtensionById(by_ext->id(), include_disabled);
153 if (extension) 154 if (extension)
154 file_value->SetString("by_ext_name", extension->name()); 155 file_value->SetString("by_ext_name", extension->name());
155 } 156 }
156 157
157 // Keep file names as LTR. 158 // Keep file names as LTR.
158 base::string16 file_name = 159 base::string16 file_name =
159 download_item->GetFileNameToReportUser().LossyDisplayName(); 160 download_item->GetFileNameToReportUser().LossyDisplayName();
160 file_name = base::i18n::GetDisplayStringInLTRDirectionality(file_name); 161 file_name = base::i18n::GetDisplayStringInLTRDirectionality(file_name);
161 file_value->SetString("file_name", file_name); 162 file_value->SetString("file_name", file_name);
162 file_value->SetString("url", download_item->GetURL().spec()); 163 file_value->SetString("url", download_item->GetURL().spec());
163 file_value->SetBoolean("otr", incognito); 164 file_value->SetBoolean("otr", incognito);
164 file_value->SetInteger("total", static_cast<int>( 165 file_value->SetInteger("total", static_cast<int>(
165 download_item->GetTotalBytes())); 166 download_item->GetTotalBytes()));
166 file_value->SetBoolean("file_externally_removed", 167 file_value->SetBoolean("file_externally_removed",
167 download_item->GetFileExternallyRemoved()); 168 download_item->GetFileExternallyRemoved());
168 file_value->SetBoolean("retry", false); // Overridden below if needed. 169 file_value->SetBoolean("retry", false); // Overridden below if needed.
169 file_value->SetBoolean("resume", download_item->CanResume()); 170 file_value->SetBoolean("resume", download_item->CanResume());
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
222 case content::DownloadItem::CANCELLED: 223 case content::DownloadItem::CANCELLED:
223 file_value->SetString("state", "CANCELLED"); 224 file_value->SetString("state", "CANCELLED");
224 file_value->SetBoolean("retry", true); 225 file_value->SetBoolean("retry", true);
225 break; 226 break;
226 227
227 case content::DownloadItem::COMPLETE: 228 case content::DownloadItem::COMPLETE:
228 DCHECK(!download_item->IsDangerous()); 229 DCHECK(!download_item->IsDangerous());
229 file_value->SetString("state", "COMPLETE"); 230 file_value->SetString("state", "COMPLETE");
230 break; 231 break;
231 232
233 case content::DownloadItem::REMOVED:
benjhayden 2014/11/13 18:01:59 How do you ensure that this isn't reached?
232 case content::DownloadItem::MAX_DOWNLOAD_STATE: 234 case content::DownloadItem::MAX_DOWNLOAD_STATE:
233 NOTREACHED() << "state undefined"; 235 NOTREACHED();
234 } 236 }
235 237
236 return file_value; 238 return file_value;
237 } 239 }
238 240
239 // Filters out extension downloads and downloads that don't have a filename yet. 241 // Filters out extension downloads and downloads that don't have a filename yet.
240 bool IsDownloadDisplayable(const content::DownloadItem& item) { 242 bool IsDownloadDisplayable(const content::DownloadItem& item) {
241 return (!download_crx_util::IsExtensionDownload(item) && 243 return (!download_crx_util::IsExtensionDownload(item) &&
242 !item.IsTemporary() && 244 !item.IsTemporary() &&
245 item.GetState() != content::DownloadItem::REMOVED &&
243 !item.GetFileNameToReportUser().empty() && 246 !item.GetFileNameToReportUser().empty() &&
244 !item.GetTargetFilePath().empty()); 247 !item.GetTargetFilePath().empty());
245 } 248 }
246 249
247 } // namespace 250 } // namespace
248 251
249 DownloadsDOMHandler::DownloadsDOMHandler(content::DownloadManager* dlm) 252 DownloadsDOMHandler::DownloadsDOMHandler(content::DownloadManager* dlm)
250 : main_notifier_(dlm, this), 253 : main_notifier_(dlm, this),
251 update_scheduled_(false), 254 update_scheduled_(false),
252 weak_ptr_factory_(this) { 255 weak_ptr_factory_(this) {
253 // Create our fileicon data source. 256 // Create our fileicon data source.
254 Profile* profile = Profile::FromBrowserContext(dlm->GetBrowserContext()); 257 Profile* profile = Profile::FromBrowserContext(dlm->GetBrowserContext());
255 content::URLDataSource::Add(profile, new FileIconSource()); 258 content::URLDataSource::Add(profile, new FileIconSource());
256 259
257 if (profile->IsOffTheRecord()) { 260 if (profile->IsOffTheRecord()) {
258 original_notifier_.reset(new AllDownloadItemNotifier( 261 original_notifier_.reset(new AllDownloadItemNotifier(
259 BrowserContext::GetDownloadManager(profile->GetOriginalProfile()), 262 BrowserContext::GetDownloadManager(profile->GetOriginalProfile()),
260 this)); 263 this));
261 } 264 }
262 } 265 }
263 266
264 DownloadsDOMHandler::~DownloadsDOMHandler() { 267 DownloadsDOMHandler::~DownloadsDOMHandler() {
268 while (!removed_ids_.empty()) {
benjhayden 2014/11/13 18:01:59 Could you use DownloadQuery to find downloads whos
Dan Beam 2014/11/19 23:09:32 no because that'd give us all removed downloads, n
Dan Beam 2014/11/19 23:14:06 whoops, meant to delete this -- the code (in gener
269 uint32 remove_id = removed_ids_.back();
270 removed_ids_.pop_back();
271
272 content::DownloadItem* download = GetDownloadById(remove_id);
273 if (download)
274 download->Remove();
275 }
265 } 276 }
266 277
267 // DownloadsDOMHandler, public: ----------------------------------------------- 278 // DownloadsDOMHandler, public: -----------------------------------------------
268 279
269 void DownloadsDOMHandler::OnPageLoaded(const base::ListValue* args) { 280 void DownloadsDOMHandler::OnPageLoaded(const base::ListValue* args) {
270 SendCurrentDownloads(); 281 SendCurrentDownloads();
271 } 282 }
272 283
273 void DownloadsDOMHandler::RegisterMessages() { 284 void DownloadsDOMHandler::RegisterMessages() {
274 web_ui()->RegisterMessageCallback("onPageLoaded", 285 web_ui()->RegisterMessageCallback("onPageLoaded",
(...skipping 28 matching lines...) Expand all
303 weak_ptr_factory_.GetWeakPtr())); 314 weak_ptr_factory_.GetWeakPtr()));
304 web_ui()->RegisterMessageCallback("cancel", 315 web_ui()->RegisterMessageCallback("cancel",
305 base::Bind(&DownloadsDOMHandler::HandleCancel, 316 base::Bind(&DownloadsDOMHandler::HandleCancel,
306 weak_ptr_factory_.GetWeakPtr())); 317 weak_ptr_factory_.GetWeakPtr()));
307 web_ui()->RegisterMessageCallback("clearAll", 318 web_ui()->RegisterMessageCallback("clearAll",
308 base::Bind(&DownloadsDOMHandler::HandleClearAll, 319 base::Bind(&DownloadsDOMHandler::HandleClearAll,
309 weak_ptr_factory_.GetWeakPtr())); 320 weak_ptr_factory_.GetWeakPtr()));
310 web_ui()->RegisterMessageCallback("openDownloadsFolder", 321 web_ui()->RegisterMessageCallback("openDownloadsFolder",
311 base::Bind(&DownloadsDOMHandler::HandleOpenDownloadsFolder, 322 base::Bind(&DownloadsDOMHandler::HandleOpenDownloadsFolder,
312 weak_ptr_factory_.GetWeakPtr())); 323 weak_ptr_factory_.GetWeakPtr()));
324 web_ui()->RegisterMessageCallback("undoRemove",
325 base::Bind(&DownloadsDOMHandler::HandleUndoRemove,
326 weak_ptr_factory_.GetWeakPtr()));
313 } 327 }
314 328
315 void DownloadsDOMHandler::OnDownloadCreated( 329 void DownloadsDOMHandler::OnDownloadCreated(
316 content::DownloadManager* manager, content::DownloadItem* download_item) { 330 content::DownloadManager* manager, content::DownloadItem* download_item) {
317 if (IsDownloadDisplayable(*download_item)) 331 if (IsDownloadDisplayable(*download_item))
318 ScheduleSendCurrentDownloads(); 332 ScheduleSendCurrentDownloads();
319 } 333 }
320 334
321 void DownloadsDOMHandler::OnDownloadUpdated( 335 void DownloadsDOMHandler::OnDownloadUpdated(
322 content::DownloadManager* manager, 336 content::DownloadManager* manager,
(...skipping 10 matching lines...) Expand all
333 query.Search(all_items.begin(), all_items.end(), &filtered_items); 347 query.Search(all_items.begin(), all_items.end(), &filtered_items);
334 if (filtered_items.empty()) 348 if (filtered_items.empty())
335 return; 349 return;
336 } 350 }
337 base::ListValue results_value; 351 base::ListValue results_value;
338 results_value.Append(CreateDownloadItemValue( 352 results_value.Append(CreateDownloadItemValue(
339 download_item, 353 download_item,
340 (original_notifier_.get() && 354 (original_notifier_.get() &&
341 (manager == main_notifier_.GetManager())))); 355 (manager == main_notifier_.GetManager()))));
342 CallDownloadUpdated(results_value); 356 CallDownloadUpdated(results_value);
357 } else if (download_item->GetState() == content::DownloadItem::REMOVED) {
358 ScheduleSendCurrentDownloads();
343 } 359 }
344 } 360 }
345 361
346 void DownloadsDOMHandler::OnDownloadRemoved( 362 void DownloadsDOMHandler::OnDownloadRemoved(
347 content::DownloadManager* manager, 363 content::DownloadManager* manager,
348 content::DownloadItem* download_item) { 364 content::DownloadItem* download_item) {
349 // This relies on |download_item| being removed from DownloadManager in this 365 // This relies on |download_item| being removed from DownloadManager in this
350 // MessageLoop iteration. |download_item| may not have been removed from 366 // MessageLoop iteration. |download_item| may not have been removed from
351 // DownloadManager when OnDownloadRemoved() is fired, so bounce off the 367 // DownloadManager when OnDownloadRemoved() is fired, so bounce off the
352 // MessageLoop to give it a chance to be removed. SendCurrentDownloads() looks 368 // MessageLoop to give it a chance to be removed. SendCurrentDownloads() looks
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
431 if (file) 447 if (file)
432 file->Resume(); 448 file->Resume();
433 } 449 }
434 450
435 void DownloadsDOMHandler::HandleRemove(const base::ListValue* args) { 451 void DownloadsDOMHandler::HandleRemove(const base::ListValue* args) {
436 if (!IsDeletingHistoryAllowed()) 452 if (!IsDeletingHistoryAllowed())
437 return; 453 return;
438 454
439 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_REMOVE); 455 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_REMOVE);
440 content::DownloadItem* file = GetDownloadByValue(args); 456 content::DownloadItem* file = GetDownloadByValue(args);
441 if (file) 457 if (file) {
442 file->Remove(); 458 removed_ids_.push_back(file->GetId());
459 file->MarkRemoved();
460 }
443 } 461 }
444 462
445 void DownloadsDOMHandler::HandleCancel(const base::ListValue* args) { 463 void DownloadsDOMHandler::HandleCancel(const base::ListValue* args) {
446 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CANCEL); 464 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CANCEL);
447 content::DownloadItem* file = GetDownloadByValue(args); 465 content::DownloadItem* file = GetDownloadByValue(args);
448 if (file) 466 if (file)
449 file->Cancel(true); 467 file->Cancel(true);
450 } 468 }
451 469
452 void DownloadsDOMHandler::HandleClearAll(const base::ListValue* args) { 470 void DownloadsDOMHandler::HandleClearAll(const base::ListValue* args) {
(...skipping 20 matching lines...) Expand all
473 const base::ListValue* args) { 491 const base::ListValue* args) {
474 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FOLDER); 492 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FOLDER);
475 content::DownloadManager* manager = main_notifier_.GetManager(); 493 content::DownloadManager* manager = main_notifier_.GetManager();
476 if (manager) { 494 if (manager) {
477 platform_util::OpenItem( 495 platform_util::OpenItem(
478 Profile::FromBrowserContext(manager->GetBrowserContext()), 496 Profile::FromBrowserContext(manager->GetBrowserContext()),
479 DownloadPrefs::FromDownloadManager(manager)->DownloadPath()); 497 DownloadPrefs::FromDownloadManager(manager)->DownloadPath());
480 } 498 }
481 } 499 }
482 500
501 void DownloadsDOMHandler::HandleUndoRemove(const base::ListValue* args) {
arv (Not doing code reviews) 2014/11/13 19:02:30 Don't we want to send an ID of some kind. My conce
502 if (removed_ids_.empty())
503 return;
504
505 uint32 last_removed = removed_ids_.back();
506 removed_ids_.pop_back();
507
508 content::DownloadItem* item = GetDownloadById(last_removed);
509 if (item)
510 item->UndoRemove();
511 }
512
483 // DownloadsDOMHandler, private: ---------------------------------------------- 513 // DownloadsDOMHandler, private: ----------------------------------------------
484 514
485 void DownloadsDOMHandler::ScheduleSendCurrentDownloads() { 515 void DownloadsDOMHandler::ScheduleSendCurrentDownloads() {
486 // Don't call SendCurrentDownloads() every time anything changes. Batch them 516 // Don't call SendCurrentDownloads() every time anything changes. Batch them
487 // together instead. This may handle hundreds of OnDownloadDestroyed() calls 517 // together instead. This may handle hundreds of OnDownloadDestroyed() calls
488 // in a single UI message loop iteration when the user Clears All downloads. 518 // in a single UI message loop iteration when the user Clears All downloads.
489 if (update_scheduled_) 519 if (update_scheduled_)
490 return; 520 return;
491 update_scheduled_ = true; 521 update_scheduled_ = true;
492 BrowserThread::PostTask( 522 BrowserThread::PostTask(
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
556 586
557 bool DownloadsDOMHandler::IsDeletingHistoryAllowed() { 587 bool DownloadsDOMHandler::IsDeletingHistoryAllowed() {
558 content::DownloadManager* manager = main_notifier_.GetManager(); 588 content::DownloadManager* manager = main_notifier_.GetManager();
559 return (manager && 589 return (manager &&
560 Profile::FromBrowserContext(manager->GetBrowserContext())-> 590 Profile::FromBrowserContext(manager->GetBrowserContext())->
561 GetPrefs()->GetBoolean(prefs::kAllowDeletingBrowserHistory)); 591 GetPrefs()->GetBoolean(prefs::kAllowDeletingBrowserHistory));
562 } 592 }
563 593
564 content::DownloadItem* DownloadsDOMHandler::GetDownloadByValue( 594 content::DownloadItem* DownloadsDOMHandler::GetDownloadByValue(
565 const base::ListValue* args) { 595 const base::ListValue* args) {
566 int download_id = -1; 596 std::string download_id;
567 if (!ExtractIntegerValue(args, &download_id)) 597 if (!args->GetString(0, &download_id)) {
598 NOTREACHED();
568 return NULL; 599 return NULL;
600 }
601
602 uint64 id;
603 if (!base::StringToUint64(download_id, &id)) {
604 NOTREACHED();
605 return NULL;
606 }
607
608 return GetDownloadById(id);
609 }
610
611 content::DownloadItem* DownloadsDOMHandler::GetDownloadById(
612 uint32 download_id) {
569 content::DownloadItem* item = NULL; 613 content::DownloadItem* item = NULL;
570 if (main_notifier_.GetManager()) 614 if (main_notifier_.GetManager())
571 item = main_notifier_.GetManager()->GetDownload(download_id); 615 item = main_notifier_.GetManager()->GetDownload(download_id);
572 if (!item && original_notifier_.get() && original_notifier_->GetManager()) 616 if (!item && original_notifier_.get() && original_notifier_->GetManager())
573 item = original_notifier_->GetManager()->GetDownload(download_id); 617 item = original_notifier_->GetManager()->GetDownload(download_id);
574 return item; 618 return item;
575 } 619 }
576 620
577 content::WebContents* DownloadsDOMHandler::GetWebUIWebContents() { 621 content::WebContents* DownloadsDOMHandler::GetWebUIWebContents() {
578 return web_ui()->GetWebContents(); 622 return web_ui()->GetWebContents();
579 } 623 }
580 624
581 void DownloadsDOMHandler::CallDownloadsList(const base::ListValue& downloads) { 625 void DownloadsDOMHandler::CallDownloadsList(const base::ListValue& downloads) {
582 web_ui()->CallJavascriptFunction("downloadsList", downloads); 626 web_ui()->CallJavascriptFunction("downloadsList", downloads);
583 } 627 }
584 628
585 void DownloadsDOMHandler::CallDownloadUpdated( 629 void DownloadsDOMHandler::CallDownloadUpdated(
586 const base::ListValue& download_item) { 630 const base::ListValue& download_item) {
587 web_ui()->CallJavascriptFunction("downloadUpdated", download_item); 631 web_ui()->CallJavascriptFunction("downloadUpdated", download_item);
588 } 632 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698