OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |