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

Side by Side Diff: chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc

Issue 2538483002: Add management related code to SafeBrowsingNavigationObserverManager (Closed)
Patch Set: filter out browser side navigation browser tests Created 4 years 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 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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/safe_browsing_navigation_observer_manager .h" 5 #include "chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager .h"
6 6
7 #include "base/memory/ptr_util.h" 7 #include "base/memory/ptr_util.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/strings/stringprintf.h"
8 #include "base/time/time.h" 10 #include "base/time/time.h"
11 #include "base/timer/timer.h"
9 #include "chrome/browser/chrome_notification_types.h" 12 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/safe_browsing/safe_browsing_navigation_observer.h" 13 #include "chrome/browser/safe_browsing/safe_browsing_navigation_observer.h"
11 #include "chrome/browser/sessions/session_tab_helper.h" 14 #include "chrome/browser/sessions/session_tab_helper.h"
12 #include "chrome/browser/tab_contents/retargeting_details.h" 15 #include "chrome/browser/tab_contents/retargeting_details.h"
13 #include "content/public/browser/navigation_details.h" 16 #include "content/public/browser/navigation_details.h"
14 #include "content/public/browser/notification_service.h" 17 #include "content/public/browser/notification_service.h"
15 #include "content/public/browser/notification_types.h" 18 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/render_frame_host.h" 19 #include "content/public/browser/render_frame_host.h"
17 #include "content/public/browser/render_process_host.h" 20 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/web_contents.h" 21 #include "content/public/browser/web_contents.h"
19 22
20 using content::WebContents; 23 using content::WebContents;
21 24
22 namespace safe_browsing { 25 namespace safe_browsing {
23 26
27 namespace {
28
29 const char kAttributionResultUMAPrefix[] = "SB2.ReferrerAttributionResult";
30 const char kAttributionURLChainSizeUMAPrefix[] = "SB2.ReferrerURLChainSize";
31 const char kReferrerHasInvalidTabIDUMAPrefix[] = "SB2.ReferrerHasInvalidTabID";
32 const char kDownloadAttributionUMASuffix[] = "DownloadAttribution";
33
34 // Given when an event happened and its TTL, determine if it is already expired.
35 // Note, if for some reason this event's timestamp is in the future, this
36 // event's timestamp is invalid, hence we treat it as expired.
37 bool IsEventExpired(const base::Time& event_time, double ttl_in_second) {
38 double current_time_in_second = base::Time::Now().ToDoubleT();
39 double event_time_in_second = event_time.ToDoubleT();
40 if (current_time_in_second <= event_time_in_second)
41 return true;
42 return current_time_in_second - event_time_in_second > ttl_in_second;
43 }
44
45 void RecordURLChainSize(const std::string& attribution_type, std::size_t size) {
46 std::string metric_name = base::StringPrintf(
47 "%s.%s", kAttributionURLChainSizeUMAPrefix, attribution_type.c_str());
48 UMA_HISTOGRAM_COUNTS_100(metric_name, size);
Nathan Parker 2016/12/05 22:21:23 Does this work? I thought most of the UMA histogr
Jialiu Lin 2016/12/06 23:10:06 You're right... this would not work. Change to lit
49 }
50
51 void RecordAttributionResult(
52 const std::string& attribution_type,
53 SafeBrowsingNavigationObserverManager::AttributionResult type) {
54 std::string metric_name = base::StringPrintf(
55 "%s.%s", kAttributionResultUMAPrefix, attribution_type.c_str());
56 UMA_HISTOGRAM_ENUMERATION(
57 metric_name, type,
58 SafeBrowsingNavigationObserverManager::ATTRIBUTION_FAILURE_TYPE_MAX);
59 }
60
61 void RecordInvalidTabIDEvent(const std::string& attribution_type) {
62 std::string metric_name = base::StringPrintf(
63 "%s.%s", kReferrerHasInvalidTabIDUMAPrefix, attribution_type.c_str());
64 UMA_HISTOGRAM_BOOLEAN(metric_name, true);
65 }
66
67 } // namespace
68
24 // The expiration period of a user gesture. Any user gesture that happened 1.0 69 // The expiration period of a user gesture. Any user gesture that happened 1.0
25 // second ago will be considered as expired and not relevant to upcoming 70 // second ago will be considered as expired and not relevant to upcoming
26 // navigation events. 71 // navigation events.
27 static const double kUserGestureTTLInSecond = 1.0; 72 static const double kUserGestureTTLInSecond = 1.0;
73 static const double kNavigationFootPrintTTLInSecond = 120.0;
Nathan Parker 2016/12/05 22:21:24 Add a comment describing how this is intended to b
Jialiu Lin 2016/12/06 23:10:06 Done.
28 74
29 // static 75 // static
30 bool SafeBrowsingNavigationObserverManager::IsUserGestureExpired( 76 bool SafeBrowsingNavigationObserverManager::IsUserGestureExpired(
31 const base::Time& timestamp) { 77 const base::Time& timestamp) {
32 double now = base::Time::Now().ToDoubleT(); 78 return IsEventExpired(timestamp, kUserGestureTTLInSecond);
33 double timestamp_in_double = timestamp.ToDoubleT();
34
35 if (now <= timestamp_in_double)
36 return true;
37 return (now - timestamp_in_double) > kUserGestureTTLInSecond;
38 } 79 }
39 80
40 // static 81 // static
41 GURL SafeBrowsingNavigationObserverManager::ClearEmptyRef(const GURL& url) { 82 GURL SafeBrowsingNavigationObserverManager::ClearEmptyRef(const GURL& url) {
42 if (url.has_ref() && url.ref().empty()) { 83 if (url.has_ref() && url.ref().empty()) {
43 url::Replacements<char> replacements; 84 url::Replacements<char> replacements;
44 replacements.ClearRef(); 85 replacements.ClearRef();
45 return url.ReplaceComponents(replacements); 86 return url.ReplaceComponents(replacements);
46 } 87 }
47 return url; 88 return url;
48 } 89 }
49 90
50 SafeBrowsingNavigationObserverManager::SafeBrowsingNavigationObserverManager() { 91 SafeBrowsingNavigationObserverManager::SafeBrowsingNavigationObserverManager() {
51 registrar_.Add(this, chrome::NOTIFICATION_RETARGETING, 92 registrar_.Add(this, chrome::NOTIFICATION_RETARGETING,
52 content::NotificationService::AllSources()); 93 content::NotificationService::AllSources());
94
95 // TODO(jialiul): call ScheduleNextCleanUpAfterInterval() when this class is
96 // ready to be hooked into SafeBrowsingService.
53 } 97 }
54 98
55 void SafeBrowsingNavigationObserverManager::RecordNavigationEvent( 99 void SafeBrowsingNavigationObserverManager::RecordNavigationEvent(
56 const GURL& nav_event_key, 100 const GURL& nav_event_key,
57 NavigationEvent* nav_event) { 101 NavigationEvent* nav_event) {
58 auto insertion_result = navigation_map_.insert( 102 auto insertion_result = navigation_map_.insert(
59 std::make_pair(nav_event_key, std::vector<NavigationEvent>())); 103 std::make_pair(nav_event_key, std::vector<NavigationEvent>()));
60 104
61 insertion_result.first->second.push_back(std::move(*nav_event)); 105 insertion_result.first->second.push_back(std::move(*nav_event));
62 } 106 }
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
97 } 141 }
98 } 142 }
99 // If this is a new IP of this host, and we added to the end of the vector. 143 // If this is a new IP of this host, and we added to the end of the vector.
100 insert_result.first->second.push_back( 144 insert_result.first->second.push_back(
101 ResolvedIPAddress(base::Time::Now(), ip)); 145 ResolvedIPAddress(base::Time::Now(), ip));
102 } 146 }
103 147
104 void SafeBrowsingNavigationObserverManager::OnWebContentDestroyed( 148 void SafeBrowsingNavigationObserverManager::OnWebContentDestroyed(
105 content::WebContents* web_contents) { 149 content::WebContents* web_contents) {
106 user_gesture_map_.erase(web_contents); 150 user_gesture_map_.erase(web_contents);
107 // TODO (jialiul): Will add other clean up tasks shortly. 151 }
152
153 void SafeBrowsingNavigationObserverManager::CleanUpStaleNavigationFootPrints() {
154 CleanUpNavigationEvents();
155 CleanUpUserGestures();
156 CleanUpIpAddresses();
157 ScheduleNextCleanUpAfterInterval(
158 base::TimeDelta::FromSecondsD(kNavigationFootPrintTTLInSecond));
159 }
160
161 void SafeBrowsingNavigationObserverManager::
162 AddReferrerChainToClientDownloadRequest(
163 const GURL& download_url,
164 content::WebContents* source_contents,
165 ClientDownloadRequest* request) {
166 std::string attribution_type(kDownloadAttributionUMASuffix);
Nathan Parker 2016/12/05 22:21:23 const. Or even just const char* attribution_type
Jialiu Lin 2016/12/06 23:10:06 no longer using this variable.
167 int download_tab_id = SessionTabHelper::IdForTab(source_contents);
168 if (download_tab_id == -1) {
169 RecordInvalidTabIDEvent(attribution_type);
170 }
171 std::vector<ClientDownloadRequest::ReferrerChainEntry> attribution_chain;
172 AttributionResult result = IdentifyReferrerChain(
173 download_url, download_tab_id, 2, &attribution_chain);
Nathan Parker 2016/12/05 22:21:23 What is the 2? Add a comment, or make it a consta
Jialiu Lin 2016/12/06 23:10:06 Added a const
174 RecordURLChainSize(attribution_type, attribution_chain.size());
175 RecordAttributionResult(attribution_type, result);
176 for (auto entry : attribution_chain)
177 (*request->add_referrer_chain_entry()) = entry;
Nathan Parker 2016/12/05 22:21:23 You shouldn't need the parens.
Jialiu Lin 2016/12/06 23:10:06 Done.
108 } 178 }
109 179
110 SafeBrowsingNavigationObserverManager:: 180 SafeBrowsingNavigationObserverManager::
111 ~SafeBrowsingNavigationObserverManager() {} 181 ~SafeBrowsingNavigationObserverManager() {}
112 182
113 void SafeBrowsingNavigationObserverManager::Observe( 183 void SafeBrowsingNavigationObserverManager::Observe(
114 int type, 184 int type,
115 const content::NotificationSource& source, 185 const content::NotificationSource& source,
116 const content::NotificationDetails& details) { 186 const content::NotificationDetails& details) {
117 if (type == chrome::NOTIFICATION_RETARGETING) 187 if (type == chrome::NOTIFICATION_RETARGETING)
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
159 OnUserGestureConsumed(it->first, it->second); 229 OnUserGestureConsumed(it->first, it->second);
160 } else { 230 } else {
161 nav_event.is_user_initiated = false; 231 nav_event.is_user_initiated = false;
162 } 232 }
163 233
164 auto insertion_result = navigation_map_.insert( 234 auto insertion_result = navigation_map_.insert(
165 std::make_pair(target_url, std::vector<NavigationEvent>())); 235 std::make_pair(target_url, std::vector<NavigationEvent>()));
166 insertion_result.first->second.push_back(std::move(nav_event)); 236 insertion_result.first->second.push_back(std::move(nav_event));
167 } 237 }
168 238
239 void SafeBrowsingNavigationObserverManager::CleanUpNavigationEvents() {
240 // Remove any stale NavigationEnvent, if it lasts longer than
Nathan Parker 2016/12/05 22:21:24 nit: NavigationEvent. And do you mean "lasts long
Jialiu Lin 2016/12/06 23:10:06 is older than...
241 // kNavigationFootPrintTTLInSecond.
242 for (auto it = navigation_map_.begin(); it != navigation_map_.end();) {
243 it->second.erase(std::remove_if(it->second.begin(), it->second.end(),
244 [](const NavigationEvent& nav_event) {
245 return IsEventExpired(
246 nav_event.last_updated,
247 kNavigationFootPrintTTLInSecond);
248 }),
249 it->second.end());
250 if (it->second.size() == 0)
251 it = navigation_map_.erase(it);
Nathan Parker 2016/12/05 22:21:24 This is clever -- avoids needing a second pass.
Jialiu Lin 2016/12/06 23:10:06 Acknowledged.
252 else
253 ++it;
254 }
255 }
256
257 void SafeBrowsingNavigationObserverManager::CleanUpUserGestures() {
258 for (auto it = user_gesture_map_.begin(); it != user_gesture_map_.end();) {
259 if (IsEventExpired(it->second, kUserGestureTTLInSecond))
260 it = user_gesture_map_.erase(it);
261 else
262 ++it;
263 }
264 }
265
266 void SafeBrowsingNavigationObserverManager::CleanUpIpAddresses() {
267 for (auto it = host_to_ip_map_.begin(); it != host_to_ip_map_.end();) {
268 it->second.erase(std::remove_if(it->second.begin(), it->second.end(),
269 [](const ResolvedIPAddress& resolved_ip) {
270 return IsEventExpired(
271 resolved_ip.timestamp,
272 kNavigationFootPrintTTLInSecond);
273 }),
274 it->second.end());
275 if (it->second.size() == 0)
276 it = host_to_ip_map_.erase(it);
277 else
278 ++it;
279 }
280 }
281
282 bool SafeBrowsingNavigationObserverManager::IsCleanUpScheduled() const {
283 return cleanup_timer_.IsRunning();
284 }
285
286 void SafeBrowsingNavigationObserverManager::ScheduleNextCleanUpAfterInterval(
287 base::TimeDelta interval) {
288 DCHECK(interval >= base::TimeDelta());
289 cleanup_timer_.Stop();
290 cleanup_timer_.Start(
291 FROM_HERE, interval, this,
292 &SafeBrowsingNavigationObserverManager::CleanUpStaleNavigationFootPrints);
293 }
294
295 NavigationEvent* SafeBrowsingNavigationObserverManager::FindNavigationEvent(
296 const GURL& target_url,
297 int target_tab_id) {
298 auto it = navigation_map_.find(target_url);
299 if (it == navigation_map_.end()) {
300 return nullptr;
301 }
302 // Since navigation events are recorded in chronological order, we traverse
Nathan Parker 2016/12/05 22:21:23 Is there any way someone could confuse this by add
Jialiu Lin 2016/12/06 23:10:06 creis@ and I talked about this before. a -> b->c->
303 // the vector in reverse order to get the latest match.
304 for (auto rit = it->second.rbegin(); rit != it->second.rend(); ++rit) {
305 // If tab id is not valid, we only compare url, otherwise we compare both.
Nathan Parker 2016/12/05 22:21:23 I'm curious: When/why would tab_id be invalid?
Jialiu Lin 2016/12/06 23:10:06 I never see this happens in real settings, but it
306 if (rit->destination_url == target_url &&
307 (target_tab_id == -1 || rit->target_tab_id == target_tab_id)) {
308 // If both source_url and source_main_frame_url are empty, and this
309 // navigation is not triggered by user, a retargeting navigation probably
310 // causes this navigation. In this case, we skip this navigation event and
311 // looks for the retargeting navigation event.
312 if (rit->source_url.is_empty() && rit->source_main_frame_url.is_empty() &&
313 !rit->is_user_initiated)
314 continue;
315 else
316 return &*rit;
317 }
318 }
319 return nullptr;
320 }
321
322 void SafeBrowsingNavigationObserverManager::AddToReferrerChain(
323 std::vector<safe_browsing::ClientDownloadRequest::ReferrerChainEntry>*
324 referrer_chain,
325 NavigationEvent* nav_event,
326 ClientDownloadRequest::ReferrerChainEntry::URLType type) {
327 ClientDownloadRequest::ReferrerChainEntry referrer_chain_entry;
328 referrer_chain_entry.set_url(nav_event->destination_url.spec());
329 referrer_chain_entry.set_type(type);
330 auto ip_it = host_to_ip_map_.find(nav_event->destination_url.host());
331 if (ip_it != host_to_ip_map_.end()) {
332 for (ResolvedIPAddress entry : ip_it->second) {
333 referrer_chain_entry.add_ip_address(entry.ip);
334 }
335 }
336 // Since we only track navigation to landing referrer, we will not log the
337 // referrer of the landing referrer page.
338 if (type != ClientDownloadRequest::ReferrerChainEntry::LANDING_REFERRER) {
339 referrer_chain_entry.set_referrer_url(nav_event->source_url.spec());
340 referrer_chain_entry.set_referrer_main_frame_url(
341 nav_event->source_main_frame_url.spec());
342 }
343 referrer_chain_entry.set_is_retargeting(nav_event->source_tab_id !=
344 nav_event->target_tab_id);
345 referrer_chain_entry.set_navigation_time_msec(
346 nav_event->last_updated.ToJavaTime());
347 referrer_chain->push_back(referrer_chain_entry);
348 }
349
350 SafeBrowsingNavigationObserverManager::AttributionResult
351 SafeBrowsingNavigationObserverManager::IdentifyReferrerChain(
352 const GURL& target_url,
353 int target_tab_id,
354 int user_gesture_count_max,
355 std::vector<ClientDownloadRequest::ReferrerChainEntry>* referrer_chain) {
356 if (!target_url.is_valid())
357 return INVALID_URL;
358
359 NavigationEvent* nav_event = FindNavigationEvent(target_url, target_tab_id);
360 if (!nav_event) {
361 // We cannot find a single navigation event related to this download.
362 return NAVIGATION_EVENT_NOT_FOUND;
363 }
364
365 AddToReferrerChain(referrer_chain, nav_event,
366 ClientDownloadRequest::ReferrerChainEntry::DOWNLOAD_URL);
367 AttributionResult result = SUCCESS;
368 int user_gesture_count = 0;
369 while (user_gesture_count < user_gesture_count_max) {
370 // Back trace to the next nav_event that initiated by user.
371 while (!nav_event->is_user_initiated) {
372 nav_event =
373 FindNavigationEvent(nav_event->source_url, nav_event->source_tab_id);
374 if (!nav_event)
375 return result;
376 AddToReferrerChain(
377 referrer_chain, nav_event,
378 nav_event->has_server_redirect
379 ? ClientDownloadRequest::ReferrerChainEntry::SERVER_REDIRECT
380 : ClientDownloadRequest::ReferrerChainEntry::CLIENT_REDIRECT);
381 }
382 user_gesture_count++;
383 // If the source_url and source_main_frame_url of current navigation event
384 // is empty, and is_user_initiated is true, this is a browser initiated
385 // navigation (e.g. trigged by typing in address bar, clicking on bookmark,
386 // etc). We reached the end of the referrer chain.
387 if (nav_event->source_url.is_empty() &&
388 nav_event->source_main_frame_url.is_empty()) {
389 DCHECK(nav_event->is_user_initiated);
390 return result;
391 }
392 nav_event =
393 FindNavigationEvent(nav_event->source_url, nav_event->source_tab_id);
394 if (!nav_event)
395 return result;
396 AddToReferrerChain(
397 referrer_chain, nav_event,
398 user_gesture_count == 1
399 ? ClientDownloadRequest::ReferrerChainEntry::LANDING_PAGE
400 : ClientDownloadRequest::ReferrerChainEntry::LANDING_REFERRER);
401 result = user_gesture_count == 1 ? SUCCESS_LANDING_PAGE
402 : SUCCESS_LANDING_REFERRER;
403 }
404 return result;
405 }
406
169 } // namespace safe_browsing 407 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698