| Index: chrome/browser/download/download_query.cc | 
| diff --git a/chrome/browser/download/download_query.cc b/chrome/browser/download/download_query.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..2f540bec3533613d303b2eb83e2c3454725b46bb | 
| --- /dev/null | 
| +++ b/chrome/browser/download/download_query.cc | 
| @@ -0,0 +1,366 @@ | 
| +// 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 "chrome/browser/download/download_query.h" | 
| + | 
| +#include <string> | 
| +#include <vector> | 
| + | 
| +#include "base/memory/scoped_ptr.h" | 
| +#include "base/stl_util-inl.h" | 
| +#include "base/string16.h" | 
| +#include "base/string_split.h" | 
| +#include "base/values.h" | 
| +#include "unicode/regex.h" | 
| +#include "chrome/browser/download/download_item.h" | 
| + | 
| +namespace { | 
| +typedef std::vector<std::string> Strings; | 
| +typedef std::vector<DownloadItem*> DownloadItems; | 
| + | 
| +struct SimpleDownloadItem { | 
| +  static const char STATE_IN_PROGRESS[]; | 
| +  static const char STATE_COMPLETE[]; | 
| +  static const char STATE_INTERRUPTED[]; | 
| +  static const char DANGER_SAFE[]; | 
| +  static const char DANGER_FILE[]; | 
| +  static const char DANGER_URL[]; | 
| + | 
| +  explicit SimpleDownloadItem(DownloadItem* download) | 
| +    : item(download) { | 
| +    CHECK(item); | 
| +  } | 
| + | 
| +  int id() const { return item->id(); } | 
| +  std::string url() const { return item->original_url().spec(); } | 
| +  std::string filename() const { | 
| +    return item->GetFileNameToReportUser().value(); | 
| +  } | 
| +  std::string danger() const { return DangerString(item->GetDangerType()); } | 
| +  bool dangerAccepted() const { | 
| +    return item->safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED; | 
| +  } | 
| +  std::string state() const { return StateString(item->state()); } | 
| +  bool paused() const { return item->is_paused(); } | 
| +  std::string mime() const { return item->mime_type(); } | 
| +  int startTime() const { | 
| +    return (item->start_time() - base::Time::UnixEpoch()).InMilliseconds(); | 
| +  } | 
| +  int endTime() const { return -1; } | 
| +  int bytesReceived() const { return item->received_bytes(); } | 
| +  int totalBytes() const { return item->total_bytes(); } | 
| +  int fileSize() const { return -1; } | 
| +  int error() const { return 0; } | 
| + | 
| +  DictionaryValue* ToJSON() const; | 
| + | 
| +  static std::string DangerString(DownloadItem::DangerType danger) { | 
| +    switch (danger) { | 
| +      case DownloadItem::NOT_DANGEROUS: return DANGER_SAFE; | 
| +      case DownloadItem::DANGEROUS_FILE: return DANGER_FILE; | 
| +      case DownloadItem::DANGEROUS_URL: return DANGER_URL; | 
| +      case DownloadItem::DANGEROUS_TYPE_MAX: | 
| +      default: | 
| +        NOTREACHED(); | 
| +        return ""; | 
| +    } | 
| +  } | 
| + | 
| +  static std::string StateString(DownloadItem::DownloadState state) { | 
| +    switch (state) { | 
| +      case DownloadItem::IN_PROGRESS: return STATE_IN_PROGRESS; | 
| +      case DownloadItem::COMPLETE: return STATE_COMPLETE; | 
| +      case DownloadItem::INTERRUPTED:  // fall through | 
| +      case DownloadItem::CANCELLED: return STATE_INTERRUPTED; | 
| +      case DownloadItem::REMOVING: | 
| +      case DownloadItem::MAX_DOWNLOAD_STATE: | 
| +      default: | 
| +        NOTREACHED(); | 
| +        return ""; | 
| +    } | 
| +  } | 
| + | 
| +  DownloadItem* item; | 
| +  // Allow copy and assign. | 
| +}; | 
| + | 
| +typedef std::vector<SimpleDownloadItem*> SimpleItems; | 
| + | 
| +// Functor passed to std::sort to sort SimpleDownloadItems. | 
| +class DownloadComparator { | 
| + public: | 
| +  DownloadComparator(std::string* error_msg, Strings* order_terms) | 
| +    : error_msg_(error_msg), | 
| +      order_terms_(order_terms) { | 
| +  } | 
| + | 
| +  bool operator() (const SimpleDownloadItem* left, | 
| +                   const SimpleDownloadItem* right); | 
| + private: | 
| +  static const char kDash = '-'; | 
| + | 
| +  template <typename FieldType> bool CompareField( | 
| +      std::string term, | 
| +      std::string field, | 
| +      FieldType left, | 
| +      FieldType right, | 
| +      bool* matched_term, | 
| +      bool* cmp) const; | 
| + | 
| +  std::string* error_msg_; | 
| +  Strings* order_terms_; | 
| +  // std::sort requires this class to be copyable. | 
| +}; | 
| + | 
| +using download_util::DownloadQuery; | 
| + | 
| +class DownloadSearch : DownloadQuery { | 
| + public: | 
| +  DownloadSearch(const DownloadQuery& query, DownloadItems* items); | 
| +  ~DownloadSearch() {} | 
| + | 
| +  bool IsWellFormed(std::string* error_msg); | 
| +  bool Matches(const SimpleDownloadItem& item); | 
| + | 
| +  SimpleItems::const_iterator begin() const { return simple_items_.begin(); } | 
| +  SimpleItems::const_iterator end() const { return simple_items_.end(); } | 
| + | 
| +  bool ReachedLimit(int num_results) { | 
| +    return has_limit_ && (num_results >= limit_); | 
| +  } | 
| + | 
| + private: | 
| +  std::string error_msg_; | 
| +  Strings order_terms_; | 
| +  SimpleItems simple_items_; | 
| +  STLElementDeleter<SimpleItems> delete_simple_items_; | 
| +  Strings query_terms_; | 
| +  scoped_ptr<icu::RegexPattern> filename_pattern_; | 
| +  scoped_ptr<icu::RegexPattern> url_pattern_; | 
| +  DownloadComparator comparator_; | 
| + | 
| +  DISALLOW_COPY_AND_ASSIGN(DownloadSearch); | 
| +}; | 
| + | 
| +DictionaryValue* SimpleDownloadItem::ToJSON() const { | 
| +  DictionaryValue* json = new DictionaryValue(); | 
| +#define FIELD(name, type) \ | 
| +  json->Set ## type(#name, name()); | 
| +#include "chrome/browser/download/simple_download_item_fields.h" | 
| +#undef FIELD | 
| +  return json; | 
| +} | 
| + | 
| +bool DownloadComparator::operator() ( | 
| +    const SimpleDownloadItem* left, | 
| +    const SimpleDownloadItem* right) { | 
| +  CHECK(left); | 
| +  CHECK(right); | 
| +  Strings::iterator order_term, | 
| +    last_order_term = order_terms_->end(); | 
| +  for (order_term = order_terms_->begin(); | 
| +        order_term != last_order_term; ++order_term) { | 
| +    bool matched_term = false; | 
| +    bool cmp = false; | 
| +#define Integer int | 
| +#define Boolean bool | 
| +#define String std::string | 
| +#define FIELD(name, type) \ | 
| +    if (CompareField<type>(*order_term, #name, left->name(), right->name(), \ | 
| +          &matched_term, &cmp)) return cmp; \ | 
| +    if (matched_term) continue; | 
| +#include "chrome/browser/download/simple_download_item_fields.h" | 
| +#undef FIELD | 
| +#undef String | 
| +#undef Boolean | 
| +#undef Integer | 
| +    *error_msg_ += " " + *order_term; | 
| +    order_terms_->erase(order_term); | 
| +  } | 
| +  CHECK_NE(left->id(), right->id()); | 
| +  return left->id() < right->id(); | 
| +} | 
| + | 
| +// Return true and set cmp if matched_term and left != right. | 
| +template <typename FieldType> | 
| +bool DownloadComparator::CompareField( | 
| +    std::string term, | 
| +    std::string field, | 
| +    FieldType left, | 
| +    FieldType right, | 
| +    bool* matched_term, | 
| +    bool* cmp) const { | 
| +  bool diff = (left != right); | 
| +  if (term == field) { | 
| +    *matched_term = true; | 
| +    if (diff) *cmp = (left < right); | 
| +  } | 
| +  if (term == kDash + field) { | 
| +    *matched_term = true; | 
| +    if (diff) *cmp = (left > right); | 
| +  } | 
| +  return diff && *matched_term; | 
| +} | 
| + | 
| +// NOTE These string constants must match those in | 
| +// chrome/common/extensions/api/extension_api.json | 
| +const char SimpleDownloadItem::STATE_IN_PROGRESS[] = "in progress"; | 
| +const char SimpleDownloadItem::STATE_COMPLETE[] = "complete"; | 
| +const char SimpleDownloadItem::STATE_INTERRUPTED[] = "interrupted"; | 
| +const char SimpleDownloadItem::DANGER_SAFE[] = "safe"; | 
| +const char SimpleDownloadItem::DANGER_FILE[] = "file"; | 
| +const char SimpleDownloadItem::DANGER_URL[] = "url"; | 
| + | 
| +DownloadSearch::DownloadSearch( | 
| +    const DownloadQuery& query, DownloadItems* items) | 
| +  : DownloadQuery(query), | 
| +    delete_simple_items_(&simple_items_), | 
| +    comparator_(&error_msg_, &order_terms_) { | 
| +  if (has_query_) { | 
| +    base::SplitString(query_, ' ', &query_terms_);  // TODO handle quopri | 
| +  } | 
| +  if (has_filenameRegex_) { | 
| +    UParseError regex_error;  // TODO do these need to outlive the pattern? | 
| +    UErrorCode regex_status = U_ZERO_ERROR; | 
| +    filename_pattern_.reset(icu::RegexPattern::compile( | 
| +          icu::UnicodeString::fromUTF8(filenameRegex_), | 
| +          regex_error, regex_status)); | 
| +    if (!U_SUCCESS(regex_status)) { | 
| +      error_msg_ = "bad filenameRegex"; | 
| +      return; | 
| +    } | 
| +  } | 
| +  if (has_urlRegex_) { | 
| +    UParseError regex_error; | 
| +    UErrorCode regex_status = U_ZERO_ERROR; | 
| +    url_pattern_.reset(icu::RegexPattern::compile( | 
| +          icu::UnicodeString::fromUTF8(urlRegex_), | 
| +          regex_error, regex_status)); | 
| +    if (!U_SUCCESS(regex_status)) { | 
| +      error_msg_ = "bad urlRegex"; | 
| +      return; | 
| +    } | 
| +  } | 
| +  for (DownloadItems::const_iterator iter = items->begin(); | 
| +       iter != items->end(); ++iter) { | 
| +    if (*iter != NULL) { | 
| +      if (!filter_func_.is_null() && !filter_func_.Run(**iter)) continue; | 
| +      simple_items_.push_back(new SimpleDownloadItem(*iter)); | 
| +    } | 
| +  } | 
| +  if (has_orderBy_ && (1 < simple_items_.size())) { | 
| +    base::SplitString(orderBy_, ' ', &order_terms_); | 
| +    std::sort(simple_items_.begin(), simple_items_.end(), comparator_); | 
| +  } | 
| +} | 
| + | 
| +bool DownloadSearch::IsWellFormed(std::string* error_msg) { | 
| +  if (error_msg != NULL) *error_msg = error_msg_; | 
| +  return error_msg_.empty(); | 
| +} | 
| + | 
| +bool DownloadSearch::Matches(const SimpleDownloadItem& item) { | 
| +#define FIELD(name, unused_type) \ | 
| +  if (has_ ## name ## _ && (item.name() != name ## _)) return false; | 
| +#include "chrome/browser/download/simple_download_item_fields.h" | 
| +#undef FIELD | 
| +  if (has_state_enum_ && (item.item->state() != state_enum_)) return false; | 
| +  if (has_danger_enum_ && (item.item->GetDangerType() != danger_enum_)) | 
| +    return false; | 
| +  if (has_startedBefore_ && (item.startTime() > startedBefore_)) return false; | 
| +  if (has_startedAfter_ && (item.startTime() < startedAfter_)) return false; | 
| +  if (has_endedBefore_ && (item.endTime() > endedBefore_)) return false; | 
| +  if (has_endedAfter_ && (item.endTime() < endedAfter_)) return false; | 
| +  if (has_totalBytesGreater_ && (item.totalBytes() < totalBytesGreater_)) | 
| +    return false; | 
| +  if (has_totalBytesLess_ && (item.totalBytes() > totalBytesLess_)) | 
| +    return false; | 
| +  Strings::const_iterator query_term_iter, | 
| +    query_term_end = query_terms_.end(); | 
| +  for (query_term_iter = query_terms_.begin(); | 
| +        query_term_iter != query_term_end; ++query_term_iter) { | 
| +    std::string query_term = *query_term_iter; | 
| +    if ((std::string::npos == item.filename().find(query_term)) && | 
| +        (std::string::npos == item.url().find(query_term))) { | 
| +      return false; | 
| +    } | 
| +  } | 
| +  if (filename_pattern_.get() != NULL) { | 
| +    UErrorCode status = U_ZERO_ERROR; | 
| +    scoped_ptr<icu::RegexMatcher> filename_matcher(filename_pattern_->matcher( | 
| +          icu::UnicodeString::fromUTF8(item.filename()), status)); | 
| +    if (U_FAILURE(status)) { | 
| +      return false; | 
| +    } | 
| +    if (!filename_matcher->find(0, status)) return false; | 
| +  } | 
| +  if (url_pattern_.get() != NULL) { | 
| +    UErrorCode status = U_ZERO_ERROR; | 
| +    scoped_ptr<icu::RegexMatcher> url_matcher(url_pattern_->matcher( | 
| +          icu::UnicodeString::fromUTF8(item.url()), status)); | 
| +    if (U_FAILURE(status)) { | 
| +      return false; | 
| +    } | 
| +    if (!url_matcher->find(0, status)) return false; | 
| +  } | 
| +  return true; | 
| +} | 
| +}  // anonymous namespace | 
| + | 
| +namespace download_util { | 
| + | 
| +DownloadQuery::DownloadQuery() { | 
| +  has_state_enum_ = false; | 
| +  has_danger_enum_ = false; | 
| +#define FIELD(name, type) \ | 
| +  has_ ## name ## _ = false; | 
| +#include "chrome/browser/download/download_query_fields.h" | 
| +#undef FIELD | 
| +} | 
| + | 
| +DownloadQuery::DownloadQuery(const DictionaryValue& json) { | 
| +  has_state_enum_ = false; | 
| +  has_danger_enum_ = false; | 
| +#define FIELD(name, type) \ | 
| +  has_ ## name ## _ = json.Get ## type(#name, &name ## _); | 
| +#include "chrome/browser/download/download_query_fields.h" | 
| +#undef FIELD | 
| +} | 
| + | 
| +DownloadQuery& DownloadQuery::filter_func(DownloadQuery::FilterType func) { | 
| +  filter_func_ = func; | 
| +  return *this; | 
| +} | 
| + | 
| +DownloadQuery& DownloadQuery::state_enum( | 
| +    DownloadItem::DownloadState stateenum) { | 
| +  has_state_enum_ = true; | 
| +  state_enum_ = stateenum; | 
| +  return *this; | 
| +} | 
| + | 
| +DownloadQuery& DownloadQuery::danger_enum(DownloadItem::DangerType dangerenum) { | 
| +  has_danger_enum_ = true; | 
| +  danger_enum_ = dangerenum; | 
| +  return *this; | 
| +} | 
| + | 
| +bool DownloadQuery::Search( | 
| +    std::vector<DownloadItem*>* items, | 
| +    std::string* error_msg, | 
| +    ListValue* results) const { | 
| +  DownloadSearch search(*this, items); | 
| +  items->clear(); | 
| +  if (!search.IsWellFormed(error_msg)) return false; | 
| +  for (SimpleItems::const_iterator iter = search.begin(); | 
| +       iter != search.end(); ++iter) { | 
| +    if (!search.Matches(**iter)) continue; | 
| +    items->push_back((*iter)->item); | 
| +    if (results != NULL) results->Append((*iter)->ToJSON()); | 
| +    if (search.ReachedLimit(items->size())) break; | 
| +  } | 
| +  return true; | 
| +} | 
| +}  // namespace download_util | 
| + | 
|  |