diff options
Diffstat (limited to 'content/browser/download/download_query.cc')
-rw-r--r-- | content/browser/download/download_query.cc | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/content/browser/download/download_query.cc b/content/browser/download/download_query.cc new file mode 100644 index 0000000..730da6a --- /dev/null +++ b/content/browser/download/download_query.cc @@ -0,0 +1,373 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/download/download_query.h" + +#include <algorithm> +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "base/string16.h" +#include "base/string_split.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "content/browser/download/download_item.h" +#include "googleurl/src/gurl.h" +#include "unicode/regex.h" + +namespace { + +// Templatized base::Value::GetAs*(). +template <typename T> bool GetAs(const base::Value& in, T* out); +template<> bool GetAs(const base::Value& in, bool* out) { + return in.GetAsBoolean(out); +} +template<> bool GetAs(const base::Value& in, int* out) { + return in.GetAsInteger(out); +} +template<> bool GetAs(const base::Value& in, std::string* out) { + return in.GetAsString(out); +} +template<> bool GetAs(const base::Value& in, string16* out) { + return in.GetAsString(out); +} + +// The next several functions are helpers for making Callbacks that access +// DownloadItem fields. + +static bool MatchesQuery(const string16& value, const DownloadItem& item) { + return item.MatchesQuery(value); +} + +static int GetStartTime(const DownloadItem& item) { + return (item.GetStartTime() - base::Time::UnixEpoch()).InMilliseconds(); +} + +static bool GetDangerAccepted(const DownloadItem& item) { + return (item.GetSafetyState() == DownloadItem::DANGEROUS_BUT_VALIDATED); +} + +static string16 GetFilename(const DownloadItem& item) { + // This filename will be compared with strings that could be passed in by the + // user, who only sees LossyDisplayNames. + return item.GetFullPath().LossyDisplayName(); +} + +static std::string GetFilenameUTF8(const DownloadItem& item) { + return UTF16ToUTF8(GetFilename(item)); +} + +static std::string GetUrl(const DownloadItem& item) { + return item.GetOriginalUrl().spec(); +} + +static DownloadItem::DownloadState GetState(const DownloadItem& item) { + return item.GetState(); +} + +static DownloadStateInfo::DangerType GetDangerType(const DownloadItem& item) { + return item.GetDangerType(); +} + +static int GetReceivedBytes(const DownloadItem& item) { + return item.GetReceivedBytes(); +} + +static int GetTotalBytes(const DownloadItem& item) { + return item.GetTotalBytes(); +} + +static std::string GetMimeType(const DownloadItem& item) { + return item.GetMimeType(); +} + +static bool IsPaused(const DownloadItem& item) { + return item.IsPaused(); +} + +// Wrap Callback to work around a bug in base::Bind/Callback where the inner +// callback is nullified when the outer callback is Run. +template<typename ValueType> +class InnerCallback { + public: + typedef base::Callback<ValueType(const DownloadItem&)> CallbackType; + + explicit InnerCallback(const CallbackType& inner) : inner_(inner) {} + ~InnerCallback() {} + + // Mimic Callback's interface to facilitate removing InnerCallback when the + // bug is fixed. + ValueType Run(const DownloadItem& item) const { return inner_.Run(item); } + + private: + CallbackType inner_; +}; + +enum ComparisonType {LT, EQ, GT}; + +// Returns true if |item| matches the filter specified by |value|, |cmptype|, +// and |accessor|. |accessor| is conceptually a function that takes a +// DownloadItem and returns one of its fields, which is then compared to +// |value|. +template<typename ValueType> +static bool FieldMatches( + const ValueType& value, + ComparisonType cmptype, + const InnerCallback<ValueType>& accessor, + const DownloadItem& item) { + switch (cmptype) { + case LT: return accessor.Run(item) < value; + case EQ: return accessor.Run(item) == value; + case GT: return accessor.Run(item) > value; + } + NOTREACHED(); + return false; +} + +// Helper for building a Callback to FieldMatches<>(). +template <typename ValueType> DownloadQuery::FilterCallback BuildFilter( + const base::Value& value, ComparisonType cmptype, + ValueType (*accessor)(const DownloadItem&)) { + ValueType cpp_value; + if (!GetAs(value, &cpp_value)) return DownloadQuery::FilterCallback(); + return base::Bind(&FieldMatches<ValueType>, cpp_value, cmptype, + InnerCallback<ValueType>(base::Bind(accessor))); +} + +// Returns true if |accessor.Run(item)| matches |pattern|. +static bool FindRegex( + icu::RegexPattern* pattern, + const InnerCallback<std::string>& accessor, + const DownloadItem& item) { + icu::UnicodeString input(accessor.Run(item).c_str()); + UErrorCode status = U_ZERO_ERROR; + scoped_ptr<icu::RegexMatcher> matcher(pattern->matcher(input, status)); + return matcher->find() == TRUE; // Ugh, VS complains bool != UBool. +} + +// Helper for building a Callback to FindRegex(). +DownloadQuery::FilterCallback BuildRegexFilter( + const base::Value& regex_value, + std::string (*accessor)(const DownloadItem&)) { + std::string regex_str; + if (!GetAs(regex_value, ®ex_str)) return DownloadQuery::FilterCallback(); + UParseError re_err; + UErrorCode re_status = U_ZERO_ERROR; + scoped_ptr<icu::RegexPattern> pattern(icu::RegexPattern::compile( + icu::UnicodeString::fromUTF8(regex_str), re_err, re_status)); + if (!U_SUCCESS(re_status)) return DownloadQuery::FilterCallback(); + return base::Bind(&FindRegex, base::Owned(pattern.release()), + InnerCallback<std::string>(base::Bind(accessor))); +} + +// Returns a ComparisonType to indicate whether a field in |left| is less than, +// greater than or equal to the same field in |right|. +template<typename ValueType> +static ComparisonType Compare( + const InnerCallback<ValueType>& accessor, + const DownloadItem& left, const DownloadItem& right) { + ValueType left_value = accessor.Run(left); + ValueType right_value = accessor.Run(right); + if (left_value > right_value) return GT; + if (left_value < right_value) return LT; + DCHECK_EQ(left_value, right_value); + return EQ; +} + +} // anonymous namespace + +DownloadQuery::DownloadQuery() + : limit_(kuint32max) { +} + +DownloadQuery::~DownloadQuery() { +} + +// AddFilter() pushes a new FilterCallback to filters_. Most FilterCallbacks are +// Callbacks to FieldMatches<>(). Search() iterates over given DownloadItems, +// discarding items for which any filter returns false. A DownloadQuery may have +// zero or more FilterCallbacks. + +bool DownloadQuery::AddFilter(const DownloadQuery::FilterCallback& value) { + if (value.is_null()) return false; + filters_.push_back(value); + return true; +} + +void DownloadQuery::AddFilter(DownloadItem::DownloadState state) { + AddFilter(base::Bind(&FieldMatches<DownloadItem::DownloadState>, state, EQ, + InnerCallback<DownloadItem::DownloadState>(base::Bind(&GetState)))); +} + +void DownloadQuery::AddFilter(DownloadStateInfo::DangerType danger) { + AddFilter(base::Bind(&FieldMatches<DownloadStateInfo::DangerType>, danger, EQ, + InnerCallback<DownloadStateInfo::DangerType>(base::Bind( + &GetDangerType)))); +} + +bool DownloadQuery::AddFilter(DownloadQuery::FilterType type, + const base::Value& value) { + switch (type) { + case FILTER_BYTES_RECEIVED: + return AddFilter(BuildFilter<int>(value, EQ, &GetReceivedBytes)); + case FILTER_DANGER_ACCEPTED: + return AddFilter(BuildFilter<bool>(value, EQ, &GetDangerAccepted)); + case FILTER_FILENAME: + return AddFilter(BuildFilter<string16>(value, EQ, &GetFilename)); + case FILTER_FILENAME_REGEX: + return AddFilter(BuildRegexFilter(value, &GetFilenameUTF8)); + case FILTER_MIME: + return AddFilter(BuildFilter<std::string>(value, EQ, &GetMimeType)); + case FILTER_PAUSED: + return AddFilter(BuildFilter<bool>(value, EQ, &IsPaused)); + case FILTER_QUERY: { + string16 query; + return GetAs(value, &query) && + AddFilter(base::Bind(&MatchesQuery, query)); + } + case FILTER_STARTED_AFTER: + return AddFilter(BuildFilter<int>(value, GT, &GetStartTime)); + case FILTER_STARTED_BEFORE: + return AddFilter(BuildFilter<int>(value, LT, &GetStartTime)); + case FILTER_START_TIME: + return AddFilter(BuildFilter<int>(value, EQ, &GetStartTime)); + case FILTER_TOTAL_BYTES: + return AddFilter(BuildFilter<int>(value, EQ, &GetTotalBytes)); + case FILTER_TOTAL_BYTES_GREATER: + return AddFilter(BuildFilter<int>(value, GT, &GetTotalBytes)); + case FILTER_TOTAL_BYTES_LESS: + return AddFilter(BuildFilter<int>(value, LT, &GetTotalBytes)); + case FILTER_URL: + return AddFilter(BuildFilter<std::string>(value, EQ, &GetUrl)); + case FILTER_URL_REGEX: + return AddFilter(BuildRegexFilter(value, &GetUrl)); + } + return false; +} + +bool DownloadQuery::Matches(const DownloadItem& item) const { + for (FilterCallbackVector::const_iterator filter = filters_.begin(); + filter != filters_.end(); ++filter) { + if (!filter->Run(item)) + return false; + } + return true; +} + +// AddSorter() creates a Sorter and pushes it onto sorters_. A Sorter is a +// direction and a Callback to Compare<>(). After filtering, Search() makes a +// DownloadComparator functor from the sorters_ and passes the +// DownloadComparator to std::partial_sort. std::partial_sort calls the +// DownloadComparator with different pairs of DownloadItems. DownloadComparator +// iterates over the sorters until a callback returns ComparisonType LT or GT. +// DownloadComparator returns true or false depending on that ComparisonType and +// the sorter's direction in order to indicate to std::partial_sort whether the +// left item is after or before the right item. If all sorters return EQ, then +// DownloadComparator compares GetId. A DownloadQuery may have zero or more +// Sorters, but there is one DownloadComparator per call to Search(). + +struct DownloadQuery::Sorter { + typedef base::Callback<ComparisonType( + const DownloadItem&, const DownloadItem&)> SortType; + + template<typename ValueType> + static Sorter Build(DownloadQuery::SortDirection adirection, + ValueType (*accessor)(const DownloadItem&)) { + return Sorter(adirection, base::Bind(&Compare<ValueType>, + InnerCallback<ValueType>(base::Bind(accessor)))); + } + + Sorter(DownloadQuery::SortDirection adirection, + const SortType& asorter) + : direction(adirection), + sorter(asorter) { + } + ~Sorter() {} + + DownloadQuery::SortDirection direction; + SortType sorter; +}; + +class DownloadQuery::DownloadComparator { + public: + DownloadComparator(const DownloadQuery::SorterVector& terms) + : terms_(terms) { + } + + // Returns true if |left| sorts before |right|. + bool operator() (const DownloadItem* left, const DownloadItem* right); + + private: + const DownloadQuery::SorterVector& terms_; + + // std::sort requires this class to be copyable. +}; + +bool DownloadQuery::DownloadComparator::operator() ( + const DownloadItem* left, const DownloadItem* right) { + for (DownloadQuery::SorterVector::const_iterator term = terms_.begin(); + term != terms_.end(); ++term) { + switch (term->sorter.Run(*left, *right)) { + case LT: return term->direction == DownloadQuery::ASCENDING; + case GT: return term->direction == DownloadQuery::DESCENDING; + case EQ: break; // break the switch but not the loop + } + } + CHECK_NE(left->GetId(), right->GetId()); + return left->GetId() < right->GetId(); +} + +void DownloadQuery::AddSorter(DownloadQuery::SortType type, + DownloadQuery::SortDirection direction) { + switch (type) { + case SORT_START_TIME: + sorters_.push_back(Sorter::Build<int>(direction, &GetStartTime)); + break; + case SORT_URL: + sorters_.push_back(Sorter::Build<std::string>(direction, &GetUrl)); + break; + case SORT_FILENAME: + sorters_.push_back(Sorter::Build<string16>(direction, &GetFilename)); + break; + case SORT_DANGER: + sorters_.push_back(Sorter::Build<DownloadStateInfo::DangerType>( + direction, &GetDangerType)); + break; + case SORT_DANGER_ACCEPTED: + sorters_.push_back(Sorter::Build<bool>(direction, &GetDangerAccepted)); + break; + case SORT_STATE: + sorters_.push_back(Sorter::Build<DownloadItem::DownloadState>( + direction, &GetState)); + break; + case SORT_PAUSED: + sorters_.push_back(Sorter::Build<bool>(direction, &IsPaused)); + break; + case SORT_MIME: + sorters_.push_back(Sorter::Build<std::string>(direction, &GetMimeType)); + break; + case SORT_BYTES_RECEIVED: + sorters_.push_back(Sorter::Build<int>(direction, &GetReceivedBytes)); + break; + case SORT_TOTAL_BYTES: + sorters_.push_back(Sorter::Build<int>(direction, &GetTotalBytes)); + break; + } +} + +void DownloadQuery::FinishSearch(DownloadQuery::DownloadVector* results) const { + if (!sorters_.empty()) + std::partial_sort(results->begin(), + results->begin() + std::min(limit_, results->size()), + results->end(), + DownloadComparator(sorters_)); + if (results->size() > limit_) + results->resize(limit_); +} |