Chromium Code Reviews| OLD | NEW |
|---|---|
| 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/md_downloads/md_downloads_dom_handler.h" | 5 #include "chrome/browser/ui/webui/md_downloads/md_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" | |
| 15 #include "base/logging.h" | 14 #include "base/logging.h" |
| 16 #include "base/memory/singleton.h" | |
| 17 #include "base/metrics/field_trial.h" | |
| 18 #include "base/metrics/histogram.h" | 15 #include "base/metrics/histogram.h" |
| 19 #include "base/prefs/pref_service.h" | 16 #include "base/prefs/pref_service.h" |
| 20 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
| 21 #include "base/strings/string_piece.h" | 18 #include "base/strings/string_piece.h" |
| 22 #include "base/strings/utf_string_conversions.h" | 19 #include "base/strings/utf_string_conversions.h" |
| 23 #include "base/supports_user_data.h" | 20 #include "base/supports_user_data.h" |
| 24 #include "base/threading/thread.h" | 21 #include "base/threading/thread.h" |
| 25 #include "base/value_conversions.h" | |
| 26 #include "base/values.h" | 22 #include "base/values.h" |
| 27 #include "chrome/browser/browser_process.h" | 23 #include "chrome/browser/browser_process.h" |
| 28 #include "chrome/browser/download/download_crx_util.h" | |
| 29 #include "chrome/browser/download/download_danger_prompt.h" | 24 #include "chrome/browser/download/download_danger_prompt.h" |
| 30 #include "chrome/browser/download/download_history.h" | 25 #include "chrome/browser/download/download_history.h" |
| 31 #include "chrome/browser/download/download_item_model.h" | 26 #include "chrome/browser/download/download_item_model.h" |
| 32 #include "chrome/browser/download/download_prefs.h" | 27 #include "chrome/browser/download/download_prefs.h" |
| 33 #include "chrome/browser/download/download_query.h" | 28 #include "chrome/browser/download/download_query.h" |
| 34 #include "chrome/browser/download/download_service.h" | 29 #include "chrome/browser/download/download_service.h" |
| 35 #include "chrome/browser/download/download_service_factory.h" | 30 #include "chrome/browser/download/download_service_factory.h" |
| 36 #include "chrome/browser/download/drag_download_item.h" | 31 #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" | 32 #include "chrome/browser/platform_util.h" |
| 40 #include "chrome/browser/profiles/profile.h" | 33 #include "chrome/browser/profiles/profile.h" |
| 41 #include "chrome/browser/ui/webui/fileicon_source.h" | 34 #include "chrome/browser/ui/webui/fileicon_source.h" |
| 42 #include "chrome/common/chrome_switches.h" | 35 #include "chrome/common/chrome_switches.h" |
| 43 #include "chrome/common/pref_names.h" | 36 #include "chrome/common/pref_names.h" |
| 44 #include "chrome/common/url_constants.h" | 37 #include "chrome/common/url_constants.h" |
| 45 #include "content/public/browser/browser_thread.h" | 38 #include "content/public/browser/browser_thread.h" |
| 46 #include "content/public/browser/download_item.h" | 39 #include "content/public/browser/download_item.h" |
| 40 #include "content/public/browser/download_manager.h" | |
| 47 #include "content/public/browser/url_data_source.h" | 41 #include "content/public/browser/url_data_source.h" |
| 48 #include "content/public/browser/user_metrics.h" | 42 #include "content/public/browser/user_metrics.h" |
| 49 #include "content/public/browser/web_contents.h" | 43 #include "content/public/browser/web_contents.h" |
| 50 #include "content/public/browser/web_ui.h" | 44 #include "content/public/browser/web_ui.h" |
| 51 #include "extensions/browser/extension_system.h" | |
| 52 #include "net/base/filename_util.h" | 45 #include "net/base/filename_util.h" |
| 53 #include "third_party/icu/source/i18n/unicode/datefmt.h" | |
| 54 #include "ui/base/l10n/time_format.h" | 46 #include "ui/base/l10n/time_format.h" |
| 55 #include "ui/gfx/image/image.h" | 47 #include "ui/gfx/image/image.h" |
| 56 | 48 |
| 57 using base::UserMetricsAction; | 49 using base::UserMetricsAction; |
| 58 using content::BrowserContext; | |
| 59 using content::BrowserThread; | 50 using content::BrowserThread; |
| 60 | 51 |
| 61 namespace { | 52 namespace { |
| 62 | 53 |
| 63 // Maximum number of downloads to show. TODO(glen): Remove this and instead | |
| 64 // stuff the downloads down the pipe slowly. | |
| 65 size_t kMaxNumberOfDownloads = 150; | |
| 66 | |
| 67 enum DownloadsDOMEvent { | 54 enum DownloadsDOMEvent { |
| 68 DOWNLOADS_DOM_EVENT_GET_DOWNLOADS = 0, | 55 DOWNLOADS_DOM_EVENT_GET_DOWNLOADS = 0, |
| 69 DOWNLOADS_DOM_EVENT_OPEN_FILE = 1, | 56 DOWNLOADS_DOM_EVENT_OPEN_FILE = 1, |
| 70 DOWNLOADS_DOM_EVENT_DRAG = 2, | 57 DOWNLOADS_DOM_EVENT_DRAG = 2, |
| 71 DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS = 3, | 58 DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS = 3, |
| 72 DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS = 4, | 59 DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS = 4, |
| 73 DOWNLOADS_DOM_EVENT_SHOW = 5, | 60 DOWNLOADS_DOM_EVENT_SHOW = 5, |
| 74 DOWNLOADS_DOM_EVENT_PAUSE = 6, | 61 DOWNLOADS_DOM_EVENT_PAUSE = 6, |
| 75 DOWNLOADS_DOM_EVENT_REMOVE = 7, | 62 DOWNLOADS_DOM_EVENT_REMOVE = 7, |
| 76 DOWNLOADS_DOM_EVENT_CANCEL = 8, | 63 DOWNLOADS_DOM_EVENT_CANCEL = 8, |
| 77 DOWNLOADS_DOM_EVENT_CLEAR_ALL = 9, | 64 DOWNLOADS_DOM_EVENT_CLEAR_ALL = 9, |
| 78 DOWNLOADS_DOM_EVENT_OPEN_FOLDER = 10, | 65 DOWNLOADS_DOM_EVENT_OPEN_FOLDER = 10, |
| 79 DOWNLOADS_DOM_EVENT_RESUME = 11, | 66 DOWNLOADS_DOM_EVENT_RESUME = 11, |
| 80 DOWNLOADS_DOM_EVENT_MAX | 67 DOWNLOADS_DOM_EVENT_MAX |
| 81 }; | 68 }; |
| 82 | 69 |
| 83 void CountDownloadsDOMEvents(DownloadsDOMEvent event) { | 70 void CountDownloadsDOMEvents(DownloadsDOMEvent event) { |
| 84 UMA_HISTOGRAM_ENUMERATION("Download.DOMEvent", | 71 UMA_HISTOGRAM_ENUMERATION("Download.DOMEvent", |
| 85 event, | 72 event, |
| 86 DOWNLOADS_DOM_EVENT_MAX); | 73 DOWNLOADS_DOM_EVENT_MAX); |
| 87 } | 74 } |
| 88 | 75 |
| 89 // Returns a string constant to be used as the |danger_type| value in | |
| 90 // CreateDownloadItemValue(). Only return strings for DANGEROUS_FILE, | |
| 91 // DANGEROUS_URL, DANGEROUS_CONTENT, and UNCOMMON_CONTENT because the | |
| 92 // |danger_type| value is only defined if the value of |state| is |DANGEROUS|. | |
| 93 const char* GetDangerTypeString(content::DownloadDangerType danger_type) { | |
| 94 switch (danger_type) { | |
| 95 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE: | |
| 96 return "DANGEROUS_FILE"; | |
| 97 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL: | |
| 98 return "DANGEROUS_URL"; | |
| 99 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT: | |
| 100 return "DANGEROUS_CONTENT"; | |
| 101 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT: | |
| 102 return "UNCOMMON_CONTENT"; | |
| 103 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST: | |
| 104 return "DANGEROUS_HOST"; | |
| 105 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED: | |
| 106 return "POTENTIALLY_UNWANTED"; | |
| 107 default: | |
| 108 // Don't return a danger type string if it is NOT_DANGEROUS or | |
| 109 // MAYBE_DANGEROUS_CONTENT. | |
| 110 NOTREACHED(); | |
| 111 return ""; | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 // TODO(dbeam): if useful elsewhere, move to base/i18n/time_formatting.h? | |
| 116 base::string16 TimeFormatLongDate(const base::Time& time) { | |
| 117 scoped_ptr<icu::DateFormat> formatter( | |
| 118 icu::DateFormat::createDateInstance(icu::DateFormat::kLong)); | |
| 119 icu::UnicodeString date_string; | |
| 120 formatter->format(static_cast<UDate>(time.ToDoubleT() * 1000), date_string); | |
| 121 return base::string16(date_string.getBuffer(), | |
| 122 static_cast<size_t>(date_string.length())); | |
| 123 } | |
| 124 | |
| 125 // Returns a JSON dictionary containing some of the attributes of |download|. | |
| 126 // The JSON dictionary will also have a field "id" set to |id|, and a field | |
| 127 // "otr" set to |incognito|. | |
| 128 base::DictionaryValue* CreateDownloadItemValue( | |
| 129 content::DownloadItem* download_item, | |
| 130 bool incognito) { | |
| 131 // TODO(asanka): Move towards using download_model here for getting status and | |
| 132 // progress. The difference currently only matters to Drive downloads and | |
| 133 // those don't show up on the downloads page, but should. | |
| 134 DownloadItemModel download_model(download_item); | |
| 135 | |
| 136 // The items which are to be written into file_value are also described in | |
| 137 // chrome/browser/resources/downloads/downloads.js in @typedef for | |
| 138 // BackendDownloadObject. Please update it whenever you add or remove | |
| 139 // any keys in file_value. | |
| 140 base::DictionaryValue* file_value = new base::DictionaryValue(); | |
| 141 | |
| 142 file_value->SetInteger( | |
| 143 "started", static_cast<int>(download_item->GetStartTime().ToTimeT())); | |
| 144 file_value->SetString( | |
| 145 "since_string", ui::TimeFormat::RelativeDate( | |
| 146 download_item->GetStartTime(), NULL)); | |
| 147 | |
| 148 base::Time start_time = download_item->GetStartTime(); | |
| 149 base::string16 date_string = TimeFormatLongDate(start_time); | |
| 150 file_value->SetString("date_string", date_string); | |
| 151 | |
| 152 file_value->SetString("id", base::Uint64ToString(download_item->GetId())); | |
| 153 | |
| 154 base::FilePath download_path(download_item->GetTargetFilePath()); | |
| 155 file_value->Set("file_path", base::CreateFilePathValue(download_path)); | |
| 156 file_value->SetString("file_url", | |
| 157 net::FilePathToFileURL(download_path).spec()); | |
| 158 | |
| 159 extensions::DownloadedByExtension* by_ext = | |
| 160 extensions::DownloadedByExtension::Get(download_item); | |
| 161 std::string by_ext_id; | |
| 162 std::string by_ext_name; | |
| 163 if (by_ext) { | |
| 164 by_ext_id = by_ext->id(); | |
| 165 // TODO(dbeam): why doesn't DownloadsByExtension::name() return a string16? | |
| 166 by_ext_name = by_ext->name(); | |
| 167 | |
| 168 // Lookup the extension's current name() in case the user changed their | |
| 169 // language. This won't work if the extension was uninstalled, so the name | |
| 170 // might be the wrong language. | |
| 171 bool include_disabled = true; | |
| 172 const extensions::Extension* extension = extensions::ExtensionSystem::Get( | |
| 173 Profile::FromBrowserContext(download_item->GetBrowserContext()))-> | |
| 174 extension_service()->GetExtensionById(by_ext->id(), include_disabled); | |
| 175 if (extension) | |
| 176 file_value->SetString("by_ext_name", extension->name()); | |
| 177 } | |
| 178 file_value->SetString("by_ext_id", by_ext_id); | |
| 179 file_value->SetString("by_ext_name", by_ext_name); | |
| 180 | |
| 181 // Keep file names as LTR. | |
| 182 base::string16 file_name = | |
| 183 download_item->GetFileNameToReportUser().LossyDisplayName(); | |
| 184 file_name = base::i18n::GetDisplayStringInLTRDirectionality(file_name); | |
| 185 file_value->SetString("file_name", file_name); | |
| 186 file_value->SetString("url", download_item->GetURL().spec()); | |
| 187 file_value->SetBoolean("otr", incognito); | |
| 188 file_value->SetInteger("total", static_cast<int>( | |
| 189 download_item->GetTotalBytes())); | |
| 190 file_value->SetBoolean("file_externally_removed", | |
| 191 download_item->GetFileExternallyRemoved()); | |
| 192 file_value->SetBoolean("resume", download_item->CanResume()); | |
| 193 | |
| 194 const char* danger_type = ""; | |
| 195 base::string16 last_reason_text; | |
| 196 // -2 is invalid, -1 means indeterminate, and 0-100 are in-progress. | |
| 197 int percent = -2; | |
| 198 base::string16 progress_status_text; | |
| 199 bool retry = false; | |
| 200 const char* state = nullptr; | |
| 201 | |
| 202 switch (download_item->GetState()) { | |
| 203 case content::DownloadItem::IN_PROGRESS: { | |
| 204 if (download_item->IsDangerous()) { | |
| 205 state = "DANGEROUS"; | |
| 206 // These are the only danger states that the UI is equipped to handle. | |
| 207 DCHECK(download_item->GetDangerType() == | |
| 208 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE || | |
| 209 download_item->GetDangerType() == | |
| 210 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL || | |
| 211 download_item->GetDangerType() == | |
| 212 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT || | |
| 213 download_item->GetDangerType() == | |
| 214 content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT || | |
| 215 download_item->GetDangerType() == | |
| 216 content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST || | |
| 217 download_item->GetDangerType() == | |
| 218 content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED); | |
| 219 danger_type = GetDangerTypeString(download_item->GetDangerType()); | |
| 220 } else if (download_item->IsPaused()) { | |
| 221 state = "PAUSED"; | |
| 222 } else { | |
| 223 state = "IN_PROGRESS"; | |
| 224 } | |
| 225 progress_status_text = download_model.GetTabProgressStatusText(); | |
| 226 percent = download_item->PercentComplete(); | |
| 227 break; | |
| 228 } | |
| 229 | |
| 230 case content::DownloadItem::INTERRUPTED: | |
| 231 state = "INTERRUPTED"; | |
| 232 progress_status_text = download_model.GetTabProgressStatusText(); | |
| 233 | |
| 234 if (download_item->CanResume()) | |
| 235 percent = download_item->PercentComplete(); | |
| 236 | |
| 237 last_reason_text = download_model.GetInterruptReasonText(); | |
| 238 if (content::DOWNLOAD_INTERRUPT_REASON_CRASH == | |
| 239 download_item->GetLastReason() && !download_item->CanResume()) { | |
| 240 retry = true; | |
| 241 } | |
| 242 break; | |
| 243 | |
| 244 case content::DownloadItem::CANCELLED: | |
| 245 state = "CANCELLED"; | |
| 246 retry = true; | |
| 247 break; | |
| 248 | |
| 249 case content::DownloadItem::COMPLETE: | |
| 250 DCHECK(!download_item->IsDangerous()); | |
| 251 state = "COMPLETE"; | |
| 252 break; | |
| 253 | |
| 254 case content::DownloadItem::MAX_DOWNLOAD_STATE: | |
| 255 NOTREACHED(); | |
| 256 } | |
| 257 | |
| 258 DCHECK(state); | |
| 259 | |
| 260 file_value->SetString("danger_type", danger_type); | |
| 261 file_value->SetString("last_reason_text", last_reason_text); | |
| 262 file_value->SetInteger("percent", percent); | |
| 263 file_value->SetString("progress_status_text", progress_status_text); | |
| 264 file_value->SetBoolean("retry", retry); | |
| 265 file_value->SetString("state", state); | |
| 266 | |
| 267 return file_value; | |
| 268 } | |
| 269 | |
| 270 // Filters out extension downloads and downloads that don't have a filename yet. | |
| 271 bool IsDownloadDisplayable(const content::DownloadItem& item) { | |
| 272 return !download_crx_util::IsExtensionDownload(item) && | |
| 273 !item.IsTemporary() && | |
| 274 !item.GetFileNameToReportUser().empty() && | |
| 275 !item.GetTargetFilePath().empty() && | |
| 276 DownloadItemModel( | |
| 277 const_cast<content::DownloadItem*>(&item)).ShouldShowInShelf(); | |
| 278 } | |
| 279 | |
| 280 } // namespace | 76 } // namespace |
| 281 | 77 |
| 282 MdDownloadsDOMHandler::MdDownloadsDOMHandler( | 78 MdDownloadsDOMHandler::MdDownloadsDOMHandler( |
| 283 content::DownloadManager* download_manager) | 79 content::DownloadManager* download_manager, content::WebUI* web_ui) |
| 284 : download_manager_(download_manager), | 80 : list_tracker_(download_manager, web_ui), |
| 285 update_scheduled_(false), | |
| 286 weak_ptr_factory_(this) { | 81 weak_ptr_factory_(this) { |
| 287 // Create our fileicon data source. | 82 // Create our fileicon data source. |
| 288 Profile* profile = Profile::FromBrowserContext( | 83 Profile* profile = Profile::FromBrowserContext( |
| 289 download_manager->GetBrowserContext()); | 84 download_manager->GetBrowserContext()); |
| 290 content::URLDataSource::Add(profile, new FileIconSource()); | 85 content::URLDataSource::Add(profile, new FileIconSource()); |
| 291 } | 86 } |
| 292 | 87 |
| 293 MdDownloadsDOMHandler::~MdDownloadsDOMHandler() { | 88 MdDownloadsDOMHandler::~MdDownloadsDOMHandler() { |
| 294 FinalizeRemovals(); | 89 FinalizeRemovals(); |
| 295 } | 90 } |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 331 base::Bind(&MdDownloadsDOMHandler::HandleCancel, | 126 base::Bind(&MdDownloadsDOMHandler::HandleCancel, |
| 332 weak_ptr_factory_.GetWeakPtr())); | 127 weak_ptr_factory_.GetWeakPtr())); |
| 333 web_ui()->RegisterMessageCallback("clearAll", | 128 web_ui()->RegisterMessageCallback("clearAll", |
| 334 base::Bind(&MdDownloadsDOMHandler::HandleClearAll, | 129 base::Bind(&MdDownloadsDOMHandler::HandleClearAll, |
| 335 weak_ptr_factory_.GetWeakPtr())); | 130 weak_ptr_factory_.GetWeakPtr())); |
| 336 web_ui()->RegisterMessageCallback("openDownloadsFolder", | 131 web_ui()->RegisterMessageCallback("openDownloadsFolder", |
| 337 base::Bind(&MdDownloadsDOMHandler::HandleOpenDownloadsFolder, | 132 base::Bind(&MdDownloadsDOMHandler::HandleOpenDownloadsFolder, |
| 338 weak_ptr_factory_.GetWeakPtr())); | 133 weak_ptr_factory_.GetWeakPtr())); |
| 339 } | 134 } |
| 340 | 135 |
| 341 void MdDownloadsDOMHandler::OnDownloadCreated( | 136 void MdDownloadsDOMHandler::RenderViewReused( |
| 342 content::DownloadManager* manager, content::DownloadItem* download_item) { | 137 content::RenderViewHost* render_view_host) { |
| 343 if (IsDownloadDisplayable(*download_item)) | 138 list_tracker_.Stop(); |
| 344 ScheduleSendCurrentDownloads(); | |
| 345 else | |
| 346 new_downloads_.insert(download_item->GetId()); | |
| 347 } | |
| 348 | |
| 349 void MdDownloadsDOMHandler::OnDownloadUpdated( | |
| 350 content::DownloadManager* manager, | |
| 351 content::DownloadItem* download_item) { | |
| 352 if (update_scheduled_) | |
| 353 return; | |
| 354 | |
| 355 bool showing_new_item = false; | |
| 356 | |
| 357 if (new_downloads_.count(download_item->GetId())) { | |
| 358 // A new download (that the page doesn't know about yet) has been updated. | |
| 359 if (!IsDownloadDisplayable(*download_item)) { | |
| 360 // Item isn't ready to be displayed yet. Wait until it is. | |
| 361 return; | |
| 362 } | |
| 363 | |
| 364 new_downloads_.erase(download_item->GetId()); | |
| 365 showing_new_item = true; | |
| 366 } | |
| 367 | |
| 368 if (showing_new_item || DownloadItemModel(download_item).IsBeingRevived() || | |
| 369 !IsDownloadDisplayable(*download_item)) { | |
| 370 // A download will be shown or hidden by this update. Resend the list. | |
| 371 ScheduleSendCurrentDownloads(); | |
| 372 return; | |
| 373 } | |
| 374 | |
| 375 if (search_terms_ && !search_terms_->empty()) { | |
| 376 // Don't CallUpdateItem() if download_item doesn't match | |
| 377 // search_terms_. | |
| 378 // TODO(benjhayden): Consider splitting MatchesQuery() out to a function. | |
| 379 content::DownloadManager::DownloadVector all_items, filtered_items; | |
| 380 all_items.push_back(download_item); | |
| 381 DownloadQuery query; | |
| 382 query.AddFilter(DownloadQuery::FILTER_QUERY, *search_terms_); | |
| 383 query.Search(all_items.begin(), all_items.end(), &filtered_items); | |
| 384 if (filtered_items.empty()) | |
| 385 return; | |
| 386 } | |
| 387 | |
| 388 DCHECK(manager); | |
| 389 scoped_ptr<base::DictionaryValue> item(CreateDownloadItemValue( | |
| 390 download_item, | |
| 391 original_notifier_ && manager == GetMainNotifierManager())); | |
| 392 CallUpdateItem(*item); | |
| 393 } | |
| 394 | |
| 395 void MdDownloadsDOMHandler::OnDownloadRemoved( | |
| 396 content::DownloadManager* manager, | |
| 397 content::DownloadItem* download_item) { | |
| 398 if (!DownloadItemModel(download_item).ShouldShowInShelf()) | |
| 399 return; | |
| 400 | |
| 401 // This relies on |download_item| being removed from DownloadManager in this | |
| 402 // MessageLoop iteration. |download_item| may not have been removed from | |
| 403 // DownloadManager when OnDownloadRemoved() is fired, so bounce off the | |
| 404 // MessageLoop to give it a chance to be removed. SendCurrentDownloads() looks | |
| 405 // at all downloads, and we do not tell it that |download_item| is being | |
| 406 // removed. If DownloadManager is ever changed to not immediately remove | |
| 407 // |download_item| from its map when OnDownloadRemoved is sent, then | |
| 408 // MdDownloadsDOMHandler::OnDownloadRemoved() will need to explicitly tell | |
| 409 // SendCurrentDownloads() that |download_item| was removed. A | |
| 410 // SupportsUserData::Data would be the correct way to do this. | |
| 411 ScheduleSendCurrentDownloads(); | |
| 412 } | 139 } |
| 413 | 140 |
| 414 void MdDownloadsDOMHandler::HandleGetDownloads(const base::ListValue* args) { | 141 void MdDownloadsDOMHandler::HandleGetDownloads(const base::ListValue* args) { |
| 415 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_GET_DOWNLOADS); | 142 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_GET_DOWNLOADS); |
| 416 search_terms_.reset(args && !args->empty() ? args->DeepCopy() : NULL); | |
| 417 ScheduleSendCurrentDownloads(); | |
| 418 | 143 |
| 419 if (!main_notifier_) { | 144 bool terms_changed = list_tracker_.SetSearchTerms(*args); |
| 420 main_notifier_.reset(new AllDownloadItemNotifier(download_manager_, this)); | 145 if (terms_changed) |
| 146 list_tracker_.CallClearAll(); | |
| 421 | 147 |
| 422 Profile* profile = Profile::FromBrowserContext( | 148 list_tracker_.Start(); |
| 423 download_manager_->GetBrowserContext()); | |
| 424 if (profile->IsOffTheRecord()) { | |
| 425 original_notifier_.reset(new AllDownloadItemNotifier( | |
| 426 BrowserContext::GetDownloadManager(profile->GetOriginalProfile()), | |
| 427 this)); | |
| 428 } | |
| 429 } | |
| 430 } | 149 } |
| 431 | 150 |
| 432 void MdDownloadsDOMHandler::HandleOpenFile(const base::ListValue* args) { | 151 void MdDownloadsDOMHandler::HandleOpenFile(const base::ListValue* args) { |
| 433 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FILE); | 152 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FILE); |
| 434 content::DownloadItem* file = GetDownloadByValue(args); | 153 content::DownloadItem* file = GetDownloadByValue(args); |
| 435 if (file) | 154 if (file) |
| 436 file->OpenDownload(); | 155 file->OpenDownload(); |
| 437 } | 156 } |
| 438 | 157 |
| 439 void MdDownloadsDOMHandler::HandleDrag(const base::ListValue* args) { | 158 void MdDownloadsDOMHandler::HandleDrag(const base::ListValue* args) { |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 542 } | 261 } |
| 543 | 262 |
| 544 void MdDownloadsDOMHandler::HandleClearAll(const base::ListValue* args) { | 263 void MdDownloadsDOMHandler::HandleClearAll(const base::ListValue* args) { |
| 545 if (!IsDeletingHistoryAllowed()) { | 264 if (!IsDeletingHistoryAllowed()) { |
| 546 // This should only be reached during tests. | 265 // This should only be reached during tests. |
| 547 return; | 266 return; |
| 548 } | 267 } |
| 549 | 268 |
| 550 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CLEAR_ALL); | 269 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CLEAR_ALL); |
| 551 | 270 |
| 271 list_tracker_.CallClearAll(); | |
| 272 list_tracker_.Stop(); | |
| 273 | |
| 552 DownloadVector downloads; | 274 DownloadVector downloads; |
| 553 if (GetMainNotifierManager()) | 275 if (GetMainNotifierManager()) |
| 554 GetMainNotifierManager()->GetAllDownloads(&downloads); | 276 GetMainNotifierManager()->GetAllDownloads(&downloads); |
| 555 if (GetOriginalNotifierManager()) | 277 if (GetOriginalNotifierManager()) |
| 556 GetOriginalNotifierManager()->GetAllDownloads(&downloads); | 278 GetOriginalNotifierManager()->GetAllDownloads(&downloads); |
| 557 RemoveDownloads(downloads); | 279 RemoveDownloads(downloads); |
| 280 | |
| 281 list_tracker_.Start(); | |
| 558 } | 282 } |
| 559 | 283 |
| 560 void MdDownloadsDOMHandler::RemoveDownloads(const DownloadVector& to_remove) { | 284 void MdDownloadsDOMHandler::RemoveDownloads(const DownloadVector& to_remove) { |
| 561 IdSet ids; | 285 IdSet ids; |
| 562 | 286 |
| 563 for (auto* download : to_remove) { | 287 for (auto* download : to_remove) { |
| 564 DownloadItemModel item_model(download); | 288 DownloadItemModel item_model(download); |
| 565 if (!item_model.ShouldShowInShelf() || | 289 if (!item_model.ShouldShowInShelf() || |
| 566 download->GetState() == content::DownloadItem::IN_PROGRESS) { | 290 download->GetState() == content::DownloadItem::IN_PROGRESS) { |
| 567 continue; | 291 continue; |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 583 if (manager) { | 307 if (manager) { |
| 584 platform_util::OpenItem( | 308 platform_util::OpenItem( |
| 585 Profile::FromBrowserContext(manager->GetBrowserContext()), | 309 Profile::FromBrowserContext(manager->GetBrowserContext()), |
| 586 DownloadPrefs::FromDownloadManager(manager)->DownloadPath(), | 310 DownloadPrefs::FromDownloadManager(manager)->DownloadPath(), |
| 587 platform_util::OPEN_FOLDER, platform_util::OpenOperationCallback()); | 311 platform_util::OPEN_FOLDER, platform_util::OpenOperationCallback()); |
| 588 } | 312 } |
| 589 } | 313 } |
| 590 | 314 |
| 591 // MdDownloadsDOMHandler, private: -------------------------------------------- | 315 // MdDownloadsDOMHandler, private: -------------------------------------------- |
| 592 | 316 |
| 593 void MdDownloadsDOMHandler::ScheduleSendCurrentDownloads() { | |
| 594 // Don't call SendCurrentDownloads() every time anything changes. Batch them | |
| 595 // together instead. This may handle hundreds of OnDownloadDestroyed() calls | |
| 596 // in a single UI message loop iteration when the user Clears All downloads. | |
| 597 if (update_scheduled_) | |
| 598 return; | |
| 599 | |
| 600 update_scheduled_ = true; | |
| 601 | |
| 602 BrowserThread::PostTask( | |
| 603 BrowserThread::UI, FROM_HERE, | |
| 604 base::Bind(&MdDownloadsDOMHandler::SendCurrentDownloads, | |
| 605 weak_ptr_factory_.GetWeakPtr())); | |
| 606 } | |
| 607 | |
| 608 content::DownloadManager* MdDownloadsDOMHandler::GetMainNotifierManager() | 317 content::DownloadManager* MdDownloadsDOMHandler::GetMainNotifierManager() |
| 609 const { | 318 const { |
| 610 return main_notifier_ ? main_notifier_->GetManager() : nullptr; | 319 return list_tracker_.GetMainNotifierManager(); |
| 611 } | 320 } |
| 612 | 321 |
| 613 content::DownloadManager* MdDownloadsDOMHandler::GetOriginalNotifierManager() | 322 content::DownloadManager* MdDownloadsDOMHandler::GetOriginalNotifierManager() |
| 614 const { | 323 const { |
| 615 return original_notifier_ ? original_notifier_->GetManager() : nullptr; | 324 return list_tracker_.GetOriginalNotifierManager(); |
| 616 } | 325 } |
| 617 | 326 |
| 618 void MdDownloadsDOMHandler::FinalizeRemovals() { | 327 void MdDownloadsDOMHandler::FinalizeRemovals() { |
| 619 while (!removals_.empty()) { | 328 while (!removals_.empty()) { |
| 620 const IdSet remove = removals_.back(); | 329 const IdSet remove = removals_.back(); |
| 621 removals_.pop_back(); | 330 removals_.pop_back(); |
| 622 | 331 |
| 623 for (const auto id : remove) { | 332 for (const auto id : remove) { |
| 624 content::DownloadItem* download = GetDownloadById(id); | 333 content::DownloadItem* download = GetDownloadById(id); |
| 625 if (download) | 334 if (download) |
| 626 download->Remove(); | 335 download->Remove(); |
| 627 } | 336 } |
| 628 } | 337 } |
| 629 } | 338 } |
| 630 | 339 |
| 631 void MdDownloadsDOMHandler::SendCurrentDownloads() { | |
| 632 update_scheduled_ = false; | |
| 633 | |
| 634 content::DownloadManager::DownloadVector all_items, filtered_items; | |
| 635 if (GetMainNotifierManager()) { | |
| 636 GetMainNotifierManager()->GetAllDownloads(&all_items); | |
| 637 GetMainNotifierManager()->CheckForHistoryFilesRemoval(); | |
|
Dan Beam
2016/01/21 02:21:42
^ well, I dropped this on the floor
| |
| 638 } | |
| 639 if (GetOriginalNotifierManager()) { | |
| 640 GetOriginalNotifierManager()->GetAllDownloads(&all_items); | |
| 641 GetOriginalNotifierManager()->CheckForHistoryFilesRemoval(); | |
| 642 } | |
| 643 | |
| 644 DownloadQuery query; | |
| 645 if (search_terms_ && !search_terms_->empty()) | |
| 646 query.AddFilter(DownloadQuery::FILTER_QUERY, *search_terms_); | |
| 647 query.AddFilter(base::Bind(&IsDownloadDisplayable)); | |
| 648 query.AddSorter(DownloadQuery::SORT_START_TIME, DownloadQuery::DESCENDING); | |
| 649 query.Limit(kMaxNumberOfDownloads); | |
| 650 query.Search(all_items.begin(), all_items.end(), &filtered_items); | |
| 651 | |
| 652 base::ListValue results_value; | |
| 653 for (auto* item : filtered_items) { | |
| 654 results_value.Append(CreateDownloadItemValue( | |
| 655 item, | |
| 656 original_notifier_ && GetMainNotifierManager() && | |
| 657 GetMainNotifierManager()->GetDownload(item->GetId()) == item)); | |
| 658 } | |
| 659 CallUpdateAll(results_value); | |
| 660 } | |
| 661 | |
| 662 void MdDownloadsDOMHandler::ShowDangerPrompt( | 340 void MdDownloadsDOMHandler::ShowDangerPrompt( |
| 663 content::DownloadItem* dangerous_item) { | 341 content::DownloadItem* dangerous_item) { |
| 664 DownloadDangerPrompt* danger_prompt = DownloadDangerPrompt::Create( | 342 DownloadDangerPrompt* danger_prompt = DownloadDangerPrompt::Create( |
| 665 dangerous_item, | 343 dangerous_item, |
| 666 GetWebUIWebContents(), | 344 GetWebUIWebContents(), |
| 667 false, | 345 false, |
| 668 base::Bind(&MdDownloadsDOMHandler::DangerPromptDone, | 346 base::Bind(&MdDownloadsDOMHandler::DangerPromptDone, |
| 669 weak_ptr_factory_.GetWeakPtr(), dangerous_item->GetId())); | 347 weak_ptr_factory_.GetWeakPtr(), dangerous_item->GetId())); |
| 670 // danger_prompt will delete itself. | 348 // danger_prompt will delete itself. |
| 671 DCHECK(danger_prompt); | 349 DCHECK(danger_prompt); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 715 if (GetMainNotifierManager()) | 393 if (GetMainNotifierManager()) |
| 716 item = GetMainNotifierManager()->GetDownload(id); | 394 item = GetMainNotifierManager()->GetDownload(id); |
| 717 if (!item && GetOriginalNotifierManager()) | 395 if (!item && GetOriginalNotifierManager()) |
| 718 item = GetOriginalNotifierManager()->GetDownload(id); | 396 item = GetOriginalNotifierManager()->GetDownload(id); |
| 719 return item; | 397 return item; |
| 720 } | 398 } |
| 721 | 399 |
| 722 content::WebContents* MdDownloadsDOMHandler::GetWebUIWebContents() { | 400 content::WebContents* MdDownloadsDOMHandler::GetWebUIWebContents() { |
| 723 return web_ui()->GetWebContents(); | 401 return web_ui()->GetWebContents(); |
| 724 } | 402 } |
| 725 | |
| 726 void MdDownloadsDOMHandler::CallUpdateAll(const base::ListValue& list) { | |
| 727 web_ui()->CallJavascriptFunction("downloads.Manager.updateAll", list); | |
| 728 } | |
| 729 | |
| 730 void MdDownloadsDOMHandler::CallUpdateItem(const base::DictionaryValue& item) { | |
| 731 web_ui()->CallJavascriptFunction("downloads.Manager.updateItem", item); | |
| 732 } | |
| OLD | NEW |