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" |
14 #include "base/logging.h" | 15 #include "base/logging.h" |
| 16 #include "base/memory/singleton.h" |
| 17 #include "base/metrics/field_trial.h" |
15 #include "base/metrics/histogram.h" | 18 #include "base/metrics/histogram.h" |
16 #include "base/prefs/pref_service.h" | 19 #include "base/prefs/pref_service.h" |
17 #include "base/strings/string_number_conversions.h" | 20 #include "base/strings/string_number_conversions.h" |
18 #include "base/strings/string_piece.h" | 21 #include "base/strings/string_piece.h" |
19 #include "base/strings/utf_string_conversions.h" | 22 #include "base/strings/utf_string_conversions.h" |
20 #include "base/supports_user_data.h" | 23 #include "base/supports_user_data.h" |
21 #include "base/threading/thread.h" | 24 #include "base/threading/thread.h" |
| 25 #include "base/value_conversions.h" |
22 #include "base/values.h" | 26 #include "base/values.h" |
23 #include "chrome/browser/browser_process.h" | 27 #include "chrome/browser/browser_process.h" |
| 28 #include "chrome/browser/download/download_crx_util.h" |
24 #include "chrome/browser/download/download_danger_prompt.h" | 29 #include "chrome/browser/download/download_danger_prompt.h" |
25 #include "chrome/browser/download/download_history.h" | 30 #include "chrome/browser/download/download_history.h" |
26 #include "chrome/browser/download/download_item_model.h" | 31 #include "chrome/browser/download/download_item_model.h" |
27 #include "chrome/browser/download/download_prefs.h" | 32 #include "chrome/browser/download/download_prefs.h" |
28 #include "chrome/browser/download/download_query.h" | 33 #include "chrome/browser/download/download_query.h" |
29 #include "chrome/browser/download/download_service.h" | 34 #include "chrome/browser/download/download_service.h" |
30 #include "chrome/browser/download/download_service_factory.h" | 35 #include "chrome/browser/download/download_service_factory.h" |
31 #include "chrome/browser/download/drag_download_item.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" |
32 #include "chrome/browser/platform_util.h" | 39 #include "chrome/browser/platform_util.h" |
33 #include "chrome/browser/profiles/profile.h" | 40 #include "chrome/browser/profiles/profile.h" |
34 #include "chrome/browser/ui/webui/fileicon_source.h" | 41 #include "chrome/browser/ui/webui/fileicon_source.h" |
35 #include "chrome/common/chrome_switches.h" | 42 #include "chrome/common/chrome_switches.h" |
36 #include "chrome/common/pref_names.h" | 43 #include "chrome/common/pref_names.h" |
37 #include "chrome/common/url_constants.h" | 44 #include "chrome/common/url_constants.h" |
38 #include "content/public/browser/browser_thread.h" | 45 #include "content/public/browser/browser_thread.h" |
39 #include "content/public/browser/download_item.h" | 46 #include "content/public/browser/download_item.h" |
40 #include "content/public/browser/download_manager.h" | |
41 #include "content/public/browser/url_data_source.h" | 47 #include "content/public/browser/url_data_source.h" |
42 #include "content/public/browser/user_metrics.h" | 48 #include "content/public/browser/user_metrics.h" |
43 #include "content/public/browser/web_contents.h" | 49 #include "content/public/browser/web_contents.h" |
44 #include "content/public/browser/web_ui.h" | 50 #include "content/public/browser/web_ui.h" |
| 51 #include "extensions/browser/extension_system.h" |
45 #include "net/base/filename_util.h" | 52 #include "net/base/filename_util.h" |
| 53 #include "third_party/icu/source/i18n/unicode/datefmt.h" |
46 #include "ui/base/l10n/time_format.h" | 54 #include "ui/base/l10n/time_format.h" |
47 #include "ui/gfx/image/image.h" | 55 #include "ui/gfx/image/image.h" |
48 | 56 |
49 using base::UserMetricsAction; | 57 using base::UserMetricsAction; |
| 58 using content::BrowserContext; |
50 using content::BrowserThread; | 59 using content::BrowserThread; |
51 | 60 |
52 namespace { | 61 namespace { |
53 | 62 |
| 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 |
54 enum DownloadsDOMEvent { | 67 enum DownloadsDOMEvent { |
55 DOWNLOADS_DOM_EVENT_GET_DOWNLOADS = 0, | 68 DOWNLOADS_DOM_EVENT_GET_DOWNLOADS = 0, |
56 DOWNLOADS_DOM_EVENT_OPEN_FILE = 1, | 69 DOWNLOADS_DOM_EVENT_OPEN_FILE = 1, |
57 DOWNLOADS_DOM_EVENT_DRAG = 2, | 70 DOWNLOADS_DOM_EVENT_DRAG = 2, |
58 DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS = 3, | 71 DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS = 3, |
59 DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS = 4, | 72 DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS = 4, |
60 DOWNLOADS_DOM_EVENT_SHOW = 5, | 73 DOWNLOADS_DOM_EVENT_SHOW = 5, |
61 DOWNLOADS_DOM_EVENT_PAUSE = 6, | 74 DOWNLOADS_DOM_EVENT_PAUSE = 6, |
62 DOWNLOADS_DOM_EVENT_REMOVE = 7, | 75 DOWNLOADS_DOM_EVENT_REMOVE = 7, |
63 DOWNLOADS_DOM_EVENT_CANCEL = 8, | 76 DOWNLOADS_DOM_EVENT_CANCEL = 8, |
64 DOWNLOADS_DOM_EVENT_CLEAR_ALL = 9, | 77 DOWNLOADS_DOM_EVENT_CLEAR_ALL = 9, |
65 DOWNLOADS_DOM_EVENT_OPEN_FOLDER = 10, | 78 DOWNLOADS_DOM_EVENT_OPEN_FOLDER = 10, |
66 DOWNLOADS_DOM_EVENT_RESUME = 11, | 79 DOWNLOADS_DOM_EVENT_RESUME = 11, |
67 DOWNLOADS_DOM_EVENT_MAX | 80 DOWNLOADS_DOM_EVENT_MAX |
68 }; | 81 }; |
69 | 82 |
70 void CountDownloadsDOMEvents(DownloadsDOMEvent event) { | 83 void CountDownloadsDOMEvents(DownloadsDOMEvent event) { |
71 UMA_HISTOGRAM_ENUMERATION("Download.DOMEvent", | 84 UMA_HISTOGRAM_ENUMERATION("Download.DOMEvent", |
72 event, | 85 event, |
73 DOWNLOADS_DOM_EVENT_MAX); | 86 DOWNLOADS_DOM_EVENT_MAX); |
74 } | 87 } |
75 | 88 |
| 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 |
76 } // namespace | 280 } // namespace |
77 | 281 |
78 MdDownloadsDOMHandler::MdDownloadsDOMHandler( | 282 MdDownloadsDOMHandler::MdDownloadsDOMHandler( |
79 content::DownloadManager* download_manager, content::WebUI* web_ui) | 283 content::DownloadManager* download_manager) |
80 : list_tracker_(download_manager, web_ui), | 284 : download_manager_(download_manager), |
| 285 update_scheduled_(false), |
81 weak_ptr_factory_(this) { | 286 weak_ptr_factory_(this) { |
82 // Create our fileicon data source. | 287 // Create our fileicon data source. |
83 Profile* profile = Profile::FromBrowserContext( | 288 Profile* profile = Profile::FromBrowserContext( |
84 download_manager->GetBrowserContext()); | 289 download_manager->GetBrowserContext()); |
85 content::URLDataSource::Add(profile, new FileIconSource()); | 290 content::URLDataSource::Add(profile, new FileIconSource()); |
86 } | 291 } |
87 | 292 |
88 MdDownloadsDOMHandler::~MdDownloadsDOMHandler() { | 293 MdDownloadsDOMHandler::~MdDownloadsDOMHandler() { |
89 FinalizeRemovals(); | 294 FinalizeRemovals(); |
90 } | 295 } |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
126 base::Bind(&MdDownloadsDOMHandler::HandleCancel, | 331 base::Bind(&MdDownloadsDOMHandler::HandleCancel, |
127 weak_ptr_factory_.GetWeakPtr())); | 332 weak_ptr_factory_.GetWeakPtr())); |
128 web_ui()->RegisterMessageCallback("clearAll", | 333 web_ui()->RegisterMessageCallback("clearAll", |
129 base::Bind(&MdDownloadsDOMHandler::HandleClearAll, | 334 base::Bind(&MdDownloadsDOMHandler::HandleClearAll, |
130 weak_ptr_factory_.GetWeakPtr())); | 335 weak_ptr_factory_.GetWeakPtr())); |
131 web_ui()->RegisterMessageCallback("openDownloadsFolder", | 336 web_ui()->RegisterMessageCallback("openDownloadsFolder", |
132 base::Bind(&MdDownloadsDOMHandler::HandleOpenDownloadsFolder, | 337 base::Bind(&MdDownloadsDOMHandler::HandleOpenDownloadsFolder, |
133 weak_ptr_factory_.GetWeakPtr())); | 338 weak_ptr_factory_.GetWeakPtr())); |
134 } | 339 } |
135 | 340 |
136 void MdDownloadsDOMHandler::RenderViewReused( | 341 void MdDownloadsDOMHandler::OnDownloadCreated( |
137 content::RenderViewHost* render_view_host) { | 342 content::DownloadManager* manager, content::DownloadItem* download_item) { |
138 list_tracker_.Stop(); | 343 if (IsDownloadDisplayable(*download_item)) |
| 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(); |
139 } | 412 } |
140 | 413 |
141 void MdDownloadsDOMHandler::HandleGetDownloads(const base::ListValue* args) { | 414 void MdDownloadsDOMHandler::HandleGetDownloads(const base::ListValue* args) { |
142 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_GET_DOWNLOADS); | 415 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_GET_DOWNLOADS); |
| 416 search_terms_.reset(args && !args->empty() ? args->DeepCopy() : NULL); |
| 417 ScheduleSendCurrentDownloads(); |
143 | 418 |
144 bool terms_changed = list_tracker_.SetSearchTerms(*args); | 419 if (!main_notifier_) { |
145 if (terms_changed) | 420 main_notifier_.reset(new AllDownloadItemNotifier(download_manager_, this)); |
146 list_tracker_.CallClearAll(); | |
147 | 421 |
148 list_tracker_.Start(); | 422 Profile* profile = Profile::FromBrowserContext( |
| 423 download_manager_->GetBrowserContext()); |
| 424 if (profile->IsOffTheRecord()) { |
| 425 original_notifier_.reset(new AllDownloadItemNotifier( |
| 426 BrowserContext::GetDownloadManager(profile->GetOriginalProfile()), |
| 427 this)); |
| 428 } |
| 429 } |
149 } | 430 } |
150 | 431 |
151 void MdDownloadsDOMHandler::HandleOpenFile(const base::ListValue* args) { | 432 void MdDownloadsDOMHandler::HandleOpenFile(const base::ListValue* args) { |
152 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FILE); | 433 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FILE); |
153 content::DownloadItem* file = GetDownloadByValue(args); | 434 content::DownloadItem* file = GetDownloadByValue(args); |
154 if (file) | 435 if (file) |
155 file->OpenDownload(); | 436 file->OpenDownload(); |
156 } | 437 } |
157 | 438 |
158 void MdDownloadsDOMHandler::HandleDrag(const base::ListValue* args) { | 439 void MdDownloadsDOMHandler::HandleDrag(const base::ListValue* args) { |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 } | 542 } |
262 | 543 |
263 void MdDownloadsDOMHandler::HandleClearAll(const base::ListValue* args) { | 544 void MdDownloadsDOMHandler::HandleClearAll(const base::ListValue* args) { |
264 if (!IsDeletingHistoryAllowed()) { | 545 if (!IsDeletingHistoryAllowed()) { |
265 // This should only be reached during tests. | 546 // This should only be reached during tests. |
266 return; | 547 return; |
267 } | 548 } |
268 | 549 |
269 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CLEAR_ALL); | 550 CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CLEAR_ALL); |
270 | 551 |
271 list_tracker_.CallClearAll(); | |
272 list_tracker_.Stop(); | |
273 | |
274 DownloadVector downloads; | 552 DownloadVector downloads; |
275 if (GetMainNotifierManager()) | 553 if (GetMainNotifierManager()) |
276 GetMainNotifierManager()->GetAllDownloads(&downloads); | 554 GetMainNotifierManager()->GetAllDownloads(&downloads); |
277 if (GetOriginalNotifierManager()) | 555 if (GetOriginalNotifierManager()) |
278 GetOriginalNotifierManager()->GetAllDownloads(&downloads); | 556 GetOriginalNotifierManager()->GetAllDownloads(&downloads); |
279 RemoveDownloads(downloads); | 557 RemoveDownloads(downloads); |
280 | |
281 list_tracker_.Start(); | |
282 } | 558 } |
283 | 559 |
284 void MdDownloadsDOMHandler::RemoveDownloads(const DownloadVector& to_remove) { | 560 void MdDownloadsDOMHandler::RemoveDownloads(const DownloadVector& to_remove) { |
285 IdSet ids; | 561 IdSet ids; |
286 | 562 |
287 for (auto* download : to_remove) { | 563 for (auto* download : to_remove) { |
288 DownloadItemModel item_model(download); | 564 DownloadItemModel item_model(download); |
289 if (!item_model.ShouldShowInShelf() || | 565 if (!item_model.ShouldShowInShelf() || |
290 download->GetState() == content::DownloadItem::IN_PROGRESS) { | 566 download->GetState() == content::DownloadItem::IN_PROGRESS) { |
291 continue; | 567 continue; |
(...skipping 15 matching lines...) Expand all Loading... |
307 if (manager) { | 583 if (manager) { |
308 platform_util::OpenItem( | 584 platform_util::OpenItem( |
309 Profile::FromBrowserContext(manager->GetBrowserContext()), | 585 Profile::FromBrowserContext(manager->GetBrowserContext()), |
310 DownloadPrefs::FromDownloadManager(manager)->DownloadPath(), | 586 DownloadPrefs::FromDownloadManager(manager)->DownloadPath(), |
311 platform_util::OPEN_FOLDER, platform_util::OpenOperationCallback()); | 587 platform_util::OPEN_FOLDER, platform_util::OpenOperationCallback()); |
312 } | 588 } |
313 } | 589 } |
314 | 590 |
315 // MdDownloadsDOMHandler, private: -------------------------------------------- | 591 // MdDownloadsDOMHandler, private: -------------------------------------------- |
316 | 592 |
| 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 |
317 content::DownloadManager* MdDownloadsDOMHandler::GetMainNotifierManager() | 608 content::DownloadManager* MdDownloadsDOMHandler::GetMainNotifierManager() |
318 const { | 609 const { |
319 return list_tracker_.GetMainNotifierManager(); | 610 return main_notifier_ ? main_notifier_->GetManager() : nullptr; |
320 } | 611 } |
321 | 612 |
322 content::DownloadManager* MdDownloadsDOMHandler::GetOriginalNotifierManager() | 613 content::DownloadManager* MdDownloadsDOMHandler::GetOriginalNotifierManager() |
323 const { | 614 const { |
324 return list_tracker_.GetOriginalNotifierManager(); | 615 return original_notifier_ ? original_notifier_->GetManager() : nullptr; |
325 } | 616 } |
326 | 617 |
327 void MdDownloadsDOMHandler::FinalizeRemovals() { | 618 void MdDownloadsDOMHandler::FinalizeRemovals() { |
328 while (!removals_.empty()) { | 619 while (!removals_.empty()) { |
329 const IdSet remove = removals_.back(); | 620 const IdSet remove = removals_.back(); |
330 removals_.pop_back(); | 621 removals_.pop_back(); |
331 | 622 |
332 for (const auto id : remove) { | 623 for (const auto id : remove) { |
333 content::DownloadItem* download = GetDownloadById(id); | 624 content::DownloadItem* download = GetDownloadById(id); |
334 if (download) | 625 if (download) |
335 download->Remove(); | 626 download->Remove(); |
336 } | 627 } |
337 } | 628 } |
338 } | 629 } |
339 | 630 |
| 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(); |
| 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 |
340 void MdDownloadsDOMHandler::ShowDangerPrompt( | 662 void MdDownloadsDOMHandler::ShowDangerPrompt( |
341 content::DownloadItem* dangerous_item) { | 663 content::DownloadItem* dangerous_item) { |
342 DownloadDangerPrompt* danger_prompt = DownloadDangerPrompt::Create( | 664 DownloadDangerPrompt* danger_prompt = DownloadDangerPrompt::Create( |
343 dangerous_item, | 665 dangerous_item, |
344 GetWebUIWebContents(), | 666 GetWebUIWebContents(), |
345 false, | 667 false, |
346 base::Bind(&MdDownloadsDOMHandler::DangerPromptDone, | 668 base::Bind(&MdDownloadsDOMHandler::DangerPromptDone, |
347 weak_ptr_factory_.GetWeakPtr(), dangerous_item->GetId())); | 669 weak_ptr_factory_.GetWeakPtr(), dangerous_item->GetId())); |
348 // danger_prompt will delete itself. | 670 // danger_prompt will delete itself. |
349 DCHECK(danger_prompt); | 671 DCHECK(danger_prompt); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
393 if (GetMainNotifierManager()) | 715 if (GetMainNotifierManager()) |
394 item = GetMainNotifierManager()->GetDownload(id); | 716 item = GetMainNotifierManager()->GetDownload(id); |
395 if (!item && GetOriginalNotifierManager()) | 717 if (!item && GetOriginalNotifierManager()) |
396 item = GetOriginalNotifierManager()->GetDownload(id); | 718 item = GetOriginalNotifierManager()->GetDownload(id); |
397 return item; | 719 return item; |
398 } | 720 } |
399 | 721 |
400 content::WebContents* MdDownloadsDOMHandler::GetWebUIWebContents() { | 722 content::WebContents* MdDownloadsDOMHandler::GetWebUIWebContents() { |
401 return web_ui()->GetWebContents(); | 723 return web_ui()->GetWebContents(); |
402 } | 724 } |
| 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 |