OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2011 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 "content/browser/download/download_query.h" | |
6 | |
7 #include <algorithm> | |
8 #include <set> | |
9 #include <string> | |
10 #include <vector> | |
11 | |
12 #include "base/bind.h" | |
13 #include "base/callback.h" | |
14 #include "base/logging.h" | |
15 #include "base/memory/scoped_ptr.h" | |
16 #include "base/stl_util.h" | |
17 #include "base/string16.h" | |
18 #include "base/string_split.h" | |
19 #include "base/time.h" | |
20 #include "base/utf_string_conversions.h" | |
21 #include "content/browser/download/download_item.h" | |
22 #include "googleurl/src/gurl.h" | |
23 #include "unicode/regex.h" | |
24 | |
25 namespace { | |
26 | |
27 enum ComparisonType {LT, EQ, GT}; | |
28 | |
29 static bool MatchesQuery(const string16& value, const DownloadItem& item) { | |
Randy Smith (Not in Mondays)
2011/11/30 19:26:17
nit, suggestion: Comment before this set of functi
benjhayden
2011/11/30 22:13:49
Done.
| |
30 return item.MatchesQuery(value); | |
31 } | |
32 | |
33 static int GetStartTime(const DownloadItem& item) { | |
34 return (item.GetStartTime() - base::Time::UnixEpoch()).InMilliseconds(); | |
35 } | |
36 | |
37 static int GetEndTime(const DownloadItem& item) { | |
38 return 0; // TODO | |
39 } | |
40 | |
41 static bool GetDangerAccepted(const DownloadItem& item) { | |
42 return (item.GetSafetyState() == DownloadItem::DANGEROUS_BUT_VALIDATED); | |
43 } | |
44 | |
45 static string16 GetFilename(const DownloadItem& item) { | |
46 // This filename will be compared with strings that could be passed in by the | |
47 // user, who only sees LossyDisplayNames. | |
48 return item.GetFullPath().LossyDisplayName(); | |
49 } | |
50 | |
51 static std::string GetFilenameUTF8(const DownloadItem& item) { | |
52 return UTF16ToUTF8(GetFilename(item)); | |
53 } | |
54 | |
55 static std::string GetUrl(const DownloadItem& item) { | |
56 return item.GetOriginalUrl().spec(); | |
57 } | |
58 | |
59 static DownloadItem::DownloadState GetState(const DownloadItem& item) { | |
60 return item.GetState(); | |
61 } | |
62 | |
63 static DownloadStateInfo::DangerType GetDangerType(const DownloadItem& item) { | |
64 return item.GetDangerType(); | |
65 } | |
66 | |
67 static int GetReceivedBytes(const DownloadItem& item) { | |
68 return item.GetReceivedBytes(); | |
69 } | |
70 | |
71 static int GetTotalBytes(const DownloadItem& item) { | |
72 return item.GetTotalBytes(); | |
73 } | |
74 | |
75 static std::string GetMimeType(const DownloadItem& item) { | |
76 return item.GetMimeType(); | |
77 } | |
78 | |
79 static bool IsPaused(const DownloadItem& item) { | |
80 return item.IsPaused(); | |
81 } | |
82 | |
83 // Wrap Callback to work around a bug in base::Bind/Callback where the inner | |
84 // callback is nullified when the outer callback is Run. | |
Randy Smith (Not in Mondays)
2011/11/30 19:26:17
nit: If there's an issue number, it should be in t
benjhayden
2011/11/30 22:13:49
There isn't a bug, it's just one CL: http://codere
| |
85 template<typename ValueType> | |
86 class InnerCallback { | |
87 public: | |
88 typedef base::Callback<ValueType(const DownloadItem&)> CallbackType; | |
89 | |
90 explicit InnerCallback(const CallbackType& inner) : inner_(inner) {} | |
91 ~InnerCallback() {} | |
92 | |
93 // Mimic Callback's interface to facilitate removing InnerCallback when the | |
94 // bug is fixed. | |
95 bool is_null() const { return inner_.is_null(); } | |
96 ValueType Run(const DownloadItem& item) const { return inner_.Run(item); } | |
97 | |
98 private: | |
99 CallbackType inner_; | |
100 }; | |
101 | |
102 // Returns true if |item| matches the filter specified by |value|, |signum|, and | |
Randy Smith (Not in Mondays)
2011/11/30 19:26:17
nit: I think this would be more easily comprehensi
benjhayden
2011/11/30 22:13:49
Done.
| |
103 // |accessor|. |accessor| is conceptually a function that takes a DownloadItem | |
104 // and returns one of its fields, which is then compared to |value|. | |
105 template<typename ValueType> | |
106 static bool FieldMatches( | |
107 const ValueType& value, | |
108 ComparisonType signum, | |
109 const InnerCallback<ValueType>& accessor, | |
110 const DownloadItem& item) { | |
111 switch (signum) { | |
112 case LT: return accessor.Run(item) < value; | |
113 case EQ: return accessor.Run(item) == value; | |
114 case GT: return accessor.Run(item) > value; | |
115 } | |
116 return false; | |
117 } | |
118 | |
119 static bool ValidateRegex(const std::string& regex_str) { | |
120 UParseError re_err; | |
121 UErrorCode re_status = U_ZERO_ERROR; | |
122 scoped_ptr<icu::RegexPattern> pattern(icu::RegexPattern::compile( | |
123 icu::UnicodeString::fromUTF8(regex_str), re_err, re_status)); | |
124 return U_SUCCESS(re_status); | |
125 } | |
126 | |
127 // Returns true if |accessor.Run(item)| matches the regex pattern in | |
128 // |regex_str|. | |
129 static bool FindRegex( | |
130 const std::string& regex_str, | |
131 const InnerCallback<std::string>& accessor, | |
132 const DownloadItem& item) { | |
133 CHECK(!accessor.is_null()); | |
134 icu::UnicodeString pattern(regex_str.c_str()); | |
135 icu::UnicodeString input(accessor.Run(item).c_str()); | |
136 UErrorCode status = U_ZERO_ERROR; | |
137 icu::RegexMatcher match(pattern, input, 0, status); | |
Randy Smith (Not in Mondays)
2011/11/30 19:26:17
This looks like we're going to recompile the patte
benjhayden
2011/11/30 22:13:49
Done.
| |
138 return match.find(); | |
139 } | |
140 | |
141 // Returns a ComparisonType to indicate whether a field in |left| is less than, | |
142 // greater than or equal to the same field in |right|. | |
143 template<typename ValueType> | |
144 static ComparisonType Compare( | |
145 const InnerCallback<ValueType>& accessor, | |
146 const DownloadItem& left, const DownloadItem& right) { | |
147 CHECK(!accessor.is_null()); | |
148 ValueType left_value = accessor.Run(left); | |
149 ValueType right_value = accessor.Run(right); | |
150 if (left_value > right_value) return GT; | |
151 if (left_value < right_value) return LT; | |
152 DCHECK_EQ(left_value, right_value); | |
153 return EQ; | |
154 } | |
155 | |
156 } // anonymous namespace | |
157 | |
158 DownloadQuery::DownloadQuery() | |
159 : limit_(kuint32max) { | |
160 } | |
161 | |
162 DownloadQuery::~DownloadQuery() { | |
163 } | |
164 | |
165 // Filter() pushes a new FilterType to filter_fields_. Most FilterTypes are | |
166 // Callbacks to FieldMatches<>(). Search() iterates over given DownloadItems, | |
167 // discarding items for which any filter returns false. A DownloadQuery may have | |
168 // zero or more FilterTypes. | |
169 | |
170 void DownloadQuery::Filter(const DownloadQuery::FilterType& value) { | |
171 filter_fields_.push_back(value); | |
172 } | |
173 | |
174 void DownloadQuery::Filter(DownloadItem::DownloadState state) { | |
175 filter_fields_.push_back(base::Bind( | |
176 &FieldMatches<DownloadItem::DownloadState>, | |
177 state, | |
178 EQ, | |
179 InnerCallback<DownloadItem::DownloadState>(base::Bind(&GetState)))); | |
180 } | |
181 | |
182 void DownloadQuery::Filter(DownloadStateInfo::DangerType danger) { | |
183 filter_fields_.push_back(base::Bind( | |
184 &FieldMatches<DownloadStateInfo::DangerType>, | |
185 danger, | |
186 EQ, | |
187 InnerCallback<DownloadStateInfo::DangerType>(base::Bind( | |
188 &GetDangerType)))); | |
189 } | |
190 | |
191 bool DownloadQuery::Filter(DownloadQuery::FilterFieldName name, int value) { | |
192 switch (static_cast<int>(name)) { | |
193 case FILTER_FIELD_START_TIME: | |
194 Filter(base::Bind(&FieldMatches<int>, value, EQ, | |
195 InnerCallback<int>(base::Bind(&GetStartTime)))); | |
196 return true; | |
197 case FILTER_FIELD_STARTED_BEFORE: | |
198 Filter(base::Bind(&FieldMatches<int>, value, GT, | |
199 InnerCallback<int>(base::Bind(&GetStartTime)))); | |
200 return true; | |
201 case FILTER_FIELD_STARTED_AFTER: | |
202 Filter(base::Bind(&FieldMatches<int>, value, LT, | |
203 InnerCallback<int>(base::Bind(&GetStartTime)))); | |
204 return true; | |
205 case FILTER_FIELD_END_TIME: | |
206 Filter(base::Bind(&FieldMatches<int>, value, EQ, | |
207 InnerCallback<int>(base::Bind(&GetEndTime)))); | |
208 return true; | |
209 case FILTER_FIELD_ENDED_BEFORE: | |
210 Filter(base::Bind(&FieldMatches<int>, value, GT, | |
211 InnerCallback<int>(base::Bind(&GetEndTime)))); | |
212 return true; | |
213 case FILTER_FIELD_ENDED_AFTER: | |
214 Filter(base::Bind(&FieldMatches<int>, value, LT, | |
215 InnerCallback<int>(base::Bind(&GetEndTime)))); | |
216 return true; | |
217 case FILTER_FIELD_BYTES_RECEIVED: | |
218 Filter(base::Bind(&FieldMatches<int>, value, EQ, | |
219 InnerCallback<int>(base::Bind(&GetReceivedBytes)))); | |
220 return true; | |
221 case FILTER_FIELD_TOTAL_BYTES: | |
222 Filter(base::Bind(&FieldMatches<int>, value, EQ, | |
223 InnerCallback<int>(base::Bind(&GetTotalBytes)))); | |
224 return true; | |
225 case FILTER_FIELD_TOTAL_BYTES_GREATER: | |
226 Filter(base::Bind(&FieldMatches<int>, value, LT, | |
227 InnerCallback<int>(base::Bind(&GetTotalBytes)))); | |
228 return true; | |
229 case FILTER_FIELD_TOTAL_BYTES_LESS: | |
230 Filter(base::Bind(&FieldMatches<int>, value, GT, | |
231 InnerCallback<int>(base::Bind(&GetTotalBytes)))); | |
232 return true; | |
233 case FILTER_FIELD_FILE_SIZE: return false; // TODO | |
234 case FILTER_FIELD_FILE_SIZE_GREATER: return false; // TODO | |
235 case FILTER_FIELD_FILE_SIZE_LESS: return false; // TODO | |
236 case FILTER_FIELD_ERROR: return false; // TODO | |
237 } | |
238 return false; | |
239 } | |
240 | |
241 bool DownloadQuery::Filter(DownloadQuery::FilterFieldName name, bool value) { | |
242 switch (static_cast<int>(name)) { | |
243 case FILTER_FIELD_DANGER_ACCEPTED: | |
244 Filter(base::Bind(&FieldMatches<bool>, value, EQ, | |
245 InnerCallback<bool>(base::Bind(&GetDangerAccepted)))); | |
246 return true; | |
247 case FILTER_FIELD_PAUSED: | |
248 Filter(base::Bind(&FieldMatches<bool>, value, EQ, | |
249 InnerCallback<bool>(base::Bind(&IsPaused)))); | |
250 return true; | |
251 } | |
252 return false; | |
253 } | |
254 | |
255 bool DownloadQuery::Filter( | |
256 DownloadQuery::FilterFieldName name, const std::string& value) { | |
257 switch (static_cast<int>(name)) { | |
258 case FILTER_FIELD_MIME: | |
259 Filter(base::Bind(&FieldMatches<std::string>, value, EQ, | |
260 InnerCallback<std::string>(base::Bind(&GetMimeType)))); | |
261 return true; | |
262 case FILTER_FIELD_URL: | |
263 Filter(base::Bind(&FieldMatches<std::string>, value, EQ, | |
264 InnerCallback<std::string>(base::Bind(&GetUrl)))); | |
265 return true; | |
266 case FILTER_FIELD_URL_REGEX: | |
267 if (!ValidateRegex(value)) return false; | |
268 Filter(base::Bind(&FindRegex, value, | |
269 InnerCallback<std::string>(base::Bind(&GetUrl)))); | |
270 return true; | |
271 } | |
272 return false; | |
273 } | |
274 | |
275 bool DownloadQuery::Filter( | |
276 DownloadQuery::FilterFieldName name, const string16& value) { | |
277 switch (static_cast<int>(name)) { | |
278 case FILTER_FIELD_QUERY: | |
279 Filter(base::Bind(&MatchesQuery, value)); | |
280 return true; | |
281 case FILTER_FIELD_FILENAME: | |
282 Filter(base::Bind(&FieldMatches<string16>, value, EQ, | |
283 InnerCallback<string16>(base::Bind(&GetFilename)))); | |
284 return true; | |
285 case FILTER_FIELD_FILENAME_REGEX: | |
286 if (!ValidateRegex(UTF16ToUTF8(value))) return false; | |
287 Filter(base::Bind(&FindRegex, UTF16ToUTF8(value), | |
288 InnerCallback<std::string>(base::Bind(&GetFilenameUTF8)))); | |
289 return true; | |
290 } | |
291 return false; | |
292 } | |
293 | |
294 bool DownloadQuery::Matches(const DownloadItem& item) const { | |
295 for (FilterFieldVector::const_iterator filter = filter_fields_.begin(); | |
296 filter != filter_fields_.end(); ++filter) { | |
297 CHECK(!filter->is_null()); | |
298 if (!filter->Run(item)) | |
299 return false; | |
300 } | |
301 return true; | |
302 } | |
303 | |
304 // Sort() creates a SortField and pushes it onto sort_fields_. A SortField is a | |
305 // direction and a Callback to Compare<>(). After filtering, Search() makes a | |
306 // DownloadComparator functor from the sort_fields_ and passes the | |
307 // DownloadComparator to std::partial_sort. std::partial_sort calls the | |
308 // DownloadComparator with different pairs of DownloadItems. DownloadComparator | |
309 // iterates over the sort fields until a callback returns ComparisonType LT or | |
310 // GT. DownloadComparator returns true or false depending on that | |
311 // ComparisonType and the sort field's direction in order to indicate to | |
312 // std::partial_sort whether the left item is after or before the right item. If | |
313 // all sort fields return EQ, then DownloadComparator compares GetId or | |
314 // GetDbHandle. A DownloadQuery may have zero or more SortFields, but there is | |
315 // one DownloadComparator per call to Search(). | |
316 | |
317 struct DownloadQuery::SortField { | |
318 typedef base::Callback<ComparisonType( | |
319 const DownloadItem&, const DownloadItem&)> SortType; | |
320 | |
321 SortField(DownloadQuery::SortFieldDirection adirection, | |
322 const SortType& asorter) | |
323 : direction(adirection), | |
324 sorter(asorter) { | |
325 } | |
326 ~SortField() {} | |
327 | |
328 DownloadQuery::SortFieldDirection direction; | |
329 SortType sorter; | |
330 }; | |
331 | |
332 class DownloadQuery::DownloadComparator { | |
333 public: | |
334 DownloadComparator(const DownloadQuery::SortFieldVector& terms) | |
335 : terms_(terms) { | |
336 } | |
337 | |
338 // Returns true if |left| sorts before |right|. | |
339 bool operator() (const DownloadItem* left, const DownloadItem* right); | |
340 | |
341 private: | |
342 const DownloadQuery::SortFieldVector& terms_; | |
343 | |
344 // std::sort requires this class to be copyable. | |
345 }; | |
346 | |
347 bool DownloadQuery::DownloadComparator::operator() ( | |
348 const DownloadItem* left, const DownloadItem* right) { | |
349 for (DownloadQuery::SortFieldVector::const_iterator term = terms_.begin(); | |
350 term != terms_.end(); ++term) { | |
351 CHECK(!(*term).sorter.is_null()); | |
352 switch ((*term).sorter.Run(*left, *right)) { | |
353 case LT: return ((*term).direction == DownloadQuery::ASCENDING); | |
354 case GT: return ((*term).direction == DownloadQuery::DESCENDING); | |
355 case EQ: continue; | |
356 } | |
357 } | |
358 if (left->GetId() != right->GetId()) | |
359 return left->GetId() < right->GetId(); | |
360 CHECK_NE(left->GetDbHandle(), right->GetDbHandle()); | |
361 return left->GetDbHandle() < right->GetDbHandle(); | |
362 } | |
363 | |
364 void DownloadQuery::Sort(DownloadQuery::SortFieldName name, | |
365 DownloadQuery::SortFieldDirection direction) { | |
366 switch (name) { | |
367 case SORT_FIELD_START_TIME: | |
368 sort_fields_.push_back(SortField(direction, base::Bind( | |
369 &Compare<int>, InnerCallback<int>(base::Bind(&GetStartTime))))); | |
370 break; | |
371 case SORT_FIELD_URL: | |
372 sort_fields_.push_back(SortField(direction, base::Bind( | |
373 &Compare<std::string>, | |
374 InnerCallback<std::string>(base::Bind(&GetUrl))))); | |
375 break; | |
376 case SORT_FIELD_FILENAME: | |
377 sort_fields_.push_back(SortField(direction, base::Bind( | |
378 &Compare<string16>, | |
379 InnerCallback<string16>(base::Bind(&GetFilename))))); | |
380 break; | |
381 case SORT_FIELD_DANGER: | |
382 sort_fields_.push_back(SortField(direction, base::Bind( | |
383 &Compare<DownloadStateInfo::DangerType>, | |
384 InnerCallback<DownloadStateInfo::DangerType>(base::Bind( | |
385 &GetDangerType))))); | |
386 break; | |
387 case SORT_FIELD_DANGER_ACCEPTED: | |
388 sort_fields_.push_back(SortField(direction, base::Bind( | |
389 &Compare<bool>, | |
390 InnerCallback<bool>(base::Bind(&GetDangerAccepted))))); | |
391 break; | |
392 case SORT_FIELD_STATE: | |
393 sort_fields_.push_back(SortField(direction, base::Bind( | |
394 &Compare<DownloadItem::DownloadState>, | |
395 InnerCallback<DownloadItem::DownloadState>(base::Bind(&GetState))))); | |
396 break; | |
397 case SORT_FIELD_PAUSED: | |
398 sort_fields_.push_back(SortField(direction, base::Bind( | |
399 &Compare<bool>, InnerCallback<bool>(base::Bind(&IsPaused))))); | |
400 break; | |
401 case SORT_FIELD_MIME: | |
402 sort_fields_.push_back(SortField(direction, base::Bind( | |
403 &Compare<std::string>, | |
404 InnerCallback<std::string>(base::Bind(&GetMimeType))))); | |
405 break; | |
406 case SORT_FIELD_END_TIME: | |
407 sort_fields_.push_back(SortField(direction, base::Bind( | |
408 &Compare<int>, InnerCallback<int>(base::Bind(&GetEndTime))))); | |
409 break; | |
410 case SORT_FIELD_BYTES_RECEIVED: | |
411 sort_fields_.push_back(SortField(direction, base::Bind( | |
412 &Compare<int>, InnerCallback<int>(base::Bind(&GetReceivedBytes))))); | |
413 break; | |
414 case SORT_FIELD_TOTAL_BYTES: | |
415 sort_fields_.push_back(SortField(direction, base::Bind( | |
416 &Compare<int>, InnerCallback<int>(base::Bind(&GetTotalBytes))))); | |
417 break; | |
418 case SORT_FIELD_FILE_SIZE: break; // TODO | |
419 case SORT_FIELD_ERROR: break; // TODO | |
Randy Smith (Not in Mondays)
2011/11/30 19:26:17
Why are these no-ops? I think of it as friendlier
benjhayden
2011/11/30 22:13:49
Sort() doesn't return anything, so I'll make it NO
| |
420 default: NOTREACHED(); | |
421 } | |
422 } | |
423 | |
424 template <typename InputIterator> | |
425 void DownloadQuery::Search(InputIterator iter, const InputIterator last, | |
426 DownloadQuery::DownloadVector* results) const { | |
427 results->clear(); | |
428 for (; iter != last; ++iter) { | |
429 if (Matches(**iter)) | |
430 results->push_back(*iter); | |
431 } | |
432 if (!sort_fields_.empty()) | |
433 std::partial_sort(results->begin(), | |
434 results->begin() + std::min(limit_, results->size()), | |
435 results->end(), | |
436 DownloadComparator(sort_fields_)); | |
437 if (results->size() > limit_) | |
438 results->resize(limit_); | |
439 } | |
440 | |
441 template void DownloadQuery::Search( | |
442 std::set<DownloadItem*>::const_iterator iter, | |
443 const std::set<DownloadItem*>::const_iterator last, | |
444 DownloadQuery::DownloadVector* results) const; | |
445 template void DownloadQuery::Search( | |
446 std::vector<DownloadItem*>::const_iterator iter, | |
447 const std::vector<DownloadItem*>::const_iterator last, | |
448 DownloadQuery::DownloadVector* results) const; | |
449 template void DownloadQuery::Search( | |
450 std::vector<DownloadItem*>::iterator iter, | |
451 const std::vector<DownloadItem*>::iterator last, | |
452 DownloadQuery::DownloadVector* results) const; | |
453 | |
OLD | NEW |