summaryrefslogtreecommitdiffstats
path: root/content/browser/download/download_query.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/download/download_query.cc')
-rw-r--r--content/browser/download/download_query.cc373
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, &regex_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_);
+}