| Index: content/browser/download/download_query.cc
|
| diff --git a/content/browser/download/download_query.cc b/content/browser/download/download_query.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..730da6a7cd96aabc515a1be72fc6b944e49db965
|
| --- /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_);
|
| +}
|
|
|