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

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

Issue 2601303002: Wireup SafeBrowsingNavigationObserverManager to help PPAPI download attribution (Closed)
Patch Set: add check for rfh Created 3 years, 11 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 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" 8 #include "base/metrics/histogram_macros.h"
9 #include "base/strings/stringprintf.h" 9 #include "base/strings/stringprintf.h"
10 #include "base/time/time.h" 10 #include "base/time/time.h"
(...skipping 24 matching lines...) Expand all
35 // Note, if for some reason this event's timestamp is in the future, this 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. 36 // event's timestamp is invalid, hence we treat it as expired.
37 bool IsEventExpired(const base::Time& event_time, double ttl_in_second) { 37 bool IsEventExpired(const base::Time& event_time, double ttl_in_second) {
38 double current_time_in_second = base::Time::Now().ToDoubleT(); 38 double current_time_in_second = base::Time::Now().ToDoubleT();
39 double event_time_in_second = event_time.ToDoubleT(); 39 double event_time_in_second = event_time.ToDoubleT();
40 if (current_time_in_second <= event_time_in_second) 40 if (current_time_in_second <= event_time_in_second)
41 return true; 41 return true;
42 return current_time_in_second - event_time_in_second > ttl_in_second; 42 return current_time_in_second - event_time_in_second > ttl_in_second;
43 } 43 }
44 44
45 // Helper function to determine if the URL type should be LANDING_REFERRER or
46 // LANDING_PAGE, and modify AttributionResult accordingly.
47 ReferrerChainEntry::URLType GetURLTypeAndAdjustAttributionResult(
48 bool at_user_gesture_limit,
49 SafeBrowsingNavigationObserverManager::AttributionResult* out_result) {
50 // Landing page of a download refers to the page user directly interacts
51 // with to trigger this download (e.g. clicking on download button). Landing
52 // referrer page is the one user interacts with right before navigating to
53 // the landing page.
54 // Since we are tracing navigations backwards, if we've reached
55 // user gesture limit before this navigation event, this is a navigation
56 // leading to the landing referrer page, otherwise it leads to landing page.
57 if (at_user_gesture_limit) {
58 *out_result =
59 SafeBrowsingNavigationObserverManager::SUCCESS_LANDING_REFERRER;
60 return ReferrerChainEntry::LANDING_REFERRER;
61 } else {
62 *out_result = SafeBrowsingNavigationObserverManager::SUCCESS_LANDING_PAGE;
63 return ReferrerChainEntry::LANDING_PAGE;
64 }
65 }
66
45 } // namespace 67 } // namespace
46 68
47 // 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
48 // second ago is considered as expired and not relevant to upcoming navigation 70 // second ago is considered as expired and not relevant to upcoming navigation
49 // events. 71 // events.
50 static const double kUserGestureTTLInSecond = 1.0; 72 static const double kUserGestureTTLInSecond = 1.0;
51 // The expiration period of navigation events and resolved IP addresses. Any 73 // The expiration period of navigation events and resolved IP addresses. Any
52 // navigation related records that happened 2 minutes ago are considered as 74 // navigation related records that happened 2 minutes ago are considered as
53 // expired. So we clean up these navigation footprints every 2 minutes. 75 // expired. So we clean up these navigation footprints every 2 minutes.
54 static const double kNavigationFootprintTTLInSecond = 120.0; 76 static const double kNavigationFootprintTTLInSecond = 120.0;
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
115 void SafeBrowsingNavigationObserverManager::OnUserGestureConsumed( 137 void SafeBrowsingNavigationObserverManager::OnUserGestureConsumed(
116 content::WebContents* web_contents, 138 content::WebContents* web_contents,
117 const base::Time& timestamp) { 139 const base::Time& timestamp) {
118 auto it = user_gesture_map_.find(web_contents); 140 auto it = user_gesture_map_.find(web_contents);
119 // Remove entry from |user_gesture_map_| as a user_gesture is consumed by 141 // Remove entry from |user_gesture_map_| as a user_gesture is consumed by
120 // a navigation event. 142 // a navigation event.
121 if (it != user_gesture_map_.end() && timestamp >= it->second) 143 if (it != user_gesture_map_.end() && timestamp >= it->second)
122 user_gesture_map_.erase(it); 144 user_gesture_map_.erase(it);
123 } 145 }
124 146
147 bool SafeBrowsingNavigationObserverManager::HasUserGesture(
148 content::WebContents* web_contents) {
149 if (!web_contents)
150 return false;
151 auto it = user_gesture_map_.find(web_contents);
152 if (it != user_gesture_map_.end() &&
153 !IsEventExpired(it->second, kUserGestureTTLInSecond)) {
154 return true;
155 }
156 return false;
157 }
158
125 void SafeBrowsingNavigationObserverManager::RecordHostToIpMapping( 159 void SafeBrowsingNavigationObserverManager::RecordHostToIpMapping(
126 const std::string& host, 160 const std::string& host,
127 const std::string& ip) { 161 const std::string& ip) {
128 auto insert_result = host_to_ip_map_.insert( 162 auto insert_result = host_to_ip_map_.insert(
129 std::make_pair(host, std::vector<ResolvedIPAddress>())); 163 std::make_pair(host, std::vector<ResolvedIPAddress>()));
130 if (!insert_result.second) { 164 if (!insert_result.second) {
131 // host_to_ip_map already contains this key. 165 // host_to_ip_map already contains this key.
132 // If this IP is already in the vector, we update its timestamp. 166 // If this IP is already in the vector, we update its timestamp.
133 for (auto& vector_entry : insert_result.first->second) { 167 for (auto& vector_entry : insert_result.first->second) {
134 if (vector_entry.ip == host) { 168 if (vector_entry.ip == host) {
(...skipping 14 matching lines...) Expand all
149 183
150 void SafeBrowsingNavigationObserverManager::CleanUpStaleNavigationFootprints() { 184 void SafeBrowsingNavigationObserverManager::CleanUpStaleNavigationFootprints() {
151 CleanUpNavigationEvents(); 185 CleanUpNavigationEvents();
152 CleanUpUserGestures(); 186 CleanUpUserGestures();
153 CleanUpIpAddresses(); 187 CleanUpIpAddresses();
154 ScheduleNextCleanUpAfterInterval( 188 ScheduleNextCleanUpAfterInterval(
155 base::TimeDelta::FromSecondsD(kNavigationFootprintTTLInSecond)); 189 base::TimeDelta::FromSecondsD(kNavigationFootprintTTLInSecond));
156 } 190 }
157 191
158 SafeBrowsingNavigationObserverManager::AttributionResult 192 SafeBrowsingNavigationObserverManager::AttributionResult
159 SafeBrowsingNavigationObserverManager::IdentifyReferrerChain( 193 SafeBrowsingNavigationObserverManager::IdentifyReferrerChainForDownload(
160 const GURL& target_url, 194 const GURL& target_url,
161 int target_tab_id, 195 int target_tab_id,
162 int user_gesture_count_limit, 196 int user_gesture_count_limit,
163 std::vector<ReferrerChainEntry>* out_referrer_chain) { 197 std::vector<ReferrerChainEntry>* out_referrer_chain) {
164 if (!target_url.is_valid()) 198 if (!target_url.is_valid())
165 return INVALID_URL; 199 return INVALID_URL;
166 200
167 NavigationEvent* nav_event = FindNavigationEvent( 201 NavigationEvent* nav_event = FindNavigationEvent(
168 target_url, 202 target_url,
169 GURL(), 203 GURL(),
170 target_tab_id); 204 target_tab_id);
171 if (!nav_event) { 205 if (!nav_event) {
172 // We cannot find a single navigation event related to this download. 206 // We cannot find a single navigation event related to this download.
173 return NAVIGATION_EVENT_NOT_FOUND; 207 return NAVIGATION_EVENT_NOT_FOUND;
174 } 208 }
209 AttributionResult result = SUCCESS;
210 AddToReferrerChain(out_referrer_chain, nav_event,
211 ReferrerChainEntry::DOWNLOAD_URL);
212 int user_gesture_count = 0;
213 GetRemainingReferrerChain(
214 nav_event,
215 user_gesture_count,
216 user_gesture_count_limit,
217 out_referrer_chain,
218 &result);
219 return result;
220 }
221
222 SafeBrowsingNavigationObserverManager::AttributionResult
223 SafeBrowsingNavigationObserverManager::IdentifyReferrerChainForPPAPIDownload(
224 const GURL& initiating_frame_url,
225 int tab_id,
226 bool has_user_gesture,
227 int user_gesture_count_limit,
228 std::vector<ReferrerChainEntry>* out_referrer_chain) {
229 if (!initiating_frame_url.is_valid())
230 return INVALID_URL;
231
232 NavigationEvent* nav_event =
233 FindNavigationEvent(initiating_frame_url, GURL(), tab_id);
234 if (!nav_event) {
235 // We cannot find a single navigation event related to this download.
236 return NAVIGATION_EVENT_NOT_FOUND;
237 }
175 238
176 AddToReferrerChain(out_referrer_chain, nav_event,
177 ReferrerChainEntry::DOWNLOAD_URL);
178 AttributionResult result = SUCCESS; 239 AttributionResult result = SUCCESS;
240
179 int user_gesture_count = 0; 241 int user_gesture_count = 0;
180 while (user_gesture_count < user_gesture_count_limit) { 242 // If this initiating_frame has user gesture, we consider this as the landing
181 // Back trace to the next nav_event that was initiated by the user. 243 // page of the PPAPI download.
182 while (!nav_event->is_user_initiated) { 244 if (has_user_gesture) {
183 nav_event = 245 user_gesture_count = 1;
184 FindNavigationEvent(nav_event->source_url, 246 AddToReferrerChain(out_referrer_chain, nav_event,
185 nav_event->source_main_frame_url, 247 GetURLTypeAndAdjustAttributionResult(
186 nav_event->source_tab_id); 248 user_gesture_count == user_gesture_count_limit,
187 if (!nav_event) 249 &result));
188 return result; 250 } else {
189 AddToReferrerChain(out_referrer_chain, nav_event, 251 AddToReferrerChain(out_referrer_chain, nav_event,
190 nav_event->has_server_redirect 252 nav_event->has_server_redirect
191 ? ReferrerChainEntry::SERVER_REDIRECT 253 ? ReferrerChainEntry::SERVER_REDIRECT
192 : ReferrerChainEntry::CLIENT_REDIRECT); 254 : ReferrerChainEntry::CLIENT_REDIRECT);
193 } 255 }
194 256
195 user_gesture_count++; 257 GetRemainingReferrerChain(
196 258 nav_event,
197 // If the source_url and source_main_frame_url of current navigation event 259 user_gesture_count,
198 // are empty, and is_user_initiated is true, this is a browser initiated 260 user_gesture_count_limit,
199 // navigation (e.g. trigged by typing in address bar, clicking on bookmark, 261 out_referrer_chain,
200 // etc). We reached the end of the referrer chain. 262 &result);
201 if (nav_event->source_url.is_empty() &&
202 nav_event->source_main_frame_url.is_empty()) {
203 DCHECK(nav_event->is_user_initiated);
204 return result;
205 }
206
207 nav_event =
208 FindNavigationEvent(nav_event->source_url,
209 nav_event->source_main_frame_url,
210 nav_event->source_tab_id);
211 if (!nav_event)
212 return result;
213
214 // Landing page of a download refers to the page user directly interacts
215 // with to trigger this download (e.g. clicking on download button). Landing
216 // referrer page is the one user interacts with right before navigating to
217 // the landing page.
218 // Since we are tracing navigations backwards, if we've encountered 1 user
219 // gesture before this navigation event, this is a navigation leading to the
220 // landing page. If we've encountered 2 user gestures, it leads to landing
221 // referrer page.
222 if (user_gesture_count == 1) {
223 AddToReferrerChain(out_referrer_chain, nav_event,
224 ReferrerChainEntry::LANDING_PAGE);
225 result = SUCCESS_LANDING_PAGE;
226 } else if (user_gesture_count == 2) {
227 AddToReferrerChain(out_referrer_chain, nav_event,
228 ReferrerChainEntry::LANDING_REFERRER);
229 result = SUCCESS_LANDING_REFERRER;
230 } else {
231 NOTREACHED();
232 }
233 }
234 return result; 263 return result;
235 } 264 }
236 265
237 SafeBrowsingNavigationObserverManager:: 266 SafeBrowsingNavigationObserverManager::
238 ~SafeBrowsingNavigationObserverManager() {} 267 ~SafeBrowsingNavigationObserverManager() {}
239 268
240 void SafeBrowsingNavigationObserverManager::Observe( 269 void SafeBrowsingNavigationObserverManager::Observe(
241 int type, 270 int type,
242 const content::NotificationSource& source, 271 const content::NotificationSource& source,
243 const content::NotificationDetails& details) { 272 const content::NotificationDetails& details) {
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
351 380
352 NavigationEvent* SafeBrowsingNavigationObserverManager::FindNavigationEvent( 381 NavigationEvent* SafeBrowsingNavigationObserverManager::FindNavigationEvent(
353 const GURL& target_url, 382 const GURL& target_url,
354 const GURL& target_main_frame_url, 383 const GURL& target_main_frame_url,
355 int target_tab_id) { 384 int target_tab_id) {
356 if (target_url.is_empty() && target_main_frame_url.is_empty()) 385 if (target_url.is_empty() && target_main_frame_url.is_empty())
357 return nullptr; 386 return nullptr;
358 387
359 // If target_url is empty, we should back trace navigation based on its 388 // If target_url is empty, we should back trace navigation based on its
360 // main frame URL instead. 389 // main frame URL instead.
361 const GURL& search_url = 390 GURL search_url =
362 target_url.is_empty() ? target_main_frame_url : target_url; 391 target_url.is_empty() ? target_main_frame_url : target_url;
392 auto it = navigation_map_.find(search_url);
393 if (it == navigation_map_.end())
394 return nullptr;
363 395
364 auto it = navigation_map_.find(search_url);
365 if (it == navigation_map_.end()) {
366 return nullptr;
367 }
368 // Since navigation events are recorded in chronological order, we traverse 396 // Since navigation events are recorded in chronological order, we traverse
369 // the vector in reverse order to get the latest match. 397 // the vector in reverse order to get the latest match.
370 for (auto rit = it->second.rbegin(); rit != it->second.rend(); ++rit) { 398 for (auto rit = it->second.rbegin(); rit != it->second.rend(); ++rit) {
371 // If tab id is not valid, we only compare url, otherwise we compare both. 399 // If tab id is not valid, we only compare url, otherwise we compare both.
400 if (rit->destination_url == search_url)
372 if (rit->destination_url == search_url && 401 if (rit->destination_url == search_url &&
373 (target_tab_id == -1 || rit->target_tab_id == target_tab_id)) { 402 (target_tab_id == -1 || rit->target_tab_id == target_tab_id)) {
374 // If both source_url and source_main_frame_url are empty, and this 403 // If both source_url and source_main_frame_url are empty, and this
375 // navigation is not triggered by user, a retargeting navigation probably 404 // navigation is not triggered by user, a retargeting navigation probably
376 // causes this navigation. In this case, we skip this navigation event and 405 // causes this navigation. In this case, we skip this navigation event and
377 // looks for the retargeting navigation event. 406 // looks for the retargeting navigation event.
378 if (rit->source_url.is_empty() && rit->source_main_frame_url.is_empty() && 407 if (rit->source_url.is_empty() && rit->source_main_frame_url.is_empty() &&
379 !rit->is_user_initiated) { 408 !rit->is_user_initiated) {
380 continue; 409 // If there is a server redirection immediately after retargeting, we
410 // need to adjust our search url to the original request.
411 if (rit->has_server_redirect){
412 NavigationEvent* retargeting_nav_event =
413 FindNavigationEvent(rit->original_request_url,
414 GURL(),
415 rit->target_tab_id);
416 // Adjust retargeting navigation event's attributes.
417 retargeting_nav_event->has_server_redirect = true;
418 retargeting_nav_event->destination_url = search_url;
419 return retargeting_nav_event;
420 } else {
421 continue;
422 }
381 } else { 423 } else {
382 return &*rit; 424 return &*rit;
383 } 425 }
384 } 426 }
385 } 427 }
386 return nullptr; 428 return nullptr;
387 } 429 }
388 430
389 void SafeBrowsingNavigationObserverManager::AddToReferrerChain( 431 void SafeBrowsingNavigationObserverManager::AddToReferrerChain(
390 std::vector<ReferrerChainEntry>* referrer_chain, 432 std::vector<ReferrerChainEntry>* referrer_chain,
(...skipping 15 matching lines...) Expand all
406 referrer_chain_entry.set_referrer_main_frame_url( 448 referrer_chain_entry.set_referrer_main_frame_url(
407 nav_event->source_main_frame_url.spec()); 449 nav_event->source_main_frame_url.spec());
408 } 450 }
409 referrer_chain_entry.set_is_retargeting(nav_event->source_tab_id != 451 referrer_chain_entry.set_is_retargeting(nav_event->source_tab_id !=
410 nav_event->target_tab_id); 452 nav_event->target_tab_id);
411 referrer_chain_entry.set_navigation_time_msec( 453 referrer_chain_entry.set_navigation_time_msec(
412 nav_event->last_updated.ToJavaTime()); 454 nav_event->last_updated.ToJavaTime());
413 referrer_chain->push_back(std::move(referrer_chain_entry)); 455 referrer_chain->push_back(std::move(referrer_chain_entry));
414 } 456 }
415 457
458 void SafeBrowsingNavigationObserverManager::GetRemainingReferrerChain(
459 NavigationEvent* last_nav_event_traced,
460 int current_user_gesture_count,
461 int user_gesture_count_limit,
462 std::vector<ReferrerChainEntry>* out_referrer_chain,
463 SafeBrowsingNavigationObserverManager::AttributionResult* out_result) {
464
465 while (current_user_gesture_count < user_gesture_count_limit) {
466 // Back trace to the next nav_event that was initiated by the user.
467 while (!last_nav_event_traced->is_user_initiated) {
468 last_nav_event_traced =
469 FindNavigationEvent(last_nav_event_traced->source_url,
470 last_nav_event_traced->source_main_frame_url,
471 last_nav_event_traced->source_tab_id);
472 if (!last_nav_event_traced)
473 return;
474 AddToReferrerChain(out_referrer_chain, last_nav_event_traced,
475 last_nav_event_traced->has_server_redirect
476 ? ReferrerChainEntry::SERVER_REDIRECT
477 : ReferrerChainEntry::CLIENT_REDIRECT);
478 }
479
480 current_user_gesture_count++;
481
482
483 // If the source_url and source_main_frame_url of current navigation event
484 // are empty, and is_user_initiated is true, this is a browser initiated
485 // navigation (e.g. trigged by typing in address bar, clicking on bookmark,
486 // etc). We reached the end of the referrer chain.
487 if (last_nav_event_traced->source_url.is_empty() &&
488 last_nav_event_traced->source_main_frame_url.is_empty()) {
489 DCHECK(last_nav_event_traced->is_user_initiated);
490 return;
491 }
492
493 last_nav_event_traced =
494 FindNavigationEvent(last_nav_event_traced->source_url,
495 last_nav_event_traced->source_main_frame_url,
496 last_nav_event_traced->source_tab_id);
497 if (!last_nav_event_traced)
498 return;
499
500 AddToReferrerChain(out_referrer_chain, last_nav_event_traced,
501 GetURLTypeAndAdjustAttributionResult(
502 current_user_gesture_count ==
503 user_gesture_count_limit,
504 out_result));
505 }
506 }
507
416 } // namespace safe_browsing 508 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698