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

Side by Side Diff: chrome/browser/ui/webui/md_downloads/downloads_list_tracker.cc

Issue 1428833005: MD Downloads: track downloads in C++, dispatch discrete JS updates (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: dpapad@ + asanka@ feedback + self-review Created 5 years 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
(Empty)
1 // Copyright 2015 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/md_downloads/downloads_list_tracker.h"
6
7 #include <iterator>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/i18n/rtl.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/time/time.h"
15 #include "base/value_conversions.h"
16 #include "base/values.h"
17 #include "chrome/browser/download/all_download_item_notifier.h"
18 #include "chrome/browser/download/download_crx_util.h"
19 #include "chrome/browser/download/download_item_model.h"
20 #include "chrome/browser/download/download_query.h"
21 #include "chrome/browser/extensions/api/downloads/downloads_api.h"
22 #include "chrome/browser/extensions/extension_service.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "content/public/browser/browser_context.h"
25 #include "content/public/browser/download_item.h"
26 #include "content/public/browser/download_manager.h"
27 #include "content/public/browser/web_ui.h"
28 #include "extensions/browser/extension_system.h"
29 #include "net/base/filename_util.h"
30 #include "third_party/icu/source/i18n/unicode/datefmt.h"
31 #include "ui/base/l10n/time_format.h"
32
33 using content::BrowserContext;
34 using content::DownloadItem;
35 using content::DownloadManager;
36
37 using DownloadVector = DownloadManager::DownloadVector;
38
39 namespace {
40
41 // Returns a string constant to be used as the |danger_type| value in
42 // CreateDownloadItemValue(). Only return strings for DANGEROUS_FILE,
43 // DANGEROUS_URL, DANGEROUS_CONTENT, and UNCOMMON_CONTENT because the
44 // |danger_type| value is only defined if the value of |state| is |DANGEROUS|.
45 const char* GetDangerTypeString(content::DownloadDangerType danger_type) {
46 switch (danger_type) {
47 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE:
48 return "DANGEROUS_FILE";
49 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_URL:
50 return "DANGEROUS_URL";
51 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_CONTENT:
52 return "DANGEROUS_CONTENT";
53 case content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT:
54 return "UNCOMMON_CONTENT";
55 case content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST:
56 return "DANGEROUS_HOST";
57 case content::DOWNLOAD_DANGER_TYPE_POTENTIALLY_UNWANTED:
58 return "POTENTIALLY_UNWANTED";
59 case content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS:
60 case content::DOWNLOAD_DANGER_TYPE_MAYBE_DANGEROUS_CONTENT:
61 case content::DOWNLOAD_DANGER_TYPE_USER_VALIDATED:
62 case content::DOWNLOAD_DANGER_TYPE_MAX:
63 // Don't return a danger type string if it is NOT_DANGEROUS,
64 // MAYBE_DANGEROUS_CONTENT, or USER_VALIDATED.
65 NOTREACHED();
66 return "";
67 }
68 }
69
70 // TODO(dbeam): if useful elsewhere, move to base/i18n/time_formatting.h?
71 base::string16 TimeFormatLongDate(const base::Time& time) {
72 scoped_ptr<icu::DateFormat> formatter(
73 icu::DateFormat::createDateInstance(icu::DateFormat::kLong));
74 icu::UnicodeString date_string;
75 formatter->format(static_cast<UDate>(time.ToDoubleT() * 1000), date_string);
76 return base::string16(date_string.getBuffer(),
77 static_cast<size_t>(date_string.length()));
78 }
79
80 } // namespace
81
82 DownloadsListTracker::DownloadsListTracker(
83 DownloadManager* download_manager,
84 content::WebUI* web_ui)
85 : main_notifier_(download_manager, this),
86 web_ui_(web_ui),
87 should_show_(base::Bind(&DownloadsListTracker::ShouldShow,
88 base::Unretained(this))) {
89 Init();
90 }
91
92 DownloadsListTracker::~DownloadsListTracker() {}
93
94 void DownloadsListTracker::CallClearAll() {
95 if (sending_updates_)
96 web_ui_->CallJavascriptFunction("downloads.Manager.clearAll");
97 }
98
99 bool DownloadsListTracker::SetSearchTerms(const base::ListValue& search_terms) {
100 std::vector<base::string16> new_terms;
101 new_terms.resize(search_terms.GetSize());
102
103 for (size_t i = 0; i < search_terms.GetSize(); ++i)
104 search_terms.GetString(i, &new_terms[i]);
105
106 if (new_terms == search_terms_)
107 return false;
108
109 search_terms_.swap(new_terms);
110 RebuildSortedSet();
111 return true;
112 }
113
114 void DownloadsListTracker::Start() {
115 sending_updates_ = true;
116
117 // TODO(dbeam): paging and limiting logic.
118
119 base::ListValue list;
120 for (auto* item : sorted_visible_items_)
121 list.Append(CreateDownloadItemValue(item).Pass());
122
123 web_ui_->CallJavascriptFunction("downloads.Manager.insertItems",
124 base::FundamentalValue(0), list);
125 }
126
127 void DownloadsListTracker::Stop() {
128 sending_updates_ = false;
129 }
130
131 DownloadManager* DownloadsListTracker::GetMainNotifierManager() const {
132 return main_notifier_.GetManager();
133 }
134
135 DownloadManager* DownloadsListTracker::GetOriginalNotifierManager() const {
136 return original_notifier_ ? original_notifier_->GetManager() : nullptr;
137 }
138
139 void DownloadsListTracker::OnDownloadCreated(DownloadManager* manager,
140 DownloadItem* download_item) {
141 if (should_show_.Run(*download_item))
142 CallInsertItem(sorted_visible_items_.insert(download_item).first);
143 }
144
145 void DownloadsListTracker::OnDownloadUpdated(DownloadManager* manager,
146 DownloadItem* download_item) {
147 auto current_position = sorted_visible_items_.find(download_item);
148 bool is_showing = current_position != sorted_visible_items_.end();
149 bool should_show = should_show_.Run(*download_item);
150
151 if (!is_showing && should_show)
152 CallInsertItem(sorted_visible_items_.insert(download_item).first);
153 else if (is_showing && !should_show)
154 RemoveItem(current_position);
155 else if (is_showing)
156 CallUpdateItem(current_position);
157 }
158
159 void DownloadsListTracker::OnDownloadRemoved(DownloadManager* manager,
160 DownloadItem* download_item) {
161 auto current_position = sorted_visible_items_.find(download_item);
162 if (current_position != sorted_visible_items_.end())
163 RemoveItem(current_position);
164 }
165
166 DownloadsListTracker::DownloadsListTracker(
167 DownloadManager* download_manager,
168 content::WebUI* web_ui,
169 base::Callback<bool(const DownloadItem&)> should_show)
170 : main_notifier_(download_manager, this),
171 web_ui_(web_ui),
172 should_show_(should_show) {
173 Init();
174 }
175
176 scoped_ptr<base::DictionaryValue> DownloadsListTracker::CreateDownloadItemValue(
177 content::DownloadItem* download_item) const {
178 // TODO(asanka): Move towards using download_model here for getting status and
179 // progress. The difference currently only matters to Drive downloads and
180 // those don't show up on the downloads page, but should.
181 DownloadItemModel download_model(download_item);
182
183 // The items which are to be written into file_value are also described in
184 // chrome/browser/resources/downloads/downloads.js in @typedef for
185 // BackendDownloadObject. Please update it whenever you add or remove
186 // any keys in file_value.
187 scoped_ptr<base::DictionaryValue> file_value(new base::DictionaryValue);
188
189 file_value->SetInteger(
190 "started", static_cast<int>(download_item->GetStartTime().ToTimeT()));
191 file_value->SetString(
192 "since_string", ui::TimeFormat::RelativeDate(
193 download_item->GetStartTime(), NULL));
194 file_value->SetString(
195 "date_string", TimeFormatLongDate(download_item->GetStartTime()));
196
197 file_value->SetString("id", base::Uint64ToString(download_item->GetId()));
198
199 base::FilePath download_path(download_item->GetTargetFilePath());
200 file_value->Set("file_path", base::CreateFilePathValue(download_path));
201 file_value->SetString("file_url",
202 net::FilePathToFileURL(download_path).spec());
203
204 extensions::DownloadedByExtension* by_ext =
205 extensions::DownloadedByExtension::Get(download_item);
206 std::string by_ext_id;
207 std::string by_ext_name;
208 if (by_ext) {
209 by_ext_id = by_ext->id();
210 // TODO(dbeam): why doesn't DownloadsByExtension::name() return a string16?
211 by_ext_name = by_ext->name();
212
213 // Lookup the extension's current name() in case the user changed their
214 // language. This won't work if the extension was uninstalled, so the name
215 // might be the wrong language.
216 bool include_disabled = true;
217 const extensions::Extension* extension = extensions::ExtensionSystem::Get(
218 Profile::FromBrowserContext(download_item->GetBrowserContext()))->
219 extension_service()->GetExtensionById(by_ext->id(), include_disabled);
220 if (extension)
221 by_ext_name = extension->name();
222 }
223 file_value->SetString("by_ext_id", by_ext_id);
224 file_value->SetString("by_ext_name", by_ext_name);
225
226 // Keep file names as LTR. TODO(dbeam): why?
227 base::string16 file_name =
228 download_item->GetFileNameToReportUser().LossyDisplayName();
229 file_name = base::i18n::GetDisplayStringInLTRDirectionality(file_name);
230 file_value->SetString("file_name", file_name);
231 file_value->SetString("url", download_item->GetURL().spec());
232 file_value->SetInteger("total", static_cast<int>(
233 download_item->GetTotalBytes()));
234 file_value->SetBoolean("file_externally_removed",
235 download_item->GetFileExternallyRemoved());
236 file_value->SetBoolean("resume", download_item->CanResume());
237
238 bool incognito = false;
239 auto* original_manager = GetOriginalNotifierManager();
240 if (original_manager) {
241 incognito =
242 original_manager->GetDownload(download_item->GetId()) == download_item;
243 }
244 file_value->SetBoolean("otr", incognito);
245
246 const char* danger_type = "";
247 base::string16 last_reason_text;
248 // -2 is invalid, -1 means indeterminate, and 0-100 are in-progress.
249 int percent = -2;
250 base::string16 progress_status_text;
251 bool retry = false;
252 const char* state = nullptr;
253
254 switch (download_item->GetState()) {
255 case content::DownloadItem::IN_PROGRESS: {
256 if (download_item->IsDangerous()) {
257 state = "DANGEROUS";
258 danger_type = GetDangerTypeString(download_item->GetDangerType());
259 } else if (download_item->IsPaused()) {
260 state = "PAUSED";
261 } else {
262 state = "IN_PROGRESS";
263 }
264 progress_status_text = download_model.GetTabProgressStatusText();
265 percent = download_item->PercentComplete();
266 break;
267 }
268
269 case content::DownloadItem::INTERRUPTED:
270 state = "INTERRUPTED";
271 progress_status_text = download_model.GetTabProgressStatusText();
272
273 if (download_item->CanResume())
274 percent = download_item->PercentComplete();
275
276 last_reason_text = download_model.GetInterruptReasonText();
277 if (content::DOWNLOAD_INTERRUPT_REASON_CRASH ==
278 download_item->GetLastReason() && !download_item->CanResume()) {
279 retry = true;
280 }
281 break;
282
283 case content::DownloadItem::CANCELLED:
284 state = "CANCELLED";
285 retry = true;
286 break;
287
288 case content::DownloadItem::COMPLETE:
289 DCHECK(!download_item->IsDangerous());
290 state = "COMPLETE";
291 break;
292
293 case content::DownloadItem::MAX_DOWNLOAD_STATE:
294 NOTREACHED();
295 }
296
297 DCHECK(state);
298
299 file_value->SetString("danger_type", danger_type);
300 file_value->SetString("last_reason_text", last_reason_text);
301 file_value->SetInteger("percent", percent);
302 file_value->SetString("progress_status_text", progress_status_text);
303 file_value->SetBoolean("retry", retry);
304 file_value->SetString("state", state);
305
306 return file_value.Pass();
307 }
308
309 const DownloadItem* DownloadsListTracker::GetItemForTesting(size_t index)
310 const {
311 if (index >= sorted_visible_items_.size())
312 return nullptr;
313
314 SortedSet::iterator it = sorted_visible_items_.begin();
315 std::advance(it, index);
316 return *it;
317 }
318
319 bool DownloadsListTracker::ShouldShow(const DownloadItem& item) const {
320 return !download_crx_util::IsExtensionDownload(item) &&
321 !item.IsTemporary() &&
322 !item.GetFileNameToReportUser().empty() &&
323 !item.GetTargetFilePath().empty() &&
324 DownloadItemModel(const_cast<DownloadItem*>(&item)).ShouldShowInShelf() &&
325 DownloadQuery::MatchesQuery(search_terms_, item);
326 }
327
328 bool DownloadsListTracker::StartTimeComparator::operator() (
329 const content::DownloadItem* a, const content::DownloadItem* b) const {
330 return a->GetStartTime() > b->GetStartTime();
331 }
332
333 void DownloadsListTracker::Init() {
334 Profile* profile = Profile::FromBrowserContext(
335 GetMainNotifierManager()->GetBrowserContext());
336 if (profile->IsOffTheRecord()) {
337 original_notifier_.reset(new AllDownloadItemNotifier(
338 BrowserContext::GetDownloadManager(profile->GetOriginalProfile()),
339 this));
340 }
341
342 RebuildSortedSet();
343 }
344
345 void DownloadsListTracker::RebuildSortedSet() {
346 DownloadVector all_items, visible_items;
347
348 GetMainNotifierManager()->GetAllDownloads(&all_items);
349
350 if (GetOriginalNotifierManager())
351 GetOriginalNotifierManager()->GetAllDownloads(&all_items);
352
353 DownloadQuery query;
354 query.AddFilter(should_show_);
355 query.Search(all_items.begin(), all_items.end(), &visible_items);
356
357 SortedSet sorted_visible_items(visible_items.begin(), visible_items.end());
358 sorted_visible_items_.swap(sorted_visible_items);
359 }
360
361 void DownloadsListTracker::CallInsertItem(const SortedSet::iterator& insert) {
362 if (!sending_updates_)
363 return;
364
365 base::ListValue list;
366 list.Append(CreateDownloadItemValue(*insert).Pass());
367
368 web_ui_->CallJavascriptFunction("downloads.Manager.insertItems",
369 base::FundamentalValue(GetIndex(insert)),
370 list);
371 }
372
373 void DownloadsListTracker::CallUpdateItem(const SortedSet::iterator& update) {
374 if (!sending_updates_)
375 return;
376
377 web_ui_->CallJavascriptFunction("downloads.Manager.updateItem",
378 base::FundamentalValue(GetIndex(update)),
379 *CreateDownloadItemValue(*update));
380 }
381
382 int DownloadsListTracker::GetIndex(const SortedSet::iterator& position) const {
383 // TODO(dbeam): this could be log(N) if |position| was random access.
384 return std::distance(sorted_visible_items_.begin(), position);
385 }
386
387 void DownloadsListTracker::RemoveItem(const SortedSet::iterator& remove) {
388 if (sending_updates_) {
389 web_ui_->CallJavascriptFunction("downloads.Manager.removeItem",
390 base::FundamentalValue(GetIndex(remove)));
391 }
392 sorted_visible_items_.erase(remove);
393 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698