blob: 14fdb02acfce05e181fb1fba65d93e5912671441 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h"
#include "base/memory/ptr_util.h"
#include "base/time/time.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/safe_browsing/safe_browsing_navigation_observer.h"
#include "chrome/browser/sessions/session_tab_helper.h"
#include "chrome/browser/tab_contents/retargeting_details.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
using content::WebContents;
namespace safe_browsing {
// The expiration period of a user gesture. Any user gesture that happened 1.0
// second ago will be considered as expired and not relevant to upcoming
// navigation events.
static const double kUserGestureTTLInSecond = 1.0;
// static
bool SafeBrowsingNavigationObserverManager::IsUserGestureExpired(
const base::Time& timestamp) {
double now = base::Time::Now().ToDoubleT();
double timestamp_in_double = timestamp.ToDoubleT();
if (now <= timestamp_in_double)
return true;
return (now - timestamp_in_double) > kUserGestureTTLInSecond;
}
// static
GURL SafeBrowsingNavigationObserverManager::ClearEmptyRef(const GURL& url) {
if (url.has_ref() && url.ref().empty()) {
url::Replacements<char> replacements;
replacements.ClearRef();
return url.ReplaceComponents(replacements);
}
return url;
}
SafeBrowsingNavigationObserverManager::SafeBrowsingNavigationObserverManager() {
registrar_.Add(this, chrome::NOTIFICATION_RETARGETING,
content::NotificationService::AllSources());
}
void SafeBrowsingNavigationObserverManager::RecordNavigationEvent(
const GURL& nav_event_key,
NavigationEvent* nav_event) {
auto insertion_result = navigation_map_.insert(
std::make_pair(nav_event_key, std::vector<NavigationEvent>()));
insertion_result.first->second.push_back(std::move(*nav_event));
}
void SafeBrowsingNavigationObserverManager::RecordUserGestureForWebContents(
content::WebContents* web_contents,
const base::Time& timestamp) {
auto insertion_result =
user_gesture_map_.insert(std::make_pair(web_contents, timestamp));
// Update the timestamp if entry already exists.
if (!insertion_result.second)
insertion_result.first->second = timestamp;
}
void SafeBrowsingNavigationObserverManager::OnUserGestureConsumed(
content::WebContents* web_contents,
const base::Time& timestamp) {
auto it = user_gesture_map_.find(web_contents);
// Remove entry from |user_gesture_map_| as a user_gesture is consumed by
// a navigation event.
if (it != user_gesture_map_.end() && timestamp >= it->second)
user_gesture_map_.erase(it);
}
void SafeBrowsingNavigationObserverManager::RecordHostToIpMapping(
const std::string& host,
const std::string& ip) {
auto insert_result = host_to_ip_map_.insert(
std::make_pair(host, std::vector<ResolvedIPAddress>()));
if (!insert_result.second) {
// host_to_ip_map already contains this key.
// If this IP is already in the vector, we update its timestamp.
for (auto& vector_entry : insert_result.first->second) {
if (vector_entry.ip == host) {
vector_entry.timestamp = base::Time::Now();
return;
}
}
}
// If this is a new IP of this host, and we added to the end of the vector.
insert_result.first->second.push_back(
ResolvedIPAddress(base::Time::Now(), ip));
}
void SafeBrowsingNavigationObserverManager::OnWebContentDestroyed(
content::WebContents* web_contents) {
user_gesture_map_.erase(web_contents);
// TODO (jialiul): Will add other clean up tasks shortly.
}
SafeBrowsingNavigationObserverManager::
~SafeBrowsingNavigationObserverManager() {}
void SafeBrowsingNavigationObserverManager::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
if (type == chrome::NOTIFICATION_RETARGETING)
RecordRetargeting(details);
}
void SafeBrowsingNavigationObserverManager::RecordRetargeting(
const content::NotificationDetails& details) {
const RetargetingDetails* retargeting_detail =
content::Details<const RetargetingDetails>(details).ptr();
DCHECK(retargeting_detail);
content::WebContents* source_contents =
retargeting_detail->source_web_contents;
content::WebContents* target_contents =
retargeting_detail->target_web_contents;
DCHECK(source_contents);
DCHECK(target_contents);
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
retargeting_detail->source_render_process_id,
retargeting_detail->source_render_frame_id);
// Remove the "#" at the end of URL, since it does not point to any actual
// page fragment ID.
GURL target_url = SafeBrowsingNavigationObserverManager::ClearEmptyRef(
retargeting_detail->target_url);
NavigationEvent nav_event;
if (rfh) {
nav_event.source_url = SafeBrowsingNavigationObserverManager::ClearEmptyRef(
rfh->GetLastCommittedURL());
}
nav_event.source_tab_id = SessionTabHelper::IdForTab(source_contents);
nav_event.source_main_frame_url =
SafeBrowsingNavigationObserverManager::ClearEmptyRef(
source_contents->GetLastCommittedURL());
nav_event.original_request_url = target_url;
nav_event.destination_url = target_url;
nav_event.target_tab_id = SessionTabHelper::IdForTab(target_contents);
nav_event.frame_id = rfh ? rfh->GetFrameTreeNodeId() : -1;
auto it = user_gesture_map_.find(source_contents);
if (it != user_gesture_map_.end() &&
!SafeBrowsingNavigationObserverManager::IsUserGestureExpired(
it->second)) {
nav_event.is_user_initiated = true;
OnUserGestureConsumed(it->first, it->second);
} else {
nav_event.is_user_initiated = false;
}
auto insertion_result = navigation_map_.insert(
std::make_pair(target_url, std::vector<NavigationEvent>()));
insertion_result.first->second.push_back(std::move(nav_event));
}
} // namespace safe_browsing