Index: chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc |
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc |
index b829960180fad95137d5d39a9559e24f17d4a9b3..0f1205de0c62ebf34cdc32834e8b4cd53fd7edd0 100644 |
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc |
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc |
@@ -28,21 +28,55 @@ namespace safe_browsing { |
namespace { |
+// Overloaded function to access members of either a DownloadRow or a |
+// ClientIncidentReport_DownloadDetails instance. |
mattm
2014/10/24 02:11:10
what function?
|
+ |
+// history::DownloadRow. |
mattm
2014/10/24 02:11:10
what?
|
+ |
+// Returns the end time of a download. |
+int64 GetEndTime(const history::DownloadRow& row) { |
+ return row.end_time.ToJavaTime(); |
+} |
+ |
+// Returns true if a download was of a binary file. |
+bool IsBinaryDownload(const history::DownloadRow& row) { |
+ // TODO(grt): Peek into archives to see if they contain binaries; |
+ // http://crbug.com/386915. |
+ return (download_protection_util::IsBinaryFile(row.target_path) && |
+ !download_protection_util::IsArchiveFile(row.target_path)); |
+} |
+ |
+// Returns true if a download has been opened. |
+bool HasBeenOpened(const history::DownloadRow& row) { |
+ return row.opened; |
+} |
+ |
+// ClientIncidentReport_DownloadDetails. |
mattm
2014/10/24 02:11:10
what?.. oh. These comments aren't really clear..
grt (UTC plus 2)
2014/10/30 19:07:35
Modified comments and re-ordered the functions for
|
+ |
+// Returns the end time of a download. |
+int64 GetEndTime(const ClientIncidentReport_DownloadDetails& details) { |
+ return details.download_time_msec(); |
+} |
+ |
+// Returns true if a download was of a binary file. |
+bool IsBinaryDownload(const ClientIncidentReport_DownloadDetails& details) { |
+ return true; |
+} |
+ |
+// Returns true if a download has been opened. |
+bool HasBeenOpened(const ClientIncidentReport_DownloadDetails& details) { |
+ return details.has_open_time_msec() && details.open_time_msec(); |
+} |
+ |
// Returns true if |first| is more recent than |second|, preferring opened over |
// non-opened for downloads that completed at the same time (extraordinarily |
// unlikely). Only files that look like some kind of executable are considered. |
-bool IsMoreInterestingThan(const history::DownloadRow& first, |
- const history::DownloadRow& second) { |
- if (first.end_time < second.end_time) |
+template <class A, class B> |
+bool IsMoreInterestingThan(const A& first, const B& second) { |
+ if (GetEndTime(first) < GetEndTime(second) || !IsBinaryDownload(first)) |
return false; |
- // TODO(grt): Peek into archives to see if they contain binaries; |
- // http://crbug.com/386915. |
- if (!download_protection_util::IsBinaryFile(first.target_path) || |
- download_protection_util::IsArchiveFile(first.target_path)) { |
- return false; |
- } |
- return (first.end_time != second.end_time || |
- (first.opened && !second.opened)); |
+ return (GetEndTime(first) != GetEndTime(second) || |
+ (HasBeenOpened(first) && !HasBeenOpened(second))); |
} |
// Returns a pointer to the most interesting completed download in |downloads|. |
@@ -60,9 +94,20 @@ const history::DownloadRow* FindMostInteresting( |
return most_recent_row; |
} |
+// Returns true if |candidate| is more interesting than whichever of |details| |
+// or |best_row| is present. |
+template <class D> |
+bool IsMostInteresting(const D& candidate, |
+ const ClientIncidentReport_DownloadDetails* details, |
+ const history::DownloadRow& best_row) { |
+ return details ? |
+ IsMoreInterestingThan(candidate, *details) : |
+ IsMoreInterestingThan(candidate, best_row); |
+} |
+ |
// Populates the |details| protobuf with information pertaining to |download|. |
-void PopulateDetails(const history::DownloadRow& download, |
- ClientIncidentReport_DownloadDetails* details) { |
+void PopulateDetailsFromRow(const history::DownloadRow& download, |
+ ClientIncidentReport_DownloadDetails* details) { |
ClientDownloadRequest* download_request = details->mutable_download(); |
download_request->set_url(download.url_chain.back().spec()); |
// digests is a required field, so force it to exist. |
@@ -103,9 +148,12 @@ LastDownloadFinder::~LastDownloadFinder() { |
// static |
scoped_ptr<LastDownloadFinder> LastDownloadFinder::Create( |
+ const DownloadDetailsGetter& download_details_getter, |
const LastDownloadCallback& callback) { |
scoped_ptr<LastDownloadFinder> finder(make_scoped_ptr(new LastDownloadFinder( |
- g_browser_process->profile_manager()->GetLoadedProfiles(), callback))); |
+ download_details_getter, |
+ g_browser_process->profile_manager()->GetLoadedProfiles(), |
+ callback))); |
// Return NULL if there is no work to do. |
if (finder->profiles_.empty()) |
return scoped_ptr<LastDownloadFinder>(); |
@@ -115,9 +163,13 @@ scoped_ptr<LastDownloadFinder> LastDownloadFinder::Create( |
LastDownloadFinder::LastDownloadFinder() : weak_ptr_factory_(this) { |
} |
-LastDownloadFinder::LastDownloadFinder(const std::vector<Profile*>& profiles, |
- const LastDownloadCallback& callback) |
- : callback_(callback), weak_ptr_factory_(this) { |
+LastDownloadFinder::LastDownloadFinder( |
+ const DownloadDetailsGetter& download_details_getter, |
+ const std::vector<Profile*>& profiles, |
+ const LastDownloadCallback& callback) |
+ : download_details_getter_(download_details_getter), |
+ callback_(callback), |
+ weak_ptr_factory_(this) { |
// Observe profile lifecycle events so that the finder can begin or abandon |
// the search in profiles while it is running. |
notification_registrar_.Add(this, |
@@ -148,31 +200,61 @@ void LastDownloadFinder::SearchInProfile(Profile* profile) { |
// Exit early if already processing this profile. This could happen if, for |
// example, NOTIFICATION_PROFILE_ADDED arrives after construction while |
// waiting for NOTIFICATION_HISTORY_LOADED. |
- if (std::find(profiles_.begin(), profiles_.end(), profile) != |
- profiles_.end()) { |
+ if (profiles_.count(profile)) |
return; |
- } |
- HistoryService* history_service = |
- HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS); |
- // No history service is returned for profiles that do not save history. |
- if (!history_service) |
+ // Initiate a metadata search. |
+ profiles_[profile] = WAITING_FOR_METADATA; |
mattm
2014/10/24 02:11:10
this is confusing now.. maybe the variable should
grt (UTC plus 2)
2014/10/30 19:07:35
Done.
|
+ download_details_getter_.Run(profile, |
+ base::Bind(&LastDownloadFinder::OnMetadataQuery, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ profile)); |
+} |
+ |
+void LastDownloadFinder::OnMetadataQuery( |
+ Profile* profile, |
+ scoped_ptr<ClientIncidentReport_DownloadDetails> details) { |
+ auto iter = profiles_.find(profile); |
+ // Early-exit if the search for this profile was abandoned. |
+ if (iter == profiles_.end()) |
return; |
- profiles_.push_back(profile); |
- if (history_service->BackendLoaded()) { |
- history_service->QueryDownloads( |
- base::Bind(&LastDownloadFinder::OnDownloadQuery, |
- weak_ptr_factory_.GetWeakPtr(), |
- profile)); |
- } // else wait until history is loaded. |
+ if (details) { |
+ if (IsMostInteresting(*details, details_.get(), most_recent_row_)) { |
+ details_ = details.Pass(); |
+ most_recent_row_.end_time = base::Time(); |
+ } |
+ |
+ RemoveProfileAndReportIfDone(iter); |
+ } else { |
+ // Search history since no metadata was found. |
+ iter->second = WAITING_FOR_HISTORY; |
+ HistoryService* history_service = |
+ HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS); |
+ // No history service is returned for profiles that do not save history. |
+ if (!history_service) { |
+ RemoveProfileAndReportIfDone(iter); |
+ return; |
+ } |
+ if (history_service->BackendLoaded()) { |
+ history_service->QueryDownloads( |
+ base::Bind(&LastDownloadFinder::OnDownloadQuery, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ profile)); |
+ } // else wait until history is loaded. |
+ } |
} |
void LastDownloadFinder::OnProfileHistoryLoaded( |
Profile* profile, |
HistoryService* history_service) { |
- if (std::find(profiles_.begin(), profiles_.end(), profile) != |
- profiles_.end()) { |
+ auto iter = profiles_.find(profile); |
+ if (iter == profiles_.end()) |
+ return; |
+ |
+ // Start the query in the history service if the finder was waiting for the |
+ // service to load. |
+ if (iter->second == WAITING_FOR_HISTORY) { |
history_service->QueryDownloads( |
base::Bind(&LastDownloadFinder::OnDownloadQuery, |
weak_ptr_factory_.GetWeakPtr(), |
@@ -182,56 +264,60 @@ void LastDownloadFinder::OnProfileHistoryLoaded( |
void LastDownloadFinder::AbandonSearchInProfile(Profile* profile) { |
// |profile| may not be present in the set of profiles. |
- std::vector<Profile*>::iterator it = |
- std::find(profiles_.begin(), profiles_.end(), profile); |
- if (it != profiles_.end()) |
- RemoveProfileAndReportIfDone(it); |
+ auto iter = profiles_.find(profile); |
+ if (iter != profiles_.end()) |
+ RemoveProfileAndReportIfDone(iter); |
} |
void LastDownloadFinder::OnDownloadQuery( |
Profile* profile, |
scoped_ptr<std::vector<history::DownloadRow> > downloads) { |
// Early-exit if the history search for this profile was abandoned. |
- std::vector<Profile*>::iterator it = |
- std::find(profiles_.begin(), profiles_.end(), profile); |
- if (it == profiles_.end()) |
+ auto iter = profiles_.find(profile); |
+ if (iter == profiles_.end()) |
return; |
// Find the most recent from this profile and use it if it's better than |
// anything else found so far. |
const history::DownloadRow* profile_best = FindMostInteresting(*downloads); |
- if (profile_best && IsMoreInterestingThan(*profile_best, most_recent_row_)) |
+ if (profile_best && |
+ IsMostInteresting(*profile_best, details_.get(), most_recent_row_)) { |
+ details_.reset(); |
most_recent_row_ = *profile_best; |
+ } |
- RemoveProfileAndReportIfDone(it); |
+ RemoveProfileAndReportIfDone(iter); |
} |
void LastDownloadFinder::RemoveProfileAndReportIfDone( |
- std::vector<Profile*>::iterator it) { |
- DCHECK(it != profiles_.end()); |
- |
- *it = profiles_.back(); |
- profiles_.resize(profiles_.size() - 1); |
+ std::map<Profile*, ProfileWaitState>::iterator iter) { |
+ DCHECK(iter != profiles_.end()); |
+ profiles_.erase(iter); |
// Finish processing if all results are in. |
if (profiles_.empty()) |
ReportResults(); |
- // Do not touch this instance after reporting results. |
+ // Do not touch this LastDownloadFinder after reporting results. |
} |
void LastDownloadFinder::ReportResults() { |
DCHECK(profiles_.empty()); |
- if (most_recent_row_.end_time.is_null()) { |
- callback_.Run(scoped_ptr<ClientIncidentReport_DownloadDetails>()); |
- // Do not touch this instance after running the callback, since it may have |
- // been deleted. |
- } else { |
+ if (details_) { |
+ callback_.Run(make_scoped_ptr(new ClientIncidentReport_DownloadDetails( |
+ *details_)).Pass()); |
+ // Do not touch this LastDownloadFinder after running the callback, since it |
+ // may have been deleted. |
+ } else if (!most_recent_row_.end_time.is_null()) { |
scoped_ptr<ClientIncidentReport_DownloadDetails> details( |
new ClientIncidentReport_DownloadDetails()); |
- PopulateDetails(most_recent_row_, details.get()); |
+ PopulateDetailsFromRow(most_recent_row_, details.get()); |
callback_.Run(details.Pass()); |
- // Do not touch this instance after running the callback, since it may have |
- // been deleted. |
+ // Do not touch this LastDownloadFinder after running the callback, since it |
+ // may have been deleted. |
+ } else { |
+ callback_.Run(scoped_ptr<ClientIncidentReport_DownloadDetails>()); |
+ // Do not touch this LastDownloadFinder after running the callback, since it |
+ // may have been deleted. |
} |
} |