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