OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/safe_browsing/incident_reporting/last_download_finder.h " | 5 #include "chrome/browser/safe_browsing/incident_reporting/last_download_finder.h " |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <functional> | 8 #include <functional> |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
(...skipping 10 matching lines...) Expand all Loading... | |
21 #include "chrome/common/safe_browsing/download_protection_util.h" | 21 #include "chrome/common/safe_browsing/download_protection_util.h" |
22 #include "content/public/browser/download_item.h" | 22 #include "content/public/browser/download_item.h" |
23 #include "content/public/browser/notification_details.h" | 23 #include "content/public/browser/notification_details.h" |
24 #include "content/public/browser/notification_service.h" | 24 #include "content/public/browser/notification_service.h" |
25 #include "content/public/browser/notification_source.h" | 25 #include "content/public/browser/notification_source.h" |
26 | 26 |
27 namespace safe_browsing { | 27 namespace safe_browsing { |
28 | 28 |
29 namespace { | 29 namespace { |
30 | 30 |
31 // Overloaded function to access members of either a DownloadRow or a | |
32 // ClientIncidentReport_DownloadDetails instance. | |
mattm
2014/10/24 02:11:10
what function?
| |
33 | |
34 // history::DownloadRow. | |
mattm
2014/10/24 02:11:10
what?
| |
35 | |
36 // Returns the end time of a download. | |
37 int64 GetEndTime(const history::DownloadRow& row) { | |
38 return row.end_time.ToJavaTime(); | |
39 } | |
40 | |
41 // Returns true if a download was of a binary file. | |
42 bool IsBinaryDownload(const history::DownloadRow& row) { | |
43 // TODO(grt): Peek into archives to see if they contain binaries; | |
44 // http://crbug.com/386915. | |
45 return (download_protection_util::IsBinaryFile(row.target_path) && | |
46 !download_protection_util::IsArchiveFile(row.target_path)); | |
47 } | |
48 | |
49 // Returns true if a download has been opened. | |
50 bool HasBeenOpened(const history::DownloadRow& row) { | |
51 return row.opened; | |
52 } | |
53 | |
54 // 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
| |
55 | |
56 // Returns the end time of a download. | |
57 int64 GetEndTime(const ClientIncidentReport_DownloadDetails& details) { | |
58 return details.download_time_msec(); | |
59 } | |
60 | |
61 // Returns true if a download was of a binary file. | |
62 bool IsBinaryDownload(const ClientIncidentReport_DownloadDetails& details) { | |
63 return true; | |
64 } | |
65 | |
66 // Returns true if a download has been opened. | |
67 bool HasBeenOpened(const ClientIncidentReport_DownloadDetails& details) { | |
68 return details.has_open_time_msec() && details.open_time_msec(); | |
69 } | |
70 | |
31 // Returns true if |first| is more recent than |second|, preferring opened over | 71 // Returns true if |first| is more recent than |second|, preferring opened over |
32 // non-opened for downloads that completed at the same time (extraordinarily | 72 // non-opened for downloads that completed at the same time (extraordinarily |
33 // unlikely). Only files that look like some kind of executable are considered. | 73 // unlikely). Only files that look like some kind of executable are considered. |
34 bool IsMoreInterestingThan(const history::DownloadRow& first, | 74 template <class A, class B> |
35 const history::DownloadRow& second) { | 75 bool IsMoreInterestingThan(const A& first, const B& second) { |
36 if (first.end_time < second.end_time) | 76 if (GetEndTime(first) < GetEndTime(second) || !IsBinaryDownload(first)) |
37 return false; | 77 return false; |
38 // TODO(grt): Peek into archives to see if they contain binaries; | 78 return (GetEndTime(first) != GetEndTime(second) || |
39 // http://crbug.com/386915. | 79 (HasBeenOpened(first) && !HasBeenOpened(second))); |
40 if (!download_protection_util::IsBinaryFile(first.target_path) || | |
41 download_protection_util::IsArchiveFile(first.target_path)) { | |
42 return false; | |
43 } | |
44 return (first.end_time != second.end_time || | |
45 (first.opened && !second.opened)); | |
46 } | 80 } |
47 | 81 |
48 // Returns a pointer to the most interesting completed download in |downloads|. | 82 // Returns a pointer to the most interesting completed download in |downloads|. |
49 const history::DownloadRow* FindMostInteresting( | 83 const history::DownloadRow* FindMostInteresting( |
50 const std::vector<history::DownloadRow>& downloads) { | 84 const std::vector<history::DownloadRow>& downloads) { |
51 const history::DownloadRow* most_recent_row = NULL; | 85 const history::DownloadRow* most_recent_row = NULL; |
52 for (size_t i = 0; i < downloads.size(); ++i) { | 86 for (size_t i = 0; i < downloads.size(); ++i) { |
53 const history::DownloadRow& row = downloads[i]; | 87 const history::DownloadRow& row = downloads[i]; |
54 // Ignore incomplete downloads. | 88 // Ignore incomplete downloads. |
55 if (row.state != content::DownloadItem::COMPLETE) | 89 if (row.state != content::DownloadItem::COMPLETE) |
56 continue; | 90 continue; |
57 if (!most_recent_row || IsMoreInterestingThan(row, *most_recent_row)) | 91 if (!most_recent_row || IsMoreInterestingThan(row, *most_recent_row)) |
58 most_recent_row = &row; | 92 most_recent_row = &row; |
59 } | 93 } |
60 return most_recent_row; | 94 return most_recent_row; |
61 } | 95 } |
62 | 96 |
97 // Returns true if |candidate| is more interesting than whichever of |details| | |
98 // or |best_row| is present. | |
99 template <class D> | |
100 bool IsMostInteresting(const D& candidate, | |
101 const ClientIncidentReport_DownloadDetails* details, | |
102 const history::DownloadRow& best_row) { | |
103 return details ? | |
104 IsMoreInterestingThan(candidate, *details) : | |
105 IsMoreInterestingThan(candidate, best_row); | |
106 } | |
107 | |
63 // Populates the |details| protobuf with information pertaining to |download|. | 108 // Populates the |details| protobuf with information pertaining to |download|. |
64 void PopulateDetails(const history::DownloadRow& download, | 109 void PopulateDetailsFromRow(const history::DownloadRow& download, |
65 ClientIncidentReport_DownloadDetails* details) { | 110 ClientIncidentReport_DownloadDetails* details) { |
66 ClientDownloadRequest* download_request = details->mutable_download(); | 111 ClientDownloadRequest* download_request = details->mutable_download(); |
67 download_request->set_url(download.url_chain.back().spec()); | 112 download_request->set_url(download.url_chain.back().spec()); |
68 // digests is a required field, so force it to exist. | 113 // digests is a required field, so force it to exist. |
69 // TODO(grt): Include digests in reports; http://crbug.com/389123. | 114 // TODO(grt): Include digests in reports; http://crbug.com/389123. |
70 ignore_result(download_request->mutable_digests()); | 115 ignore_result(download_request->mutable_digests()); |
71 download_request->set_length(download.received_bytes); | 116 download_request->set_length(download.received_bytes); |
72 for (size_t i = 0; i < download.url_chain.size(); ++i) { | 117 for (size_t i = 0; i < download.url_chain.size(); ++i) { |
73 const GURL& url = download.url_chain[i]; | 118 const GURL& url = download.url_chain[i]; |
74 ClientDownloadRequest_Resource* resource = | 119 ClientDownloadRequest_Resource* resource = |
75 download_request->add_resources(); | 120 download_request->add_resources(); |
(...skipping 20 matching lines...) Expand all Loading... | |
96 details->set_open_time_msec(download.end_time.ToJavaTime()); | 141 details->set_open_time_msec(download.end_time.ToJavaTime()); |
97 } | 142 } |
98 | 143 |
99 } // namespace | 144 } // namespace |
100 | 145 |
101 LastDownloadFinder::~LastDownloadFinder() { | 146 LastDownloadFinder::~LastDownloadFinder() { |
102 } | 147 } |
103 | 148 |
104 // static | 149 // static |
105 scoped_ptr<LastDownloadFinder> LastDownloadFinder::Create( | 150 scoped_ptr<LastDownloadFinder> LastDownloadFinder::Create( |
151 const DownloadDetailsGetter& download_details_getter, | |
106 const LastDownloadCallback& callback) { | 152 const LastDownloadCallback& callback) { |
107 scoped_ptr<LastDownloadFinder> finder(make_scoped_ptr(new LastDownloadFinder( | 153 scoped_ptr<LastDownloadFinder> finder(make_scoped_ptr(new LastDownloadFinder( |
108 g_browser_process->profile_manager()->GetLoadedProfiles(), callback))); | 154 download_details_getter, |
155 g_browser_process->profile_manager()->GetLoadedProfiles(), | |
156 callback))); | |
109 // Return NULL if there is no work to do. | 157 // Return NULL if there is no work to do. |
110 if (finder->profiles_.empty()) | 158 if (finder->profiles_.empty()) |
111 return scoped_ptr<LastDownloadFinder>(); | 159 return scoped_ptr<LastDownloadFinder>(); |
112 return finder.Pass(); | 160 return finder.Pass(); |
113 } | 161 } |
114 | 162 |
115 LastDownloadFinder::LastDownloadFinder() : weak_ptr_factory_(this) { | 163 LastDownloadFinder::LastDownloadFinder() : weak_ptr_factory_(this) { |
116 } | 164 } |
117 | 165 |
118 LastDownloadFinder::LastDownloadFinder(const std::vector<Profile*>& profiles, | 166 LastDownloadFinder::LastDownloadFinder( |
119 const LastDownloadCallback& callback) | 167 const DownloadDetailsGetter& download_details_getter, |
120 : callback_(callback), weak_ptr_factory_(this) { | 168 const std::vector<Profile*>& profiles, |
169 const LastDownloadCallback& callback) | |
170 : download_details_getter_(download_details_getter), | |
171 callback_(callback), | |
172 weak_ptr_factory_(this) { | |
121 // Observe profile lifecycle events so that the finder can begin or abandon | 173 // Observe profile lifecycle events so that the finder can begin or abandon |
122 // the search in profiles while it is running. | 174 // the search in profiles while it is running. |
123 notification_registrar_.Add(this, | 175 notification_registrar_.Add(this, |
124 chrome::NOTIFICATION_PROFILE_ADDED, | 176 chrome::NOTIFICATION_PROFILE_ADDED, |
125 content::NotificationService::AllSources()); | 177 content::NotificationService::AllSources()); |
126 notification_registrar_.Add(this, | 178 notification_registrar_.Add(this, |
127 chrome::NOTIFICATION_HISTORY_LOADED, | 179 chrome::NOTIFICATION_HISTORY_LOADED, |
128 content::NotificationService::AllSources()); | 180 content::NotificationService::AllSources()); |
129 notification_registrar_.Add(this, | 181 notification_registrar_.Add(this, |
130 chrome::NOTIFICATION_PROFILE_DESTROYED, | 182 chrome::NOTIFICATION_PROFILE_DESTROYED, |
(...skipping 10 matching lines...) Expand all Loading... | |
141 // Do not look in OTR profiles or in profiles that do not participate in | 193 // Do not look in OTR profiles or in profiles that do not participate in |
142 // safe browsing. | 194 // safe browsing. |
143 if (profile->IsOffTheRecord() || | 195 if (profile->IsOffTheRecord() || |
144 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) { | 196 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) { |
145 return; | 197 return; |
146 } | 198 } |
147 | 199 |
148 // Exit early if already processing this profile. This could happen if, for | 200 // Exit early if already processing this profile. This could happen if, for |
149 // example, NOTIFICATION_PROFILE_ADDED arrives after construction while | 201 // example, NOTIFICATION_PROFILE_ADDED arrives after construction while |
150 // waiting for NOTIFICATION_HISTORY_LOADED. | 202 // waiting for NOTIFICATION_HISTORY_LOADED. |
151 if (std::find(profiles_.begin(), profiles_.end(), profile) != | 203 if (profiles_.count(profile)) |
152 profiles_.end()) { | |
153 return; | |
154 } | |
155 | |
156 HistoryService* history_service = | |
157 HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS); | |
158 // No history service is returned for profiles that do not save history. | |
159 if (!history_service) | |
160 return; | 204 return; |
161 | 205 |
162 profiles_.push_back(profile); | 206 // Initiate a metadata search. |
163 if (history_service->BackendLoaded()) { | 207 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.
| |
164 history_service->QueryDownloads( | 208 download_details_getter_.Run(profile, |
165 base::Bind(&LastDownloadFinder::OnDownloadQuery, | 209 base::Bind(&LastDownloadFinder::OnMetadataQuery, |
166 weak_ptr_factory_.GetWeakPtr(), | 210 weak_ptr_factory_.GetWeakPtr(), |
167 profile)); | 211 profile)); |
168 } // else wait until history is loaded. | 212 } |
213 | |
214 void LastDownloadFinder::OnMetadataQuery( | |
215 Profile* profile, | |
216 scoped_ptr<ClientIncidentReport_DownloadDetails> details) { | |
217 auto iter = profiles_.find(profile); | |
218 // Early-exit if the search for this profile was abandoned. | |
219 if (iter == profiles_.end()) | |
220 return; | |
221 | |
222 if (details) { | |
223 if (IsMostInteresting(*details, details_.get(), most_recent_row_)) { | |
224 details_ = details.Pass(); | |
225 most_recent_row_.end_time = base::Time(); | |
226 } | |
227 | |
228 RemoveProfileAndReportIfDone(iter); | |
229 } else { | |
230 // Search history since no metadata was found. | |
231 iter->second = WAITING_FOR_HISTORY; | |
232 HistoryService* history_service = | |
233 HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS); | |
234 // No history service is returned for profiles that do not save history. | |
235 if (!history_service) { | |
236 RemoveProfileAndReportIfDone(iter); | |
237 return; | |
238 } | |
239 if (history_service->BackendLoaded()) { | |
240 history_service->QueryDownloads( | |
241 base::Bind(&LastDownloadFinder::OnDownloadQuery, | |
242 weak_ptr_factory_.GetWeakPtr(), | |
243 profile)); | |
244 } // else wait until history is loaded. | |
245 } | |
169 } | 246 } |
170 | 247 |
171 void LastDownloadFinder::OnProfileHistoryLoaded( | 248 void LastDownloadFinder::OnProfileHistoryLoaded( |
172 Profile* profile, | 249 Profile* profile, |
173 HistoryService* history_service) { | 250 HistoryService* history_service) { |
174 if (std::find(profiles_.begin(), profiles_.end(), profile) != | 251 auto iter = profiles_.find(profile); |
175 profiles_.end()) { | 252 if (iter == profiles_.end()) |
253 return; | |
254 | |
255 // Start the query in the history service if the finder was waiting for the | |
256 // service to load. | |
257 if (iter->second == WAITING_FOR_HISTORY) { | |
176 history_service->QueryDownloads( | 258 history_service->QueryDownloads( |
177 base::Bind(&LastDownloadFinder::OnDownloadQuery, | 259 base::Bind(&LastDownloadFinder::OnDownloadQuery, |
178 weak_ptr_factory_.GetWeakPtr(), | 260 weak_ptr_factory_.GetWeakPtr(), |
179 profile)); | 261 profile)); |
180 } | 262 } |
181 } | 263 } |
182 | 264 |
183 void LastDownloadFinder::AbandonSearchInProfile(Profile* profile) { | 265 void LastDownloadFinder::AbandonSearchInProfile(Profile* profile) { |
184 // |profile| may not be present in the set of profiles. | 266 // |profile| may not be present in the set of profiles. |
185 std::vector<Profile*>::iterator it = | 267 auto iter = profiles_.find(profile); |
186 std::find(profiles_.begin(), profiles_.end(), profile); | 268 if (iter != profiles_.end()) |
187 if (it != profiles_.end()) | 269 RemoveProfileAndReportIfDone(iter); |
188 RemoveProfileAndReportIfDone(it); | |
189 } | 270 } |
190 | 271 |
191 void LastDownloadFinder::OnDownloadQuery( | 272 void LastDownloadFinder::OnDownloadQuery( |
192 Profile* profile, | 273 Profile* profile, |
193 scoped_ptr<std::vector<history::DownloadRow> > downloads) { | 274 scoped_ptr<std::vector<history::DownloadRow> > downloads) { |
194 // Early-exit if the history search for this profile was abandoned. | 275 // Early-exit if the history search for this profile was abandoned. |
195 std::vector<Profile*>::iterator it = | 276 auto iter = profiles_.find(profile); |
196 std::find(profiles_.begin(), profiles_.end(), profile); | 277 if (iter == profiles_.end()) |
197 if (it == profiles_.end()) | |
198 return; | 278 return; |
199 | 279 |
200 // Find the most recent from this profile and use it if it's better than | 280 // Find the most recent from this profile and use it if it's better than |
201 // anything else found so far. | 281 // anything else found so far. |
202 const history::DownloadRow* profile_best = FindMostInteresting(*downloads); | 282 const history::DownloadRow* profile_best = FindMostInteresting(*downloads); |
203 if (profile_best && IsMoreInterestingThan(*profile_best, most_recent_row_)) | 283 if (profile_best && |
284 IsMostInteresting(*profile_best, details_.get(), most_recent_row_)) { | |
285 details_.reset(); | |
204 most_recent_row_ = *profile_best; | 286 most_recent_row_ = *profile_best; |
287 } | |
205 | 288 |
206 RemoveProfileAndReportIfDone(it); | 289 RemoveProfileAndReportIfDone(iter); |
207 } | 290 } |
208 | 291 |
209 void LastDownloadFinder::RemoveProfileAndReportIfDone( | 292 void LastDownloadFinder::RemoveProfileAndReportIfDone( |
210 std::vector<Profile*>::iterator it) { | 293 std::map<Profile*, ProfileWaitState>::iterator iter) { |
211 DCHECK(it != profiles_.end()); | 294 DCHECK(iter != profiles_.end()); |
212 | 295 profiles_.erase(iter); |
213 *it = profiles_.back(); | |
214 profiles_.resize(profiles_.size() - 1); | |
215 | 296 |
216 // Finish processing if all results are in. | 297 // Finish processing if all results are in. |
217 if (profiles_.empty()) | 298 if (profiles_.empty()) |
218 ReportResults(); | 299 ReportResults(); |
219 // Do not touch this instance after reporting results. | 300 // Do not touch this LastDownloadFinder after reporting results. |
220 } | 301 } |
221 | 302 |
222 void LastDownloadFinder::ReportResults() { | 303 void LastDownloadFinder::ReportResults() { |
223 DCHECK(profiles_.empty()); | 304 DCHECK(profiles_.empty()); |
224 if (most_recent_row_.end_time.is_null()) { | 305 if (details_) { |
225 callback_.Run(scoped_ptr<ClientIncidentReport_DownloadDetails>()); | 306 callback_.Run(make_scoped_ptr(new ClientIncidentReport_DownloadDetails( |
226 // Do not touch this instance after running the callback, since it may have | 307 *details_)).Pass()); |
227 // been deleted. | 308 // Do not touch this LastDownloadFinder after running the callback, since it |
228 } else { | 309 // may have been deleted. |
310 } else if (!most_recent_row_.end_time.is_null()) { | |
229 scoped_ptr<ClientIncidentReport_DownloadDetails> details( | 311 scoped_ptr<ClientIncidentReport_DownloadDetails> details( |
230 new ClientIncidentReport_DownloadDetails()); | 312 new ClientIncidentReport_DownloadDetails()); |
231 PopulateDetails(most_recent_row_, details.get()); | 313 PopulateDetailsFromRow(most_recent_row_, details.get()); |
232 callback_.Run(details.Pass()); | 314 callback_.Run(details.Pass()); |
233 // Do not touch this instance after running the callback, since it may have | 315 // Do not touch this LastDownloadFinder after running the callback, since it |
234 // been deleted. | 316 // may have been deleted. |
317 } else { | |
318 callback_.Run(scoped_ptr<ClientIncidentReport_DownloadDetails>()); | |
319 // Do not touch this LastDownloadFinder after running the callback, since it | |
320 // may have been deleted. | |
235 } | 321 } |
236 } | 322 } |
237 | 323 |
238 void LastDownloadFinder::Observe(int type, | 324 void LastDownloadFinder::Observe(int type, |
239 const content::NotificationSource& source, | 325 const content::NotificationSource& source, |
240 const content::NotificationDetails& details) { | 326 const content::NotificationDetails& details) { |
241 switch (type) { | 327 switch (type) { |
242 case chrome::NOTIFICATION_PROFILE_ADDED: | 328 case chrome::NOTIFICATION_PROFILE_ADDED: |
243 SearchInProfile(content::Source<Profile>(source).ptr()); | 329 SearchInProfile(content::Source<Profile>(source).ptr()); |
244 break; | 330 break; |
245 case chrome::NOTIFICATION_HISTORY_LOADED: | 331 case chrome::NOTIFICATION_HISTORY_LOADED: |
246 OnProfileHistoryLoaded(content::Source<Profile>(source).ptr(), | 332 OnProfileHistoryLoaded(content::Source<Profile>(source).ptr(), |
247 content::Details<HistoryService>(details).ptr()); | 333 content::Details<HistoryService>(details).ptr()); |
248 break; | 334 break; |
249 case chrome::NOTIFICATION_PROFILE_DESTROYED: | 335 case chrome::NOTIFICATION_PROFILE_DESTROYED: |
250 AbandonSearchInProfile(content::Source<Profile>(source).ptr()); | 336 AbandonSearchInProfile(content::Source<Profile>(source).ptr()); |
251 break; | 337 break; |
252 default: | 338 default: |
253 break; | 339 break; |
254 } | 340 } |
255 } | 341 } |
256 | 342 |
257 } // namespace safe_browsing | 343 } // namespace safe_browsing |
OLD | NEW |