Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(66)

Side by Side Diff: chrome/browser/safe_browsing/incident_reporting/last_download_finder.cc

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

Powered by Google App Engine
This is Rietveld 408576698